diff options
author | Martin Willi <martin@revosec.ch> | 2013-01-04 15:33:10 +0100 |
---|---|---|
committer | Martin Willi <martin@revosec.ch> | 2013-01-11 10:45:14 +0100 |
commit | 9fe24b004d6d1a64d0e2f106d39cf7a8fbd3179d (patch) | |
tree | fe2340bf65174e0d7f8756a0f42d1b436629d9f9 /src | |
parent | ed8dc6f1320f655a371236d3f5ac7bbff3d99006 (diff) | |
download | strongswan-9fe24b004d6d1a64d0e2f106d39cf7a8fbd3179d.tar.bz2 strongswan-9fe24b004d6d1a64d0e2f106d39cf7a8fbd3179d.tar.xz |
Provide RNG_WEAK quality random generator in rdrand
Diffstat (limited to 'src')
-rw-r--r-- | src/libstrongswan/plugins/rdrand/Makefile.am | 3 | ||||
-rw-r--r-- | src/libstrongswan/plugins/rdrand/rdrand_plugin.c | 17 | ||||
-rw-r--r-- | src/libstrongswan/plugins/rdrand/rdrand_rng.c | 277 | ||||
-rw-r--r-- | src/libstrongswan/plugins/rdrand/rdrand_rng.h | 47 |
4 files changed, 342 insertions, 2 deletions
diff --git a/src/libstrongswan/plugins/rdrand/Makefile.am b/src/libstrongswan/plugins/rdrand/Makefile.am index d87324f5c..4be7b7215 100644 --- a/src/libstrongswan/plugins/rdrand/Makefile.am +++ b/src/libstrongswan/plugins/rdrand/Makefile.am @@ -10,6 +10,7 @@ plugin_LTLIBRARIES = libstrongswan-rdrand.la endif libstrongswan_rdrand_la_SOURCES = \ - rdrand_plugin.h rdrand_plugin.c + rdrand_plugin.h rdrand_plugin.c \ + rdrand_rng.h rdrand_rng.c libstrongswan_rdrand_la_LDFLAGS = -module -avoid-version diff --git a/src/libstrongswan/plugins/rdrand/rdrand_plugin.c b/src/libstrongswan/plugins/rdrand/rdrand_plugin.c index a719cd781..fc9c6202d 100644 --- a/src/libstrongswan/plugins/rdrand/rdrand_plugin.c +++ b/src/libstrongswan/plugins/rdrand/rdrand_plugin.c @@ -14,6 +14,7 @@ */ #include "rdrand_plugin.h" +#include "rdrand_rng.h" #include <stdio.h> @@ -90,6 +91,17 @@ METHOD(plugin_t, get_name, char*, return "rdrand"; } +METHOD(plugin_t, get_features, int, + private_rdrand_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + PLUGIN_REGISTER(RNG, rdrand_rng_create), + PLUGIN_PROVIDE(RNG, RNG_WEAK), + }; + *features = f; + return countof(f); +} + METHOD(plugin_t, destroy, void, private_rdrand_plugin_t *this) { @@ -113,7 +125,10 @@ plugin_t *rdrand_plugin_create() }, ); - have_rdrand(); + if (have_rdrand()) + { + this->public.plugin.get_features = _get_features; + } return &this->public.plugin; } diff --git a/src/libstrongswan/plugins/rdrand/rdrand_rng.c b/src/libstrongswan/plugins/rdrand/rdrand_rng.c new file mode 100644 index 000000000..64ce12509 --- /dev/null +++ b/src/libstrongswan/plugins/rdrand/rdrand_rng.c @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec AG + * + * 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. + */ + +#include "rdrand_rng.h" + +typedef struct private_rdrand_rng_t private_rdrand_rng_t; + +/** + * Private data of an rdrand_rng_t object. + */ +struct private_rdrand_rng_t { + + /** + * Public rdrand_rng_t interface. + */ + rdrand_rng_t public; + + /** + * Quality we produce RNG data + */ + rng_quality_t quality; +}; + +/** + * Retries for failed RDRAND instructions + */ +#define MAX_TRIES 16 + +/** + * Get a two byte word using RDRAND + */ +static bool rdrand16(u_int16_t *out) +{ + u_char res; + int i; + + for (i = 0; i < MAX_TRIES; i++) + { + asm("rdrand %0;" + "setc %1;" + : "=r"(*out), "=qm"(res)); + + if (res) + { + return TRUE; + } + } + return FALSE; +} + +/** + * Get a four byte word using RDRAND + */ +static bool rdrand32(u_int32_t *out) +{ + u_char res; + int i; + + for (i = 0; i < MAX_TRIES; i++) + { + asm("rdrand %0;" + "setc %1;" + : "=r"(*out), "=qm"(res)); + + if (res) + { + return TRUE; + } + } + return FALSE; +} + +#ifdef __x86_64__ +/** + * Get a eight byte word using RDRAND + */ +static bool rdrand64(u_int64_t *out) +{ + u_char res; + int i; + + for (i = 0; i < MAX_TRIES; i++) + { + asm("rdrand %0;" + "setc %1;" + : "=r"(*out), "=qm"(res)); + + if (res) + { + return TRUE; + } + } + return FALSE; +} +#endif /* __x86_64__ */ + +/** + * Get a one byte word using RDRAND + */ +static bool rdrand8(u_int8_t *out) +{ + u_int16_t u16; + + if (!rdrand16(&u16)) + { + return FALSE; + } + *out = u16; + return TRUE; +} + +METHOD(rng_t, get_bytes, bool, + private_rdrand_rng_t *this, size_t bytes, u_int8_t *buffer) +{ + chunk_t chunk; + + chunk = chunk_create(buffer, bytes); + + /* align to 2 byte */ + if (chunk.len >= sizeof(u_int8_t)) + { + if ((uintptr_t)chunk.ptr % 2) + { + if (!rdrand8((u_int8_t*)chunk.ptr)) + { + return FALSE; + } + chunk = chunk_skip(chunk, sizeof(u_int8_t)); + } + } + + /* align to 4 byte */ + if (chunk.len >= sizeof(u_int16_t)) + { + if ((uintptr_t)chunk.ptr % 4) + { + if (!rdrand16((u_int16_t*)chunk.ptr)) + { + return FALSE; + } + chunk = chunk_skip(chunk, sizeof(u_int16_t)); + } + } + +#ifdef __x86_64__ + + /* align to 8 byte */ + if (chunk.len >= sizeof(u_int32_t)) + { + if ((uintptr_t)chunk.ptr % 8) + { + if (!rdrand32((u_int32_t*)chunk.ptr)) + { + return FALSE; + } + chunk = chunk_skip(chunk, sizeof(u_int32_t)); + } + } + + /* fill with 8 byte words */ + while (chunk.len >= sizeof(u_int64_t)) + { + if (!rdrand64((u_int64_t*)chunk.ptr)) + { + return FALSE; + } + chunk = chunk_skip(chunk, sizeof(u_int64_t)); + } + + /* append 4 byte word */ + if (chunk.len >= sizeof(u_int32_t)) + { + if (!rdrand32((u_int32_t*)chunk.ptr)) + { + return FALSE; + } + chunk = chunk_skip(chunk, sizeof(u_int32_t)); + } + +#else /* __i386__ */ + + /* fill with 4 byte words */ + while (chunk.len >= sizeof(u_int32_t)) + { + if (!rdrand32((u_int32_t*)chunk.ptr)) + { + return FALSE; + } + chunk = chunk_skip(chunk, sizeof(u_int32_t)); + } + +#endif /* __x86_64__ / __i386__ */ + + /* append 2 byte word */ + if (chunk.len >= sizeof(u_int16_t)) + { + if (!rdrand16((u_int16_t*)chunk.ptr)) + { + return FALSE; + } + chunk = chunk_skip(chunk, sizeof(u_int16_t)); + } + + /* append 1 byte word */ + if (chunk.len >= sizeof(u_int8_t)) + { + if (!rdrand8((u_int8_t*)chunk.ptr)) + { + return FALSE; + } + chunk = chunk_skip(chunk, sizeof(u_int8_t)); + } + + return TRUE; +} + +METHOD(rng_t, allocate_bytes, bool, + private_rdrand_rng_t *this, size_t bytes, chunk_t *chunk) +{ + *chunk = chunk_alloc(bytes); + if (get_bytes(this, bytes, chunk->ptr)) + { + return TRUE; + } + free(chunk->ptr); + return FALSE; +} + +METHOD(rng_t, destroy, void, + private_rdrand_rng_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +rdrand_rng_t *rdrand_rng_create(rng_quality_t quality) +{ + private_rdrand_rng_t *this; + + switch (quality) + { + case RNG_WEAK: + break; + case RNG_STRONG: + case RNG_TRUE: + default: + /* not yet */ + return NULL; + } + + INIT(this, + .public = { + .rng = { + .get_bytes = _get_bytes, + .allocate_bytes = _allocate_bytes, + .destroy = _destroy, + }, + }, + .quality = quality, + ); + + return &this->public; +} diff --git a/src/libstrongswan/plugins/rdrand/rdrand_rng.h b/src/libstrongswan/plugins/rdrand/rdrand_rng.h new file mode 100644 index 000000000..d15a48224 --- /dev/null +++ b/src/libstrongswan/plugins/rdrand/rdrand_rng.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec AG + * + * 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. + */ + +/** + * @defgroup rdrand_rng rdrand_rng + * @{ @ingroup rdrand + */ + +#ifndef RDRAND_RNG_H_ +#define RDRAND_RNG_H_ + +#include <crypto/rngs/rng.h> + +typedef struct rdrand_rng_t rdrand_rng_t; + +/** + * RNG implemented with Intels RDRAND instructions, introduced in Ivy Bridge. + */ +struct rdrand_rng_t { + + /** + * Implements rng_t interface. + */ + rng_t rng; +}; + +/** + * Create a rdrand_rng instance. + * + * @param quality RNG quality + * @return RNG instance + */ +rdrand_rng_t *rdrand_rng_create(rng_quality_t quality); + +#endif /** RDRAND_RNG_H_ @}*/ |