bool rdseed_fallback()

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;
}