0
$\begingroup$

Let's say you're building RNG with ChaCha20 and the fast key erasure technique. There seem to be a few ways to do it.

The reference implementation in the SUPERCOP benchmarking toolkit looks like this:

#define KEYBYTES 32
#define OUTPUTBYTES 736

int crypto_rng_chacha20(
        unsigned char *r, /* random output */
        unsigned char *n, /* new key */
  const unsigned char *g  /* old key */
)
{
  unsigned char x[KEYBYTES + OUTPUTBYTES];
  crypto_stream_chacha20(x,sizeof x,nonce,g);
  memcpy(n,x,KEYBYTES);
  memcpy(r,x + KEYBYTES,OUTPUTBYTES);
  return 0;
}
  1. How often should I replace the key? The SUPERCOP implementation replaces the 32-byte key every 768 bytes.
  2. What about the nonce and counter? The SUPERCOP implementation sets the nonce to zero and resets the counter to zero every time the key is replaced. What about treating the nonce/counter similar to the key and periodically replacing them with ChaCha20 output? (Except for a few counter bits that we need to crank to get 768 bytes of output.)
  3. The SUPERCOP implementation replaces the key with the first 32 byte of the 768-byte sequence. Can I use the last 32 bytes instead? That would allow my implementation to keep less state.
  4. What about replacing the key incrementally? For example, instead of fully replacing the key with the first 32 bytes of every 768, what if I used the first 4 bytes of every 96 and incrementally replaced the key?

Note: I'm asking this question primarily to get a deeper understanding of how things work. In practice, I'd just copy the SUPERCOP implementation.

$\endgroup$
3
$\begingroup$
  1. How often should I replace the key? The SUPERCOP implementation replaces the 32-byte key every 768 bytes.

It's a latency/throughput tradeoff. Generating a longer batch of output raises the maximum latency of any query to the PRNG but also increases the throughput by using proportionally more CPU cycles for generating data and fewer for generating intermediate keys. Pick the rate that serves your needs. Maybe you should replace it after every 32-byte output so the latency is guaranteed to be bounded by a single ChaCha call. Maybe you should replace it after every kilobyte of output to get better throughput. Or just use 768 bytes like SUPERCOP.

  1. What about the nonce and counter? The SUPERCOP implementation sets the nonce to zero and resets the counter to zero every time the key is replaced. What about treating the nonce/counter similar to the key and periodically replacing them with ChaCha20 output? (Except for a few counter bits that we need to crank to get 768 bytes of output.)

Additional implementation complexity for no security benefit.

  1. The SUPERCOP implementation replaces the key with the first 32 byte of the 768-byte sequence. Can I use the last 32 bytes instead? That would allow my implementation to keep less state.

Yes, but your results won't be compatible with SUPERCOP which may matter if you ever care about reproducibility or interoperability.

  1. What about replacing the key incrementally? For example, instead of fully replacing the key with the first 32 bytes of every 768, what if I used the first 4 bytes of every 96 and incrementally replaced the key?

No. Bad. Do not. My head hurts.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.