How can AES-256-CTR be used to construct a CSPRNG? I need to generate thousands of random 128-bit IVs per second and would like to amortize calls to /dev/urandom.

How can AES-256-CTR be used to construct a CSPRNG?

For example, would the following be acceptable?


Get 48 bytes from /dev/urandom.
Use the first 32 bytes as the AES-256-CTR key.
Use the next 16 bytes as the AES-256-CTR IV.
Encrypt 65536 bytes of zeroes.
Slice the ciphertext up into 4096 128-bit IVs (65536 / 16).
Rinse and repeat steps 1 to 6 every 4096 IVs.


Does it matter whether one encrypts 65536 bytes of zeroes at a time, or 16 bytes of zeroes at a time (i.e. should the size of the plaintext be no more than the cipher's block size)?

Would this scheme stretch the 256-bit + 128-bit entropy (initial AES key + intial AES IV) equally across the 65536 bytes of ciphertext? How does this translate to each IV sliced out of it? Would each IV have at least 128-bits of entropy or would the entropy of each IV be reduced in some way?

How could this scheme be improved?
To improve, use NIST SP 800-90A Rev. 1 Recommendation for Random Number Generation Using Deterministic RBGs, section 10.2.1: CTR_DRBG.




  Does it matter whether one encrypts 65536 bytes of zeroes at a time, or 16 bytes of zeroes at a time (i.e. should the size of the plaintext be no more than the cipher's block size)?


No, however you might wonder if you want to store that much data in memory. Stuff may be evicted from cache and I'm not sure that a 64 KiB buffer is much faster than a 1 KiB buffer.


  Would this scheme stretch the 256-bit + 128-bit entropy (initial AES key + intial AES IV) equally across the 65536 bytes of ciphertext?


Yes it does, but note that that is different than saying you've achieved a security strength of 384 bits.


  How does this translate to each IV sliced out of it? Would each IV have at least 128-bits of entropy or would the entropy of each IV be reduced in some way?


The output of a DRBG should probably not be confused with entropy, which is usually the input of a DRBG. You should be concerned if the output is distinguishable from random, and to that the answer is probably no.



The main difference between your scheme and the NIST version is that you directly use /dev/urandom instead of the INITIALIZE and UPDATE functions. I don't think it will usually pose a problem, but you'd lose any security NIST has put in that scheme.

Note that /dev/urandom is just a block device, the security of /dev/urandom depends strongly on the implementation. Some implementations may return data before enough entropy has been gathered; /dev/random does not have this problem, but can and does block even after enough entropy has been gathered.