How can I get secure random bytes in a actix route, for password hashing?

A password hashing crate requires a &[u8] to act as a salt when generating its hashes. Of course, this salt should be generated with a secure random number generator, and the rand crate provides a few such generators. From looking through its documentation, I can write the salt-generation code like this:

//in an actix route
let mut salt: [u8; 128] = [0; 128]; //I'm not sure if 128 bytes is a good length — if anyone has information on this, please share it
let mut rng = StdRng::from_entropy(); //the only initialization technique that is documented as secure
rng.fill_bytes(&mut salt);

This seems like it may work under small load, but under larger load it seems like OS-level entropy may run out, and then cause from_entropy() to panic and/or block, both of which would be disastrous as they would cause problems for an entire Actix worker thread (which, via async Rust, can handle many requests at once). I can't use Actix's system for application state since many copies of the route may run at once, thus preventing eacy copy from having its own mut reference to the RngCore implementation (Mutex would probably cause deadlocks, and RefCell would probably cause panics if one registration request starts before another ends and they're allocated to the same worker thread.)

As such, I ask, how can I handle this cleanly and efficiently?

getrandom crate is used under the hood of the from_entropy crate, as specified in its documentation, after OS entropy source is fully initialized it will never block/panic (it still could return an error in theory, but it's extremely unlikely in practice). Note that your code will reseed PRNG each time you generate salt, which is quite inefficient. You could use the getrandom crate directly, but a more efficient approach will be to use rand::ThreadRng, it uses thread-local PRNG which is periodically reseeded using getrandom.

P.S.: As for salt size, salt should be unique for each user, so even 64-bit random salt should be sufficient in practice, though 128 and 256 bit salts are common as well.

1 Like

I would use rand::thread_rng, which returns a handle to a thread-local cryptographically-secure PRNG seeded from the system's entropy pool. It's essentially the same as sharing a single StdRng, except with the bonus that it's thread-local (so there's no need for a mutex) and has automatic reseeding.

Thanks @newpavlov and @mbrubeck, I'll use ThreadRng. I was hesitant at first since I wasn't sure if it was cryptographically secure.

FYI: cryptographically secure RNGs (including ThreadRng) implement the CryptoRng trait.