Shameless plug: my bc [1] has a random number generator that is much higher quality than the shell one. Because it's bc, you can generate random numbers of any size, and you can generate random reals as well.
If you have a password manager installed on your system (and you should) then it might have a command-line client that can generate new passwords. e.g.:
> The shuf, shred, and sort commands sometimes need random data to do their work. For example, ‘sort -R’ must choose a hash function at random, and it needs random data to make this selection.
> By default these commands use an internal pseudo-random generator initialized by a small amount of entropy, but can be directed to use an external source with the --random-source=file option. An error is reported if file does not contain enough bytes.
You might end up with a fairly easy-to-brute-force password using that method.
They say "never roll your own crypto", but I think that advice probably holds pretty well for password generators(/managers). I'll stick with using a dedicated program. I reckon it's far less likely to have obvious blunders than something that's just been cobbled together using whatever tools happened to be lying around nearby.
The go-to source for randomness in Linux is /dev/urandom, is it not?
I’m not sure the best way to make use of it as a source of random decimals — there are surely a dozen ways to do so — but this works:
printf %d 0x`
head -c 1000 /dev/urandom
| shasum
| head -c 8
`
As always, my advice for things like this is that if you’re in need of true random numbers in your shell script then you should probably stop writing whatever it is you are writing as a shell script.
> As always, my advice for things like this is that if you’re in need of true random numbers in your shell script then you should probably stop writing whatever it is you are writing as a shell script.
The use of "true" here seems ambiguous.
Does this statement mean that you discourage creation of cryptographic applications with shell scripts, you discourage use of shell scripts to orchestrate applications that use /dev/urandom for any reason, or something else entirely?
(first, and only edit: This comment is in good faith, so if you're downvoting please respond to let me know why)
There are cryptographically secure pseudorandom generators. In fact all sources non-hardware (i.e. /dev/random and /dev/urandom) are psuedo random as are the keystream generators for most algos that use one.
Yes, I'm aware of that. The commenter seems to be implying that one should stop using a shell script when one needs "true" random, and I'm curious as to why since one can call out to Perl, Python, etc.
The linked gist doesn't appear to be making the case for using its recommendations in cryptographic applications so what I'm ultimately after is what the commenter thinks is being done in the shell that's inappropriate.
Incorrect. I'm not sure what you mean by "true random", and we could get into some interesting philosophical discussion on that.
However, all software random number generators are pseudo random, including cryptographic ones. They are called CSPRNGs, which stands for Cryptographically Secure Pseudo Random Number Generator.
There are several scenarios where a cryptographic-quality random number generator might be needed within the context of a shell script.
My favorite tool for this is "openssl rand" and I can find the manual page on rhel7 with "man rand."
Here are 8 bytes of randomness in base64:
$ openssl rand -base64 8
mGHxYcVOmA8=
If you like decimal:
$ printf %d\\n 0x$(openssl rand -hex 2)
21924
When I've got dozens of users to create, this is how I am assigning their initial passwords.
The shell's $RANDOM is somewhat less trustworthy than other sources here. It also does not appear to be POSIX, but comes from ksh88, where it is described as returning a random integer between 0 and 32767 each time it is referenced. If unset in a script, it cannot be used again for the life of the shell.
Sometimes, extreme portability makes us do desperate things.
# uname -a
HP-UX prdcrm B.10.20 A 9000/800 862741461 two-user license
# ls -l /dev/urandom
/dev/urandom not found
# openssl rand -base64 8
v51U1RvSD2Q=
While being overly pedantic (because "not true" random is a spectrum), it is good practice to use random numbers to ensure that a recurring cron job does not run at the same time on all machines.
This goes from good practice to necessary as a cron job uses increasing resources or talks to a centralized service.
I assume that the random numbers in bash come from an uniform distribution. Is there a way of getting random numbers from a different distribution with given parameters?
I can only think of one-liners in R or maybe python.
The standard approach is to take the CDF form of the desired distribution, solve it for equaling some constant, then plug in your uniform value and out comes where on the desired distribution that maps to. The hard part is you either have to pre-solve it or use numerical solving methods and in either case you'd have to write that in Bash.
for a normal distribution, chatgpt suggests the box-muller transform and calling out to awk (which is a scripting language that continues to surprise me; it seems underrated?)
In testing it out, they did seem normally-distributed but rerunning it quickly did seem to produce more consecutively duplicate results than would be expected, not sure why that would be (I was using ranges of 0-10 and 0-20, perhaps the probability of normally-distributed consecutive dupes in that range is quite higher?)
EDIT: Nope, there's definitely something wrong with this, possibly the rand() call(s)? Can anyone guess? (A real-world example of the perils of relying on chatGPT!) EDIT2: Figured it out. srand with no args seeds to the current second, making any run of this function within the same second produce the same result. Fixing this left as an exercise to the reader EDIT3: I fixed it by seeding it externally via $RANDOM.
"The Box-Muller transform generates two normally distributed random numbers, z0 and z1, using the two uniformly distributed random numbers u1 and u2. However, only z0 is used in the provided function to create the random number within the given range. If you want to additionally supply z1 in the output, you can." It then provides the modified code.
Are you on GNU/Bash version 5.1[0]? A new bash variable, SRANDOM added to GNU/Bash-5.1 release, which gets its random data from the system’s entropy engine and is not lined and cannot be reseeded to get an identical random sequence. For example:
echo "$SRANDOM"
for r in {1..5}; do printf "%s\n" "$SRANDOM"; done
Maths isn't my background, but isn't this a serious flaw with the quality of the pseudo-random numbers it generates?
I remember learning about the enigma machine and how it had a flaw where a letter could not be transformed into itself. And knowing that, for example, an A could never be a A was critical to cracking the enigma code. Because of that knowledge, my cryptographic intuition is that any seemingly minor fact like this could actually be a critical flaw.
I doubt it matters much. Bash's $RANDOM is implemented using a linear congruential generator, which is not suitable for cryptographic applications anyway.
The problem with random as a word is it covers user intents ranging from "crudely load balance well enough most of the time" or "add a small variable delay to smooth the load over time" up to extremely security critical crypto applications.
I used this to create random decisions for a while:
if (( RANDOM % 2 == 0 )); then echo Yes; else echo No; fi
I also logged those decisions. I got significantly more No then Yes. I could never reproduce the bias in a loop. But in real life usage, I got more No. Could be by chance of course. But it made me worried enough that I switched to urandom:
bit=$(od -An -N1 -i /dev/urandom)
if (( $bit &1 ))
then echo "Yes"
else echo "No"
fi
Another fun fact: on Linux systems, the /proc/sys/kernel/random/uuid file contains a random UUID. This can be used to get 32-bit random numbers in POSIX shell without calling out to external utilities. Quite handy on OpenWrt.
On a related note, I've once had to reproducibly generate a random sequence of N-bytes, and one of the more elegant ways I discovered and ended up using was to 'encrypt' a known sequence of bytes with the required length (so that the sequence will be long enough) with some known 'password', and truncating it.
63 comments
[ 3.2 ms ] story [ 136 ms ] thread> I could do more experiments and try to definitively determine what's happening, bottom line, don't use $RANDOM, even for unimportant random numbers.
It is also seeded.
[1]: https://git.gavinhoward.com/gavin/bc
It can also be used to generate either really small or really large integers, arbitrary size. My bc will take care of everything behind the scenes.
If you want to generate a real between 0 and 1, use this:
where <places> is the number of decimal places you want. Note the "-l" argument; this function is not built-in like the other two.If you want an arbitrary real (not between 0 and 1), use this:
My bc will seed itself from the system CSPRNG, but you can seed the PRNG by assigning the seed to the special variable `seed` first: You can even save the current seed by getting the value of the `seed` variable: If you want to use the same sequence over multiple invocations of bc, you need to save the seed to pass to the next invocation: (Haven't tried the above code. Sorry if it's wrong.)This pattern works because the `seed` variable is updated after every call to the PRNG.
Does that help?
Say you want to generate a strong password, you can use this handy function that works in bash and zsh:
> The shuf, shred, and sort commands sometimes need random data to do their work. For example, ‘sort -R’ must choose a hash function at random, and it needs random data to make this selection.
> By default these commands use an internal pseudo-random generator initialized by a small amount of entropy, but can be directed to use an external source with the --random-source=file option. An error is reported if file does not contain enough bytes.
-- https://www.gnu.org/software/coreutils/manual/html_node/Rand...
(emphasis mine)
You might end up with a fairly easy-to-brute-force password using that method.
They say "never roll your own crypto", but I think that advice probably holds pretty well for password generators(/managers). I'll stick with using a dedicated program. I reckon it's far less likely to have obvious blunders than something that's just been cobbled together using whatever tools happened to be lying around nearby.
I use it for usernames since Bitwarden doesn’t support diceware names.
I’m not sure the best way to make use of it as a source of random decimals — there are surely a dozen ways to do so — but this works:
As always, my advice for things like this is that if you’re in need of true random numbers in your shell script then you should probably stop writing whatever it is you are writing as a shell script.AFAIK it's the go-to source of randomness in any modern-ish unix.
> I’m not sure the best way to make use of it as a source of random decimals — there are surely a dozen ways to do so — but this works:
`od(1)` will do binary data to decimal:
The use of "true" here seems ambiguous.
Does this statement mean that you discourage creation of cryptographic applications with shell scripts, you discourage use of shell scripts to orchestrate applications that use /dev/urandom for any reason, or something else entirely?
(first, and only edit: This comment is in good faith, so if you're downvoting please respond to let me know why)
The linked gist doesn't appear to be making the case for using its recommendations in cryptographic applications so what I'm ultimately after is what the commenter thinks is being done in the shell that's inappropriate.
However, all software random number generators are pseudo random, including cryptographic ones. They are called CSPRNGs, which stands for Cryptographically Secure Pseudo Random Number Generator.
My favorite tool for this is "openssl rand" and I can find the manual page on rhel7 with "man rand."
Here are 8 bytes of randomness in base64:
If you like decimal: When I've got dozens of users to create, this is how I am assigning their initial passwords.The shell's $RANDOM is somewhat less trustworthy than other sources here. It also does not appear to be POSIX, but comes from ksh88, where it is described as returning a random integer between 0 and 32767 each time it is referenced. If unset in a script, it cannot be used again for the life of the shell.
https://lwn.net/Articles/889452/
Pragmatically, they are equivalent for most scripting use cases.
This goes from good practice to necessary as a cron job uses increasing resources or talks to a centralized service.
Puppet implements this as splay: https://www.puppet.com/docs/puppet/7/configuration.html#spla...
I can only think of one-liners in R or maybe python.
EDIT: Nope, there's definitely something wrong with this, possibly the rand() call(s)? Can anyone guess? (A real-world example of the perils of relying on chatGPT!) EDIT2: Figured it out. srand with no args seeds to the current second, making any run of this function within the same second produce the same result. Fixing this left as an exercise to the reader EDIT3: I fixed it by seeding it externally via $RANDOM.
"The Box-Muller transform generates two normally distributed random numbers, z0 and z1, using the two uniformly distributed random numbers u1 and u2. However, only z0 is used in the provided function to create the random number within the given range. If you want to additionally supply z1 in the output, you can." It then provides the modified code.
PORT=$(shuf -i 1000-2000 -n1)
aria2c --listen-port=$PORT ...other...options...
I remember learning about the enigma machine and how it had a flaw where a letter could not be transformed into itself. And knowing that, for example, an A could never be a A was critical to cracking the enigma code. Because of that knowledge, my cryptographic intuition is that any seemingly minor fact like this could actually be a critical flaw.
https://gist.github.com/pmarreck/e65f457e8755e461a87db7c94d4...
read -r < /proc/sys/kernel/random/uuid
MYRAND=$(( 0x${REPLY%%-*} ))
`( yes '00' | head -n 100000 | tr -d $'\n') | xxd -r -p | aespipe -e AES256 -P <(echo 'your-seed')`