2

What is the best way to generate random numbers in Solaris?

I can not seem to find a good answer for this. Most results do not work in my environment. There is a variable or command RAND that seems logical it would work in some manner similar to $RANDOM which I see in most of my searches but it always produces 0.

I have found this command

od -X -A n /dev/random | head -2

Which seems very random but the return format is odd (to me).

     140774 147722 131645 061031 125411 053337 011722 165106
     066120 073123 040613 143651 040740 056675 061051 015211

Currently using:

-bash-3.2$ uname -a
SunOS XXXXXXXXX 5.10 Generic_150400-29 sun4v sparc SUNW,SPARC-Enterprise-T5120
  • 1
    bash and ksh (among others), even on Solaris, provide $RANDOM, which you can use for a random integer between zero and one less than a modulus. For example, $[${RANDOM}%5] will resolve to a random number between 0 and 4, inclusive. Note that sh does not provide $RANDOM. – DopeGhoti Nov 23 '15 at 23:20

3 Answers3

6

$RANDOM is available in ksh and in bash, but not in /bin/sh. The value is a random number between 0 and 32768, and is not suitable for cryptographic use.

Reading from /dev/random generates a stream of random bytes which is suitable for cryptographic use. Since these are arbitrary bytes, potentially including null bytes, you can't store them in a shell variable. You can store $n bytes in a file with

</dev/random dd ibs=1 count=$n >rnd

You can use od to transform these bytes into a printable representation using octal or hexadecimal values. If you find the output “strange”, well, maybe you should pick different od options.

Another option to obtain a printable representation is to call uuencode to produce Base64:

</dev/random dd ibs=1 count=$n | uuencode -m _ | sed -e '1d' -e '$d'
  • Well, if you're going to implement cryptography in shell, you're doomed anyway :-) – Wouter Verhelst Nov 24 '15 at 01:17
  • @WouterVerhelst You might want to generate a key from a shell script, and pass it to some other command that does cryptography. The random value might also be something that isn't directly involved in cryptography, but needs to be unpredictable, such as a password/cookie of some kind. /dev/random is suitable for that, $RANDOM isn't. – Gilles 'SO- stop being evil' Nov 24 '15 at 01:23
  • This is getting way off topic, but no. Generating a key involves much more than 'just' random numbers, and doing it in shell is a terrible idea. There are certainly reasons why you might want to use /dev/random in a shell script, but cryptography is not one of them. – Wouter Verhelst Nov 24 '15 at 09:27
  • 1
    @WouterVerhelst I write cryptographic software for a living, and it is my professional opinion that there is nothing wrong with generating a cryptographic key by retrieving bytes from /dev/random with a shell script. (There are of course plenty of ways to do that wrong, or to misuse the key, but the fundamental approach is sound.) – Gilles 'SO- stop being evil' Nov 24 '15 at 10:33
  • @Gilles I am not sure what the issue is with the server I am working on, but yesterday echo $RANDOM was producing null / blank values. Today it appears to be working fine. This makes me nervous about making a script that relies on it so I appreciate you looking into the other option. Thanks! – JaredTS486 Nov 24 '15 at 15:15
1

This are three ways to get n bytes, expressed as 2*n Hexadecimal digits:

#!/bin/bash
n=64
# Read n bytes from urandom (in hex).
xxd -l "$n" -p                    /dev/urandom | tr -d " \n" ; echo
od  -vN "$n" -An -tx1             /dev/urandom | tr -d " \n" ; echo
hexdump -vn "$n" -e ' /1 "%02x"'  /dev/urandom ; echo

Reading from urandom (and Please, do use urandom).

The hex digits may be redirected to a file or stored in a variable:

 a="$(xxd -l "$n" -p /dev/urandom)"

And you could get the original bytes using xxd, as simple as:

## Warning, this produces binary values

echo "$a" | xxd -r -p      # If the Hex digits are in a variable.
xxd -r -p "$rndfile"       # If the Hex digits are in a file.

If xxd is not available, and assuming the hex digits are in $a.
You could use this bash code:

#!/bin/bash

>"$rndfile"                            # erase the file

for (( i=0; i<${#a}/2; i++ )); do      # do a loop over all byte values.

    # convert 2 hex digits to the byte unsigned value:
    printf '%b' $(printf '\\0%o' "0x${a:$i*2:2}") >> "$rndfile"

    b="${b#??}"  # chop out two hexadecimal digits.

done

Or you may try this sh compatible code (should run in bash comfortably). Yes it should be sh compatible. Report if you find problems.

Only tested in dash (but should run in some others).

#!/bin/sh

i=$((${#a}/2))          # The (length of $a ) / 2 is the number of bytes.
b="$a"                  # use a temporal variable. It will be erased.
: >"$rndfile"           # erase file contents

while [ $i != 0 ]; do

    # Write one byte transformed to binary:
    printf '%b' $(printf '\\0%o' "0x${b%"${b#??}"}") >> "$rndfile"

    b="${b#??}"  # chop out two hexadecimal digits.
    i=$((i-1))   # One byte less to finish.

done
0
</dev/random \
dd bs="${num_len}" count=1 | LC_ALL=C \
tr -c 0-9 "$(printf '[%d*24]' 9 8 7 6 5 4 3 2 1)[0*]"

that ought to work well for base 10 and wastes nothing, though the digit distribution is very slightly favored for 0.

with Solaris v<11 you'll want to use ` backticks rather than $(...) for the command substitution.

mikeserv
  • 58,310
  • You could remove the last 6 possible values to get 250 possible values, divisible by ten. Then, the distribution is fully uniform. Use an aditional tr filter : tr -d '\372-\377' . –  Nov 25 '15 at 19:49
  • @BinaryZebra - i don't understand what you mean about entropy, really, but i'm no crytpographer. i am aware of the distribution issue, which is why i mentioned it. and no, my tr cannot be shortened in that way. i have written other answers on this website to similar purpose. this is far better than what i generally see people do, which is tr -dc 0-9 </dev/random | dd bs=... count=1 which is horribly wasteful. i know that i use all of the bytes, but i don't know much about the rate of universal decay. – mikeserv Nov 26 '15 at 00:47
  • @BinaryZebra - openssl is not anything like an efficient random number generator. and yes, it is far better than what i generally see people do, the most common example of which i've already given. almost every answer at your link is using bash's $RANDOM anyway, which is actually a derivation of $$ and $SECONDS. – mikeserv Nov 26 '15 at 04:28
  • Of the 8 answers at the (piked at random) link, only 2 use $RANDOM. One is just as a second option for the users, the other has 0 votes. That's 1 in 8. So, your comment is very incorrect. .... .... .... If you need the detail of counting, here it is: echo -e "\n\nRamesh\t\t10\ openssl\ urandom \nl0b0\t\t5\ random \njimmij\t\t5\ rand48 \nMalteSkoruppa\t4\ urandom\ \$RANDOM \nJ.F.Sebastian\t4\ python.random\ \ \nterdon\t\t3\ perl.rand\ \ \nFalco\t\t2\ -\ \ \nAdrian\t\t\0\ \$RANDOM" –  Nov 26 '15 at 20:56
  • @BinaryZebra - ok, the very large majority of the text in the answers there concerned $RANDOM. and i still don't get what the problem is with converting a random occurrence in input across a fixed range of distribution. how is that a bad thing? every byte is used, and when you add one to the string you don't add one you append to a string which will be interpreted as a number. this doesn't concern itself with byte values, but rather focuses on maximizing the randomness in input occurrence. – mikeserv Nov 26 '15 at 21:41
  • Your (last) question has a simple answer: There are 256 different values in a byte (used as one character in your code). Each one is transformed to a 0-9 number, so each becomes only 10 different values. The 256 values fit in 8 bits (of course). The 10 values fit in (about) 3.32192 bits (in average). Then the math is simple: 256 * 8 compared to 256 * 3.32192. The only tricky part is: Where you get 3.32192, please?. That, conceptually is also simple: the log base 2 of the number of options. That could be calculated as: echo "l(10)/l(2)"| bc -l". Sorry, end of comment space !! :-). –  Nov 26 '15 at 22:38
  • About $RANDOM: Really? ...... Please, move on. Nothing important to discuss. –  Nov 26 '15 at 22:40
  • Maybe I was no perfectly crystal clear: The 256 * 8 math is done assuming a count of 256 different bytes. That actualy becomes 256 decimal digits in your code. Let's leave out the 6 that are repetitions of zero value just to make the explanation as simple as it could be. Including that fact reduces (a little bit more) the entropy. But the reason is a little more complex. After you understand the comment above, If you want to, I could also explain that detail. –  Nov 26 '15 at 22:56
  • @BinaryZebra - well, im probably just not going to get it. as i see it, 1 byte ~ 0-9, 2 bytes ~ 0-99, 3 bytes ~ 0-999,..., and that seems to exponentially increase the resulting value's randomness for each power of ten in a string len, and is at least expressive as the targeted output base will allow. – mikeserv Nov 26 '15 at 23:39
  • Yes, that's correct and valid. The core of the question is not how you join several things, it is how many bits you need to represent them. 0-9 need ~3.32, 0-99 need twice that: ~2*3.32 (or 6.64) so on and so forth. –  Nov 27 '15 at 00:11
  • @BinaryZebra - i'm sorry, but i don't get it. join several what things? this just writes a random number, as requested. – mikeserv Nov 27 '15 at 00:24
  • How many bits you need to represent the maximum random number (the thing) that your code produce. Choose an example, like 9999. Divide that number of bits by the number of digits (4 in this example). What number do you get?. Probably this needs to be moved to chat ..... –  Nov 27 '15 at 00:39
  • @BinaryZebra - so why not move it to chat? and i don't know why youre asking how many bits - you keep talking about bits, not i, and ive already told you i dont get why. – mikeserv Nov 27 '15 at 00:40