arc4random(3bsd)

πŸ“¦
version 0.12.2 Β· 3 versions
, in alpine 3.22, alpine 3.23, alpine 3.24, arch rolling, debian trixie, epel 10, epel 9, fedora 43, fedora 44, nixos 26.05, ubuntu noble, ubuntu questing, ubuntu resolute, ubuntu stonking

πŸ’– arc4random(3bsd) β€” the random number generator page, bestie edition πŸ’–

okay so you opened the arc4random(3bsd) manual, from the LOCAL section, and honestly? you're about to have a great time. this whole page is about getting good random numbers and not getting owned. let's go πŸŽ€


🌸 NAME 🌸

arc4random, arc4random_uniform, arc4random_buf, arc4random_stir, arc4random_addrandom β€” random number generator. that's the whole squad, no cap πŸ’…


πŸ“š LIBRARY πŸ“š

Utility functions from BSD systems β€” that's libbsd, link with -lbsd. icon behavior honestly ✨


πŸ¦‹ SYNOPSIS πŸ¦‹

#include <stdlib.h>
(peep libbsd(7) for the whole include usage situation, iykyk)
uint32_t
arc4random(void);

uint32_t
arc4random_uniform(uint32_t bound);

void
arc4random_buf(void *buf, size_t len);

void
arc4random_stir(void);

void
arc4random_addrandom(unsigned char *buf, int len);

look at them. five little functions. all of them just vibing and being cryptographically secure. we love to see it 🫢


✨ DESCRIPTION ✨

so the arc4random family is a cryptographic pseudorandom number generator. it seeds itself automatically from the system entropy pool β€” like, you don't have to do anything, it just knows β€” and it's safe to call from multiple threads. arc4random is literally designed so an adversary cannot guess its outputs, which is very much not the case for rand(3) and random(3). and it's faster and more convenient than reading from /dev/urandom yourself. obsessed. no notes. πŸ’–

here's the tea on each one:

  • arc4random() β€” returns an integer in [0, 2^32), chosen independently with a uniform distribution. every value equally likely, as it should be πŸ‘‘
  • arc4random_uniform() β€” returns an integer in [0, bound), also chosen independently with a uniform distribution. you give it a bound, it respects the bound πŸŽ€
  • arc4random_buf() β€” stores len bytes into the memory pointed to by buf, and each byte is chosen independently from [0, 256) with a uniform distribution. fill my buffer, bestie πŸ’…
  • arc4random_stir() β€” draws entropy from the operating system and folds it into the library's PRNG state, so it influences future outputs. stirring the pot, literally πŸ“
  • arc4random_addrandom() β€” takes len bytes from the buffer buf and incorporates them into the library's PRNG state, influencing future outputs. important: len must be nonnegative ⚠️

and here's the part that makes life easy: you do not need to call arc4random_stir() or arc4random_addrandom() before calling the other arc4random functions. the very first call to any arc4random function will initialize the PRNG state unpredictably from the system entropy pool. it handles itself. main character energy fr fr ✨


πŸ›‘οΈ SECURITY MODEL πŸ›‘οΈ

this is the section where we get serious, because the arc4random functions provide the following security properties against three different classes of attackers β€” assuming enough entropy is provided by the operating system. and if the OS isn't providing entropy, that's on the OS, not on us πŸ’€

  1. An attacker who has seen some outputs of any of the arc4random functions cannot predict past or future unseen outputs.
  2. An attacker who has seen the library's PRNG state in memory cannot predict past outputs.
  3. An attacker who has seen one process's PRNG state cannot predict past or future outputs in other processes β€” especially not its parent or its siblings.

and when we say one 'output', we mean the result of any single request to an arc4random function, no matter how short it is. one tiny little request still counts, bestie πŸ¦‹

property number two has a few nicknames, and you should know all of them: it's sometimes called 'forward secrecy', 'backtracking resistance', or 'key erasure after each output'. three names, one iconic behavior 🌈


πŸ”¬ IMPLEMENTATION NOTES πŸ”¬

okay nerd corner, but like, the cool nerd corner 🧚

right now the arc4random functions are implemented using the ChaCha20 pseudorandom function family. for any 32-byte string s, ChaCha20_s is a function from 16-byte strings to 64-byte strings. and here's the conjecture: if s is chosen with uniform distribution, then the distribution on ChaCha20_s is indistinguishable β€” to a computationally bounded adversary β€” from a uniform distribution on all functions from 16-byte strings to 64-byte strings. that's the whole flex πŸ’ͺ

the PRNG state is a 32-byte ChaCha20 key s. and each request to an arc4random function does this little dance:

  • computes the 64-byte quantity x = ChaCha20_s(0),
  • splits x into two 32-byte quantities s' and k,
  • replaces s by s', and
  • uses k as output.

then each function does its own thing:

  • arc4random() yields the first four bytes of k as output directly. short and sweet πŸ“
  • arc4random_buf() either yields up to 32 bytes of k as output directly, or, for longer requests, uses k as a ChaCha20 key and yields the concatenation ChaCha20_k(0) || ChaCha20_k(1) || ... as output. it scales, periodt ✨
  • arc4random_uniform() repeats arc4random() until it obtains an integer in [2^32 % bound, 2^32), and reduces that modulo bound. rejection sampling but make it cute πŸ’…

now, threading tea β˜•: the PRNG state is per-thread, unless memory allocation fails inside the library β€” in which case some threads may share global PRNG state with a mutex. (not ideal but it's the fallback, okay?)

the global PRNG state is zeroed on fork in the parent via pthread_atfork(3), and the per-thread PRNG state is zeroed on fork in the child via minherit(2) with MAP_INHERIT_ZERO, so that the child cannot reuse or see the parent's PRNG state. no peeking, no hand-me-downs πŸ™ˆ. and the PRNG state is reseeded automatically from the system entropy pool on the first use of an arc4random function after that zeroing.

⚠️ heads up: the first use of an arc4random function may abort the process in the highly unlikely event that library initialization necessary to implement the security model fails. and arc4random_stir() and arc4random_addrandom() may also abort the process in the highly unlikely event that the operating system fails to provide entropy. rare, but dramatic when it happens πŸ’€

πŸ‘€ SEE ALSO πŸ‘€

and the reading list for the girlies who want the deep lore πŸ“–:

Daniel J. Bernstein, ChaCha, a variant of Salsa20, http://cr.yp.to/papers.html#chacha, 2008-01-28, Document ID: 4027b5256e17b9796842e6d0f68b0b5e.


πŸ•°οΈ HISTORY πŸ•°οΈ

these functions first appeared in OpenBSD 2.1, FreeBSD 3.0, NetBSD 1.6, and DragonFly 1.0. then later, arc4random(), arc4random_buf() and arc4random_uniform() showed up in glibc 2.36. glow up across decades honestly 🌟


πŸ’€ BUGS πŸ’€

grab a snack, this section is a whole saga.

  • there is no way to get deterministic, reproducible results out of arc4random for testing purposes. it's secure, it's not a lab rat πŸ§ͺ
  • the name 'arc4random' was chosen for hysterical raisins β€” it was originally implemented using the RC4 stream cipher, which has been known since shortly after it was published in 1994 to have observable biases in the output, and is now known to be broken badly enough to admit practical attacks in the real world. unfortunately, the library found widespread adoption and the name stuck before anyone recognized that it was silly. so yeah. the name is a fossil. the implementation is not πŸ¦–
  • the signature of arc4random_addrandom() is silly. there is no reason to require casts or accept negative lengths β€” it should take a void * buffer and a size_t length. but it's too late to change that now. we're stuck with it, bestie 😭
  • arc4random_uniform() does not help you choose integers in [0, n) uniformly at random when n > 2^32. just so you know πŸ‘€
  • the security model of arc4random is stronger than many applications need, and stronger than other operating systems provide. for example, applications encrypting messages with random β€” but not secret β€” initialization vectors only need to prevent an adversary from guessing future outputs, since past outputs will have been published already. so if that's you, you're getting more security than you asked for, which is a nice problem to have πŸŽ€
  • on the one hand, arc4random could be marginally faster if it were not necessary to prevent an adversary who sees the state from predicting past outputs. on the other hand, there are applications in the wild that use arc4random to generate key material, such as OpenSSH, so for the sake of NetBSD users it would be imprudent to weaken the security model. and on the third hand, relying on the security model of arc4random in NetBSD may lead you to an unpleasant surprise on another operating system whose implementation of arc4random has a weaker security model. three hands, three arguments, all valid 🫠
  • one may be tempted to create new APIs to accommodate different security models and performance constraints without unpleasant surprises on different operating systems. this should not be done lightly, though, because there are already too many different choices, and too many opportunities for programmers to reach for one and pick the wrong one. restraint, bestie. restraint πŸ’…

and that's the page!! before you go β€” this one's from the Debian folks, dated November 16, 2014, and it lives at arc4random(3bsd). go forth and be cryptographically unpredictable πŸ¦‹πŸ’–βœ¨

πŸ“¦ where this page comes from