Randomness
As a passphrase generator, Passgen depends heavily on having access to sources of randomness.
There are two kinds of sources of randomness that Passgen supports:
- Non-deterministic: Sources such as the system secure random number generator always generates unpredictable output. This is the default for Passgen, if no other option is specified.
- Deterministic: In some cases, it is desired to use deterministic sources of randomness. For example, to be able to generate passphrases from a master-passphrase. These sources of randomness are also used internally for the unit tests, to ensure that the output of Passgen is stable.
Independent streams
Since Passgen supports outputting multiple passphrases, every source of randomness needs to be able to generate n independent streams of randomness.
For non-deterministic sources of randomness, it can just create multiple instances of the same source of randomness. For deterministic sources of randomness, care must be taken to ensure that:
- Independence: Every stream of randomness returned is independent, meaning that it is non-overlapping with any other stream.
- Determinism: Whenever you get the nth stream, regardless of which streams you have produced previously (or in which order), it will always yield the same sequence of random values.
Deterministic source of randomness are typically implemented using pseudorandom number generators. Implementing these properties can be done in one of three ways:
- Built-in notion of streams: Some of these have a built-in notion of streams, such that for the same seed, n independent streams can be produced.
- Ability to seek: Some have the ability to seek in the output stream. Seeking to a large offset (2^64) is sufficient to implement independence for all reasonable use-cases of Passgen, because Passgen is unable to generate a passphrase using this much of the stream.
- Recursive seeding: It can also be implemented using recursion, using the pseudorandom number generator to generate seeds for itself.
Implementation
Passgen uses the rand
crate behind the scenes. All sources of randomness must
implement the RandomSource
trait, which has methods to use it to create some value
which implements rand::Rng
:
#![allow(unused)] fn main() { pub trait RandomSource { /// Underlying type of the randomness source. type Rng: RngCore + 'static; /// Return a stream of randomness for the given index. fn get_rng(&self, index: usize) -> Self::Rng; } }
As you can see, the get_rng()
function takes an index. When Passgen creates
multiple passphrases, it will generate one random number generator per passphrase
that it generates.
Part of the reason this interface was built this way is because it allows Passgen to use multi-threading when generating large amounts of passphrases. Another possible design would be to use a single randomness generator and to use it in sequence to generate passphrases, but this means that the generation of each passphrase is dependent on the previous one and forces Passgen to work serially.
Passgen currently supports three randomness sources:
- System: Secure system random number generator.
- Xoshiro256PlusPlus: Deterministic number generator, mainly used for testing
- ChaCha20: Cryptographic deterministic number generator, used to implement the master-passphrase mode.
These sources are discussed in the next chapters.