Skip to content
Blog ยป Generating Random Numbers in Rust using Ring

Generating Random Numbers in Rust using Ring

    Ring is one of the most popular Rust cryptography libraries and it provides a way to securely generate random numbers by using the ring::rand module. In this post I will show you how to generate random numbers correctly using the types provided by Ring.

    PRNGs vs CSPRNGs

    Computers are deterministic and therefore can’t produce real random numbers without input from external sources. For this reason random number generators implemented in software libraries are generally pseudo-random number generators (PRNGs). On the other hand cryptography libraries require the use of cryptographically secure pseudo-random number generators (CSPRNGs) because the values need to be completely unpredictable for most cryptographic use cases.

    PRNGs and CSPRNGs generally require a seed and then once seeded they produce random values deterministically from that initial seed. Usually CSPRNGs are seeded with an unpredictable random value from the operating system or other external source of entropy and as long as the seed is kept secret then the random numbers generated by the CSPRNG will be unpredictable.

    Summary of Types

    The ring::rand module contains the following types and functions:

    struct SystemRandom – The CSPRNG used for generating random numbers where the source of entropy is the operating system. Each application should only create a single instance of SystemRandom and use it throughout the whole application. SystemRandom is thread safe and so it can be shared across multiple threads safely.

    trait SecureRandom – Defines a fill method for filling bytes into a buffer and is implemented by the SystemRandom struct.

    struct Random – Wraps a random number and supports only exposing it once.

    trait RandomlyConstructable – The type wrapped by Random must implement RandomlyConstructable. The Ring library has implemented RandomlyConstructable on byte arrays [u8, n]. This has the effect of restricting the type of random values to byte arrays.

    fn generate – The function used to generate random numbers which takes a reference to a SecureRandom and returns a Result containing a Random<T>. See the full function definition here:

    pub fn generate<T: RandomlyConstructable>(
        rng: &dyn SecureRandom
    ) -> Result<Random<T>, Unspecified>

    Rust Imports

    Let’s start by importing the required types into our project.

    use ring::rand;
    use ring::rand::SystemRandom;
    use ring::rand::Random;
    use ring::rand::SecureRandom;

    Create an Instance of SystemRandom

    To create an instance of the SystemRandom CSPRNG we simple call the SystemRandom::new() function. The new function is guaranteed to always succeed and have low latency. This is because most of the initialisation logic is deferred to the SecureRandom::fill method.

    let sys_random = SystemRandom::new();

    Generate Random Numbers

    We can generate random numbers in two ways using the ring::rand module; by using the SecureRandom::fill method or by using the rand::generate function.

    To use the fill method we first need to create a buffer which is an array of bytes which will be used to store the random data. We then call fill, passing in a reference to the buffer. The buffer needs to be mutable to support updating the buffer in place. After the call to fill, the SystemRandom instance will have updated the buffer with random bytes sourced from the OS. The fill method will internally call the OS to get the random data. For example on Linux it will use the getrandom syscall and on MacOS will use the SecRandomCopyBytes syscall.

    In the below example I debug print the random bytes and then convert the bytes to a u32 before printing a second time.

    // initialise a buffer with zeros
    let mut buffer = [0u8; 4];
    
    // fill the buffer with random bytes
    sys_random.fill(&mut buffer).unwrap();
    println!("{:?}", buffer);
    println!("{:?}", u32::from_be_bytes(buffer));

    In this next example we use the higher level generate function which handles creating the buffer for us. We pass in a reference to the SystemRandom and it simply returns the random value wrapped in a Random instance. Calling expose on the Random will consume it and so expose can only be called once. This design can help prevent accidental reuse of the same random value which can have big consequences in cryptographic systems. In these examples the byte array used to store the random value is set to size 4 but we could pick any size to suite our needs.

    // generate the random number
    let result : Random<[u8; 4]> = rand::generate(&sys_random).unwrap();
    
    // get the value
    let rand_bytes = result.expose(); // can only be called once
    println!("{:?}", rand_bytes);
    println!("{:?}", u32::from_be_bytes(rand_bytes));

    Full Sample Code

    Here is the full code sample for reference.

    use ring::rand;
    use ring::rand::SystemRandom;
    use ring::rand::Random;
    use ring::rand::SecureRandom;
    
    fn main() {
        // Create a secure random number generator
        let sys_random = SystemRandom::new();
    
        // initialise a buffer with zeros
        let mut buffer = [0u8; 4];
    
        // fill the buffer with random bytes
        sys_random.fill(&mut buffer).unwrap();
        println!("{:?}", buffer);
        println!("{:?}", u32::from_be_bytes(buffer));
    
        // generate the random number
        let result : Random<[u8; 4]> = rand::generate(&sys_random).unwrap();
        // get the value
        let rand_bytes = result.expose(); // can only be called once
        println!("{:?}", rand_bytes);
        println!("{:?}", u32::from_be_bytes(rand_bytes));
    
        // generate more random numbers using the same SystemRandom object
        let result : Random<[u8; 4]> = rand::generate(&sys_random).unwrap();
        let rand_bytes = result.expose();
        println!("{:?}", rand_bytes);
        println!("{:?}", u32::from_be_bytes(rand_bytes));
    }

    Conclusion

    In this post we covered how to securely generate random numbers using the Ring cryptography library. We showed how to create an instance of the SystemRandom type and how to use either SecureRandom::fill or rand::generate to create random bytes and then convert into a number if required. If you have any questions on this topic, feel free to reach out.

    Leave a Reply

    Your email address will not be published. Required fields are marked *