Annotation of embedaddon/strongswan/src/libstrongswan/plugins/rdrand/rdrand_rng.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (C) 2012 Martin Willi
! 3: * Copyright (C) 2012 revosec AG
! 4: *
! 5: * This program is free software; you can redistribute it and/or modify it
! 6: * under the terms of the GNU General Public License as published by the
! 7: * Free Software Foundation; either version 2 of the License, or (at your
! 8: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
! 9: *
! 10: * This program is distributed in the hope that it will be useful, but
! 11: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
! 12: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
! 13: * for more details.
! 14: */
! 15:
! 16: #include "rdrand_rng.h"
! 17:
! 18: #include <unistd.h>
! 19:
! 20: typedef struct private_rdrand_rng_t private_rdrand_rng_t;
! 21:
! 22: /**
! 23: * Private data of an rdrand_rng_t object.
! 24: */
! 25: struct private_rdrand_rng_t {
! 26:
! 27: /**
! 28: * Public rdrand_rng_t interface.
! 29: */
! 30: rdrand_rng_t public;
! 31:
! 32: /**
! 33: * Quality we produce RNG data
! 34: */
! 35: rng_quality_t quality;
! 36: };
! 37:
! 38: /**
! 39: * Retries for failed RDRAND instructions
! 40: */
! 41: #define MAX_TRIES 16
! 42:
! 43: /**
! 44: * After how many bytes should we reseed for RNG_STRONG
! 45: * (must be a power of two >= 8)
! 46: */
! 47: #define FORCE_RESEED 16
! 48:
! 49: /**
! 50: * How many times we mix reseeded RDRAND output when using RNG_TRUE
! 51: */
! 52: #define MIX_ROUNDS 32
! 53:
! 54: /**
! 55: * Get a two byte word using RDRAND
! 56: */
! 57: static bool rdrand16(uint16_t *out)
! 58: {
! 59: u_char res;
! 60: int i;
! 61:
! 62: for (i = 0; i < MAX_TRIES; i++)
! 63: {
! 64: asm(".byte 0x66;.byte 0x0f;.byte 0xc7;.byte 0xf0; " /* rdrand */
! 65: "setc %1;"
! 66: : "=a"(*out), "=qm"(res));
! 67:
! 68: if (res)
! 69: {
! 70: return TRUE;
! 71: }
! 72: }
! 73: return FALSE;
! 74: }
! 75:
! 76: /**
! 77: * Get a four byte word using RDRAND
! 78: */
! 79: static bool rdrand32(uint32_t *out)
! 80: {
! 81: u_char res;
! 82: int i;
! 83:
! 84: for (i = 0; i < MAX_TRIES; i++)
! 85: {
! 86: asm(".byte 0x0f;.byte 0xc7;.byte 0xf0;" /* rdrand */
! 87: "setc %1;"
! 88: : "=a"(*out), "=qm"(res));
! 89:
! 90: if (res)
! 91: {
! 92: return TRUE;
! 93: }
! 94: }
! 95: return FALSE;
! 96: }
! 97:
! 98: #ifdef __x86_64__
! 99: /**
! 100: * Get a eight byte word using RDRAND
! 101: */
! 102: static bool rdrand64(uint64_t *out)
! 103: {
! 104: u_char res;
! 105: int i;
! 106:
! 107: for (i = 0; i < MAX_TRIES; i++)
! 108: {
! 109: asm(".byte 0x48;.byte 0x0f;.byte 0xc7;.byte 0xf0;" /* rdrand */
! 110: "setc %1;"
! 111: : "=a"(*out), "=qm"(res));
! 112:
! 113: if (res)
! 114: {
! 115: return TRUE;
! 116: }
! 117: }
! 118: return FALSE;
! 119: }
! 120: #endif /* __x86_64__ */
! 121:
! 122: /**
! 123: * Get a one byte word using RDRAND
! 124: */
! 125: static bool rdrand8(uint8_t *out)
! 126: {
! 127: uint16_t u16;
! 128:
! 129: if (!rdrand16(&u16))
! 130: {
! 131: return FALSE;
! 132: }
! 133: *out = u16;
! 134: return TRUE;
! 135: }
! 136:
! 137: /**
! 138: * Get a 16 byte word using RDRAND
! 139: */
! 140: static bool rdrand128(void *out)
! 141: {
! 142: #ifdef __x86_64__
! 143: if (!rdrand64(out) ||
! 144: !rdrand64(out + sizeof(uint64_t)))
! 145: {
! 146: return FALSE;
! 147: }
! 148: #else /* __i386__ */
! 149: if (!rdrand32(out) ||
! 150: !rdrand32(out + 1 * sizeof(uint32_t)) ||
! 151: !rdrand32(out + 2 * sizeof(uint32_t)) ||
! 152: !rdrand32(out + 3 * sizeof(uint32_t)))
! 153: {
! 154: return FALSE;
! 155: }
! 156: #endif /* __x86_64__ / __i386__ */
! 157: return TRUE;
! 158: }
! 159:
! 160: /**
! 161: * Enforce a DRNG reseed by reading 511 128-bit samples
! 162: */
! 163: static bool reseed()
! 164: {
! 165: int i;
! 166:
! 167: #ifdef __x86_64__
! 168: uint64_t tmp;
! 169:
! 170: for (i = 0; i < 511 * 16 / sizeof(uint64_t); i++)
! 171: {
! 172: if (!rdrand64(&tmp))
! 173: {
! 174: return FALSE;
! 175: }
! 176: }
! 177: #else /* __i386__ */
! 178: uint32_t tmp;
! 179:
! 180: for (i = 0; i < 511 * 16 / sizeof(uint32_t); i++)
! 181: {
! 182: if (!rdrand32(&tmp))
! 183: {
! 184: return FALSE;
! 185: }
! 186: }
! 187: #endif /* __x86_64__ / __i386__ */
! 188: return TRUE;
! 189: }
! 190:
! 191: /**
! 192: * Fill a preallocated chunk of data with random bytes
! 193: */
! 194: static bool rdrand_chunk(private_rdrand_rng_t *this, chunk_t chunk)
! 195: {
! 196: if (this->quality == RNG_STRONG)
! 197: {
! 198: if (!reseed())
! 199: {
! 200: return FALSE;
! 201: }
! 202: }
! 203:
! 204: /* align to 2 byte */
! 205: if (chunk.len >= sizeof(uint8_t))
! 206: {
! 207: if ((uintptr_t)chunk.ptr % 2)
! 208: {
! 209: if (!rdrand8((uint8_t*)chunk.ptr))
! 210: {
! 211: return FALSE;
! 212: }
! 213: chunk = chunk_skip(chunk, sizeof(uint8_t));
! 214: }
! 215: }
! 216:
! 217: /* align to 4 byte */
! 218: if (chunk.len >= sizeof(uint16_t))
! 219: {
! 220: if ((uintptr_t)chunk.ptr % 4)
! 221: {
! 222: if (!rdrand16((uint16_t*)chunk.ptr))
! 223: {
! 224: return FALSE;
! 225: }
! 226: chunk = chunk_skip(chunk, sizeof(uint16_t));
! 227: }
! 228: }
! 229:
! 230: #ifdef __x86_64__
! 231:
! 232: /* align to 8 byte */
! 233: if (chunk.len >= sizeof(uint32_t))
! 234: {
! 235: if ((uintptr_t)chunk.ptr % 8)
! 236: {
! 237: if (!rdrand32((uint32_t*)chunk.ptr))
! 238: {
! 239: return FALSE;
! 240: }
! 241: chunk = chunk_skip(chunk, sizeof(uint32_t));
! 242: }
! 243: }
! 244:
! 245: /* fill with 8 byte words */
! 246: while (chunk.len >= sizeof(uint64_t))
! 247: {
! 248: if (this->quality == RNG_STRONG && chunk.len % FORCE_RESEED == 0)
! 249: {
! 250: if (!reseed())
! 251: {
! 252: return FALSE;
! 253: }
! 254: }
! 255: if (!rdrand64((uint64_t*)chunk.ptr))
! 256: {
! 257: return FALSE;
! 258: }
! 259: chunk = chunk_skip(chunk, sizeof(uint64_t));
! 260: }
! 261:
! 262: /* append 4 byte word */
! 263: if (chunk.len >= sizeof(uint32_t))
! 264: {
! 265: if (!rdrand32((uint32_t*)chunk.ptr))
! 266: {
! 267: return FALSE;
! 268: }
! 269: chunk = chunk_skip(chunk, sizeof(uint32_t));
! 270: }
! 271:
! 272: #else /* __i386__ */
! 273:
! 274: /* fill with 4 byte words */
! 275: while (chunk.len >= sizeof(uint32_t))
! 276: {
! 277: if (this->quality == RNG_STRONG && chunk.len % FORCE_RESEED == 0)
! 278: {
! 279: if (!reseed())
! 280: {
! 281: return FALSE;
! 282: }
! 283: }
! 284: if (!rdrand32((uint32_t*)chunk.ptr))
! 285: {
! 286: return FALSE;
! 287: }
! 288: chunk = chunk_skip(chunk, sizeof(uint32_t));
! 289: }
! 290:
! 291: #endif /* __x86_64__ / __i386__ */
! 292:
! 293: if (this->quality == RNG_STRONG)
! 294: {
! 295: if (!reseed())
! 296: {
! 297: return FALSE;
! 298: }
! 299: }
! 300:
! 301: /* append 2 byte word */
! 302: if (chunk.len >= sizeof(uint16_t))
! 303: {
! 304: if (!rdrand16((uint16_t*)chunk.ptr))
! 305: {
! 306: return FALSE;
! 307: }
! 308: chunk = chunk_skip(chunk, sizeof(uint16_t));
! 309: }
! 310:
! 311: /* append 1 byte word */
! 312: if (chunk.len >= sizeof(uint8_t))
! 313: {
! 314: if (!rdrand8((uint8_t*)chunk.ptr))
! 315: {
! 316: return FALSE;
! 317: }
! 318: chunk = chunk_skip(chunk, sizeof(uint8_t));
! 319: }
! 320:
! 321: return TRUE;
! 322: }
! 323:
! 324: /**
! 325: * Stronger variant mixing reseeded results of rdrand output
! 326: *
! 327: * This is based on the Intel DRNG "Software Implementation Guide", using
! 328: * AES-CBC to mix several reseeded RDRAND outputs.
! 329: */
! 330: static bool rdrand_mixed(private_rdrand_rng_t *this, chunk_t chunk)
! 331: {
! 332: u_char block[16], forward[16], key[16], iv[16];
! 333: crypter_t *crypter;
! 334: int i, len;
! 335:
! 336: memset(iv, 0, sizeof(iv));
! 337: crypter = lib->crypto->create_crypter(lib->crypto, ENCR_AES_CBC, 16);
! 338: if (!crypter)
! 339: {
! 340: return FALSE;
! 341: }
! 342: for (i = 0; i < sizeof(key); i++)
! 343: {
! 344: key[i] = i;
! 345: }
! 346: if (!crypter->set_key(crypter, chunk_from_thing(key)))
! 347: {
! 348: crypter->destroy(crypter);
! 349: return FALSE;
! 350: }
! 351: while (chunk.len > 0)
! 352: {
! 353: memset(forward, 0, sizeof(forward));
! 354: for (i = 0; i < MIX_ROUNDS; i++)
! 355: {
! 356: /* sleep to reseed PRNG */
! 357: usleep(10);
! 358: if (!rdrand128(block))
! 359: {
! 360: crypter->destroy(crypter);
! 361: return FALSE;
! 362: }
! 363: memxor(forward, block, sizeof(block));
! 364: if (!crypter->encrypt(crypter, chunk_from_thing(forward),
! 365: chunk_from_thing(iv), NULL))
! 366: {
! 367: crypter->destroy(crypter);
! 368: return FALSE;
! 369: }
! 370: }
! 371: len = min(chunk.len, sizeof(forward));
! 372: memcpy(chunk.ptr, forward, len);
! 373: chunk = chunk_skip(chunk, len);
! 374: }
! 375: crypter->destroy(crypter);
! 376:
! 377: return TRUE;
! 378: }
! 379:
! 380: METHOD(rng_t, get_bytes, bool,
! 381: private_rdrand_rng_t *this, size_t bytes, uint8_t *buffer)
! 382: {
! 383: switch (this->quality)
! 384: {
! 385: case RNG_WEAK:
! 386: case RNG_STRONG:
! 387: return rdrand_chunk(this, chunk_create(buffer, bytes));
! 388: case RNG_TRUE:
! 389: return rdrand_mixed(this, chunk_create(buffer, bytes));
! 390: default:
! 391: return FALSE;
! 392: }
! 393: }
! 394:
! 395: METHOD(rng_t, allocate_bytes, bool,
! 396: private_rdrand_rng_t *this, size_t bytes, chunk_t *chunk)
! 397: {
! 398: *chunk = chunk_alloc(bytes);
! 399: if (get_bytes(this, bytes, chunk->ptr))
! 400: {
! 401: return TRUE;
! 402: }
! 403: free(chunk->ptr);
! 404: return FALSE;
! 405: }
! 406:
! 407: METHOD(rng_t, destroy, void,
! 408: private_rdrand_rng_t *this)
! 409: {
! 410: free(this);
! 411: }
! 412:
! 413: /*
! 414: * Described in header.
! 415: */
! 416: rdrand_rng_t *rdrand_rng_create(rng_quality_t quality)
! 417: {
! 418: private_rdrand_rng_t *this;
! 419:
! 420: switch (quality)
! 421: {
! 422: case RNG_WEAK:
! 423: case RNG_STRONG:
! 424: case RNG_TRUE:
! 425: break;
! 426: default:
! 427: return NULL;
! 428: }
! 429:
! 430: INIT(this,
! 431: .public = {
! 432: .rng = {
! 433: .get_bytes = _get_bytes,
! 434: .allocate_bytes = _allocate_bytes,
! 435: .destroy = _destroy,
! 436: },
! 437: },
! 438: .quality = quality,
! 439: );
! 440:
! 441: return &this->public;
! 442: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>