diff options
Diffstat (limited to 'src/libstrongswan/plugins/padlock/padlock_rng.c')
-rw-r--r-- | src/libstrongswan/plugins/padlock/padlock_rng.c | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/src/libstrongswan/plugins/padlock/padlock_rng.c b/src/libstrongswan/plugins/padlock/padlock_rng.c new file mode 100644 index 000000000..50d9f0c43 --- /dev/null +++ b/src/libstrongswan/plugins/padlock/padlock_rng.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2008 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * $Id$ + */ + +#include "padlock_rng.h" + +typedef struct private_padlock_rng_t private_padlock_rng_t; +typedef enum padlock_quality_factor_t padlock_quality_factor_t; + +/** + * Padlock RNG quality factors + */ +enum padlock_quality_factor_t { + /* Lowest quality: Reads 8 bytes */ + PADLOCK_QF0 = 0x00, + /* Medium quality: Reads 4 bytes */ + PADLOCK_QF1 = 0x01, + /* Better quality: Reads 2 bytes */ + PADLOCK_QF2 = 0x10, + /* Highest quality: Reads 1 byte */ + PADLOCK_QF3 = 0x11, +}; + +/** + * Private data of an padlock_rng_t object. + */ +struct private_padlock_rng_t { + + /** + * Public padlock_rng_t interface. + */ + padlock_rng_t public; + + /** + * Padlock quality factor + */ + padlock_quality_factor_t quality; +}; + +/** + * Get bytes from Padlock RNG. buf should have space for (len + 7) + */ +static void rng(char *buf, int len, int quality) +{ + while (len > 0)
+ { + int status; + + /* run XSTORE until we have all bytes needed. We do not use REP, as + * this should not be performance critical and it's easier this way. */ + asm volatile ( + ".byte 0x0F,0xA7,0xC0 \n\t"
+ : "=D"(buf), "=a"(status)
+ : "d"(quality), "D"(buf)); + + /* bits[0..4] of status word contains the number of bytes read */ + len -= status & 0x1F; + } +} + +/** + * Implementation of padlock_rng_t.allocate_bytes. + */ +static void allocate_bytes(private_padlock_rng_t *this, size_t bytes, + chunk_t *chunk) +{ + chunk->len = bytes; + /* padlock requires some additional bytes */ + chunk->ptr = malloc(bytes + 7); + + rng(chunk->ptr, chunk->len, this->quality); +} + +/** + * Implementation of padlock_rng_t.get_bytes. + */ +static void get_bytes(private_padlock_rng_t *this, size_t bytes, + u_int8_t *buffer) +{ + chunk_t chunk; + + /* Padlock needs a larger buffer than "bytes", we need a new buffer */ + allocate_bytes(this, bytes, &chunk); + memcpy(buffer, chunk.ptr, bytes); + chunk_clear(&chunk); +} + +/** + * Implementation of padlock_rng_t.destroy. + */ +static void destroy(private_padlock_rng_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +padlock_rng_t *padlock_rng_create(rng_quality_t quality) +{ + private_padlock_rng_t *this = malloc_thing(private_padlock_rng_t); + + this->public.rng.get_bytes = (void (*) (rng_t *, size_t, u_int8_t*)) get_bytes; + this->public.rng.allocate_bytes = (void (*) (rng_t *, size_t, chunk_t*)) allocate_bytes; + this->public.rng.destroy = (void (*) (rng_t *))destroy; + + /* map RNG quality to Padlock quality factor */ + switch (quality) + { + case RNG_WEAK: + this->quality = PADLOCK_QF0; + break; + case RNG_STRONG: + this->quality = PADLOCK_QF1; + break; + case RNG_REAL: + this->quality = PADLOCK_QF3; + break; + } + + return &this->public; +} + |