in csrc/rdrand.cpp [188:287]
bool rdseed_fallback(uint64_t *dest) {
/* This routine performs a "512:1 reduction" as described in
* https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide
* section 4.2.6.
*
* This involves taking 512 _128-bit_ samples of RDRAND and mixing them down to a single 128-bit sample
* (which we then fold to a 64-bit sample). This guarantees reseeding by exceeding the 1022-sample limit
* on the number of RDRAND samples generated by a single hardware-generated seed. Mixing is performed using
* AES-128-CBC-MAC.
*
* As we sample RDRAND, we also retry sampling RDSEED in the hopes that it will recover and give us a result
* faster.
*/
// Buffers for key, IV, and data blocks
uint8_t inbuf[16], outbuf[16], key[16], iv[16];
bool success = false;
int blockindex = 0, rdrand_retries_remain = 100;
raii_cipher_ctx ctx;
ctx.init();
EVP_CIPHER_CTX_init(ctx);
for (blockindex = 0; blockindex < (512 + 2) * 2; blockindex++) {
// First, retry rdseed. Maybe it'll work this time?
if (rng_rdseed(dest)) {
success = true;
goto out;
}
if (!rng_rdrand((uint64_t *)inbuf + (blockindex & 1))) {
rdrand_retries_remain--;
if (!rdrand_retries_remain) {
goto out;
}
blockindex--; // retry getting this block
continue;
}
if (!(blockindex & 1)) {
// We loaded the first half of this 128-bit component, wait for the next
continue;
}
int outl;
switch (blockindex) {
case 1: // Key loaded
memcpy(key, inbuf, 16);
memset(inbuf, 0, 16);
break;
case 3: // IV loaded
memcpy(iv, inbuf, 16);
memset(inbuf, 0, 16);
if (!EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv)) {
goto out;
}
if (!EVP_CIPHER_CTX_set_padding(ctx, 0)) {
goto out;
}
break;
default: // all other blocks are data to pump through
if (!EVP_EncryptUpdate(ctx, outbuf, &outl, inbuf, sizeof(inbuf))) {
goto out;
}
if (outl != sizeof(outbuf)) {
abort(); // possible buffer overflow?
}
break;
}
}
// At the end we should have a value in outbuf that is our reduced CBC-MAC value. Fold it and
// present it as our result.
// Note that we do this memcpy thing because directly addressing the buffer is technically
// a strict aliasing violation.
{
uint64_t a, b;
memcpy(&a, &outbuf[0], sizeof(a));
memcpy(&b, &outbuf[8], sizeof(a));
*dest = a ^ b;
secureZero(&a, sizeof(a));
secureZero(&b, sizeof(b));
}
success = true;
out:
secureZero(inbuf, sizeof(inbuf));
secureZero(outbuf, sizeof(outbuf));
secureZero(key, sizeof(outbuf));
secureZero(iv, sizeof(outbuf));
if (!success) *dest = 0;
return success;
}