This is the third article in a series on Cryptography for the Everyday Developer. Follow along to learn the basics of modern cryptography and encryption.
Without randomness, cryptography would be impossible because all operations would become predictable and therefore insecure.
— Jean-Philippe Aumasson, Serious Cryptography
The cryptographic strength of most systems lies in their ability to generate random numbers that cannot be easily guessed or reproduced, making it difficult for adversaries to crack the encryption or predict the output. Unfortunately for us, computers and the software that they run are very predictable. As long as they are given the same inputs each time, they’ll always come up with the same outputs. This is very good for reliability, but not so good for cryptography where randomness and unpredictability are required for secure operation.
The quality of randomness is so critical to secure cryptography that specialized hardware random number generators and cryptographically secure pseudo-random number generators are used to maintain the integrity of cryptographic systems. This post discusses how random numbers are generated for use within cryptography by harnessing the randomness that exists in the physical world.
Random Number Generators
True randomness is not possible to achieve in a digital world. Instead, to get random values, systems rely on the analog environment around them by measurement: mouse movements, temperature readings, I/O device activity, network activity, system logs, and running processes can all provide sources of random activity used to generate random values.
The problem with generating random values using analog measurements (so called true random number generators, or RNGs, is that they can be slow to produce values: they operate at the speed of the analog world around them, which can be much slower than the needs of a cryptographic system processing thousands of requests per second. Imagine a web service that would need to wait for a person to move the mouse before performing a crypto operation.
To address this challenge, pseudo-random number generators are used to create unpredictable sequences of numbers of arbitrary length given a source of randomness from the analog world as input.
Pseudo Random Number Generators — Making RNGs Useful
Pseudo Random Number Generators (PRNGs) take random numbers as input, and generate long streams of random, unpredictable, and uniformly distributed values that are useful to cryptographic systems.
These systems work by taking slowly arriving random numbers from an RNG and using them to update a large memory buffer, called the entropy pool, by mixing the random bits received together. It then uses this set of truly random bits to generate longer sequences of data using an algorithm. Since this is an algorithm executed by a computer, it is deterministic, and it will produce the same set of output values given the same input value.
To make a cryptosystem useful, we need an unpredictable or non-deterministic sequence. To get that property, we make sure to use truly random numbers as input into the PNRG so that we cannot predict the sequence of numbers generated. The PRNG ensures that the deterministic algorithm used to generate long sequences of values never receives the same input twice during execution so that it can generate long sequences of pseudo random numbers.
A typical PRNG follows steps similar to this outline:
- Seed Initialization
- The generator starts with an initial value called a seed.
- The seed is a number derived from an RNG or other external source that serves as the starting point for the sequence.
- If you use the same seed, the PRNG will generate the same sequence every time.
- Transformation Function
- The generator applies a deterministic mathematical function to the current value to produce the next value. This function is designed to create a sequence of numbers that appear random given random input.
- Modulus Operation
- The output of the transformation function is usually large, so a modulus operation (mod m) is used to keep the numbers within a desired range (e.g., between 0 and 1 for floating-point numbers or within a certain integer range).
- Output Extraction
- The generated value is returned, and the process repeats to generate the next number.
Periodically, the entropy pool is updated using new data from an RNG to ensure random inputs are available to the PNRG
Cryptographically Secure RNGs
Not all PRNGs are suitable for cryptographic applications. Many standard PRNGs, such as those found in general-purpose programming languages, are designed for simulations or statistical modelling rather than security. These PRNGs can be predictable if an attacker discovers their seed or internal state, making them unsuitable for cryptographic use.
Cryptographically Secure PRNGs are a specialized class of PRNGs that meet stringent security requirements. They must produce output that is computationally indistinguishable from true randomness and be resistant to prediction, even if an attacker knows some of the previous outputs. Additionally, a Cryptographically Secure PRNGs should withstand state compromise — meaning that even if part of its internal state is exposed, it should be difficult for an attacker to reconstruct past or future outputs.
Operating systems and security libraries provide built-in CSPRNGs, such as
/dev/urandom
in Linux, or CryptGenRandom
in Windows. Modern cryptographic
libraries like OpenSSL’s RAND_bytes()
or Python’s secrets
module provide
similar utilities to programmers. These ensure that cryptographic keys, nonces,
and other security-critical values are generated with strong randomness, making
encryption systems resistant to attacks.
Cryptography relies on randomness to ensure security, as predictable operations would make encryption vulnerable to attacks. However, computers are inherently deterministic, making it challenging to generate true randomness. To address this, cryptographic systems use specialized random number generators (RNGs) and cryptographically secure pseudo-random number generators (CSPRNGs).
Random Number Generators (RNGs) derive entropy from unpredictable real-world sources like mouse movements, system activity, or environmental noise. While truly random, these sources are slow, making them impractical for high-speed cryptographic applications.
Pseudo-Random Number Generators (PRNGs) solve this issue by taking small amounts of true randomness from RNGs and expanding them into longer sequences using deterministic algorithms. These generators use an entropy pool to ensure their outputs appear random while maintaining efficiency.
Together, RNGs and PRNGs provide the randomness building block necessary to make truly secure crytosystems.