Jonatan
Jonatan

Reputation: 2886

Something similar to /dev/urandom with configurable seed?

I'm dd'ing from /dev/urandom in order to create files with random contents. This works well, but I would like to be able to reproduce the file contents at some later point by running the PRNG again with the same seed. Is there any seedable PRNG which exposes a character device?

I'm using recent Linux 3.X kernels.

Upvotes: 5

Views: 5807

Answers (6)

Python 3.9 random.randbytes + random.seed

I've learnt to stop fighting what Bash can't do and just go with the flow:

randbytes() (
  python -c 'import random;import sys;random.seed(int(sys.argv[1]));sys.stdout.buffer.write(random.randbytes(int(sys.argv[2])))' "$@"
)

Usage:

randbytes <seed> <nbytes>

e.g.:

randbytes 0 8 | hd

always outputs 8 identical pseudo-random bytes with seed 0:

00000000  cd 07 2c d8 be 6f 9f 62                           |..,..o.b|
00000008

Readable multiline version at: Generating random string of seedable data

On my Lenovo ThinkPad P51, I can dump 100 million bytes in ramfs in 0.5s. however, if I try to dump 1 billion it blows up with:

Python int too large to convert to C int

so it is something to keep in mind.

For comparison:

time sudo dd if=/dev/urandom of=ramfs/test bs=4k count=24414

took 2.5s, so it is slower, which is not surprising as it is a more random source, while the Python generator is deterministic and appears to be written in C.

Tested on Ubuntu 20.10, Linux kernel 5.8.0.

Upvotes: 2

hdante
hdante

Reputation: 8030

Check out the ISO C rand() and srand() functions:

http://man7.org/linux/man-pages/man3/rand.3.html

Upvotes: 0

Christian Hudon
Christian Hudon

Reputation: 1950

If you want your files to be random but reproductible given a seed, you are using the wrong tool. /dev/urandom gets its randomness from the environment (timing of incoming and outgoing network packets, disk accesses, etc.) so even if you start with the same seed, it's very highly unlikely that you will get the same sequence of random numbers two times in a row.

You want a conventional (aka software-only) pseudo-random number generator. The Mersenne Twister is a good one. It should have an implementation in the language you're using. Or just use whatever pseudo-random number generator comes with your language.

Upvotes: 0

Nikos C.
Nikos C.

Reputation: 51910

/dev/*random do not use a seed, because they're not pseudo-random number generators. They provide randomness from the environment (using even hardware as a source, like keyboard, interrupts, network, etc.) In the case of /dev/urandom, a seed and an RNG is only used when the pool is exhausted. But it's still unpredictable when that happens.

So, do not read random numbers from /dev/*random. Instead, use the bash facilities to generate random numbers:

#! /bin/bash

# Seed the RNG
RANDOM = 1234

# Print 10 random numbers
for i in {1..10}
do
    echo $RANDOM
done

The above script will print the same sequence of numbers every time, because the seed is constant.

If you want to generate bytes, meaning values in the range of 0 to 255, then use printf instead of echo and cycle the random number you get to that range:

printf "\\x$(printf "%x" $(($RANDOM % 256)))"

If you now run the script and redirect output to a file, it will contain 10 "random" bytes.

Upvotes: 1

Taymon
Taymon

Reputation: 25676

/dev/urandom is designed to be as unpredictable as possible. It sounds like you want a more conventional seeded pseudorandom number generator.

Here's one I just wrote in C:

#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    char *endptr;
    unsigned long int seed;

    if (argc != 2 || *argv[1] == '\0') {
        fprintf(stderr, "usage: %s seed\n", argv[0]);
        return EXIT_FAILURE;
    }
    errno = 0;
    seed = strtoul(argv[1], &endptr, 0);
    if (errno != 0 || *endptr != '\0' || seed > UINT_MAX) {
        fprintf(stderr, "%s: invalid seed\n", argv[0]);
        return EXIT_FAILURE;
    }
    srandom((unsigned int) seed);

    while (1) {
        int i;
        long int randomnum = random();
        for (i = 0; i < sizeof randomnum; i++) {
            if (putchar((randomnum >> (i * CHAR_BIT)) & UCHAR_MAX) == EOF) {
                return EXIT_SUCCESS;
            }
        }
    }
}

This is a program, not a device file, but its output is the same format as you'd get from /dev/urandom. You can pipe the output from it into dd and omit if.

If you need to come up with a truly random seed to supply to the program, you can get one in bash from /dev/urandom like this:

seed=$(od -vAn -N4 -tu4 </dev/urandom)

Replace 4 with whatever sizeof(unsigned int) is on your machine (it's probably 4).

Upvotes: 2

Ottavio Campana
Ottavio Campana

Reputation: 4188

taken from urandom documentation

When a Linux system starts up without much operator interaction, the entropy pool may be in a fairly predictable state. This reduces the actual amount of noise in the entropy pool below the estimate. In order to counteract this effect, it helps to carry entropy pool information across shut-downs and start-ups. To do this, add the following lines to an appropriate script which is run during the Linux system start-up sequence:

    echo "Initializing kernel random number generator..."
    # Initialize kernel random number generator with random seed 
    # from last shut-down (or start-up) to this start-up.  Load and 
    # then save 512 bytes, which is the size of the entropy pool.
    if [ -f /var/random-seed ]; then
            cat /var/random-seed >/dev/urandom
    fi
    dd if=/dev/urandom of=/var/random-seed count=1

Upvotes: 1

Related Questions