For security-sensitive values in Rust, take random bytes from the operating system through the getrandom crate, or from the rand crate's thread generator if its design fits your threat model. Do not seed a generator with the clock, do not write your own cipher, and compare secrets in constant time.
What do the rand docs promise?
The rand documentation describes ThreadRng as a reference to a lazily initialized thread-local generator, obtained with rand::rng(). Its stated design criteria include automatic seeding from the system generator and after every 64 kB of output, and a rigorously analyzed, unpredictable (cryptographic) pseudo-random algorithm, currently ChaCha with 12 rounds. It also says it is meant to be a "fast, reasonably secure generator" and leaves it to the user to decide whether that meets their requirements.
Read the limits too. There is no automatic reseeding on process fork, and the docs recommend reseeding explicitly. The state is not required to be zeroed on exit. The project says security must be judged against a threat model and gives no guarantee of fitness for purpose.
When should you go to the operating system directly?
The getrandom crate retrieves random data from operating system sources and assumes the system always provides high-quality, cryptographically secure data. It maps to the right call per platform, such as the getrandom system call on Linux, getentropy on macOS and ProcessPrng on current Windows. Its docs call it a low-level API and say most users should prefer a higher-level library like rand.
A practical split: for session tokens, API keys, password reset codes and key material, ask the system generator for bytes directly, or use a crypto library's own key generation. For shuffling a list in a game or picking a retry jitter, the thread generator is fine.
A constructed example: a team builds password reset codes from SystemTime seconds mixed through a fast non-cryptographic generator. An attacker who knows roughly when a code was requested can enumerate a few thousand candidates. Replacing it with 16 bytes from the system generator, encoded, removes the guessable input.
What about comparing secrets?
A normal equality check can return early at the first differing byte, and the time taken can leak information. The subtle crate offers traits and a Choice type intended to run in constant time. Its own documentation is candid: it is a best-effort attempt, since side channels depend on the whole deployed system, including hardware, and the compiler can in principle undo constant-time bitwise tricks. Use it for token and MAC comparisons, and prefer a vetted crypto library that already does the comparison inside its verify call.
What are the basic rules for crypto in Rust?
- Use maintained, audited libraries for primitives and protocols. Do not implement ciphers, hashes or key exchange yourself.
- Do not reuse nonces with the same key for any cipher that forbids it. Let the library generate them.
- Keep keys in wrapper types that do not print, and wipe them on drop. See the companion post on secrets handling.
- Use authenticated encryption, not encryption alone.
- Watch advisories for the crates you use, since cryptography bugs are fixed in releases.
What to do
- Search for
thread_rng,rand::rng,SmallRng,StdRng::seed_from_u64and any use of time as a seed. Mark which ones feed security decisions. - For tokens and keys, switch to system randomness or library key generation.
- Read the fork guidance if the service forks workers, and reseed in the child.
- Replace
==on secrets with a constant-time comparison. - Add a test that generates a few thousand tokens and checks for duplicates and length, as a sanity check and not a proof of randomness.
- Pin and audit crypto crates in your lockfile. Safeguard's dependency scanning can flag known issues in them.
Quick check
Is seeding with the current time acceptable?
Not for security values. Time is guessable. Use system randomness, which the getrandom docs describe as cryptographically secure.
Does constant-time comparison guarantee no timing leak?
No. The subtle docs call their approach best effort because side channels depend on hardware and compiler behavior too.
Sources
Last reviewed: 18 October 2026.