2

In GENERAL, on Linux, UNIX, BSD, cygwin systems, how can I get random numbers from /dev/urandom inside a given range, example:

217 < X < 34523

OR other example:

36856 < X < 76543 

I am afraid to even try to search for solutions, since this is not just "google and first hit", because it has to be 100% correct from randomness point of view.

I tried to write it my own:

$ cat randomfournumbers.sh
#!/bin/bash

working=true
howmanyneeded=0

while "$working"; do
fivedigit=$(tr -cd "[:digit:]"</dev/urandom|fold -w5|head -1)

        if [ "$fivedigit" -ge 300 ]; then
                if [ "$fivedigit" -le 33000 ]; then

                        echo "$fivedigit"
                        howmanyneeded=$((howmanyneeded+1))

                        if [ "$howmanyneeded" -ge 4 ]; then
                                working=false
                        fi
                fi
        fi
done
$ sh randomfournumbers.sh
11442
26742
13905
23547
$

But afaik cryptography, randomness.. I am sure it contains an error that I cannot see (no problem with urandom, rather the logic).

  • 4
    echo $(( ( RANDOM % (34523-217) ) + 217+1)) – Rui F Ribeiro Dec 30 '17 at 21:10
  • 1
    In general, you use a library. Or start by considering the actual algorithm you need to fill the requirements you have, and only then implement it in some programming language. – ilkkachu Dec 30 '17 at 22:13
  • 1
    I'm voting to close this question as off-topic because it's about mathematics, not Unix, and would be better off on math.SE or crypto.SE. – ilkkachu Dec 30 '17 at 22:14
  • @ilkkachu, the question asks how to obtain specific output from a specific special file (/dev/urandom), on specific Unix-like operating systems or environments ("Linux, UNIX, BSD, cygwin systems"). I am amazed that you regard it as off-topic. –  Jan 06 '18 at 19:26
  • @sampablokuper, urandom gives random byte values. An interface like that is not unique to Unix, other systems have that too, e.g. CryptGenRandom on Windows, RAND_bytes in OpenSSL. All that's Unix-specific here is the name of the interface, and we already know that. After that, it's just mathematics on how to turn random bytes to random numbers in a given range, while getting the required properties (uniform distribution). – ilkkachu Jan 06 '18 at 20:51
  • @ilkkachu, no, it is not "just mathematics". The OP posted a Bash script as a starting point, and is clearly interested in how to use Unix/POSIX/etc tools for the task. Nor is the question asking how to achieve the desired result on Windows with CryptGenRandom as input. –  Jan 07 '18 at 15:35
  • @sampablokuper, if they want "100% correct from randomness point of view" there has to be mathematics involved in finding out what to do. In general, a specification, an algorithm to be implemented. Which in this case is primarily maths. Jumping directly to the implementation is not a way to create correct software. It pretty much works in the cases where any mistakes produce errors that are immediately obvious. Alas, crypto and randomness notoriously aren't like that. – ilkkachu Jan 07 '18 at 15:58
  • @ilkkachu, of course maths is integral; no-one is disputing that. But the OP isn't just asking for a suitable algorithm expressed in the language of mathematics: they appear to be asking for help obtaining a suitable algorithm expressed in the language of Unix/POSIX tools. Asking for, or giving, help obtaining a suitable algorithm for some given problem, expressed in the language of Unix/POSIX tools, is a huge part of the raison d'etre of this site, and as such the OP's question is on-topic here. –  Jan 07 '18 at 16:06

2 Answers2

2

I think shuf would be a better tool for this purpose.

Example:

$ shuf -i 217-34523 -n 1
11623

But if you really want to use /dev/urandom, this should do the work:

random_numbers() {
  a="$1"
  b="$2"
  lim="$3"
  count="0"

  while :; do
    num=$(tr -dc '0-9\n' < /dev/urandom | grep -Pom1 "^\\d{${#a},${#b}}")
    if [ "$num" -ge "$a" ] && [ "$num" -le "$b" ]; then
      echo "$num"
      count="$((count + 1))"
      [ "$count" -ge "$lim" ] && break
    fi
  done
}

Example:

$ random_numbers 36856 76543 5
75544
55383
43024
72678
63635
nxnev
  • 3,654
  • By default, shuf doesn't use a cryptographically secure pseudo-random number generator, but it's possible to use shuf with /dev/urandom (--random-source option). Related: https://unix.stackexchange.com/a/705633/133353 (By default these commands use an internal pseudo-random generator initialized...) – Artfaith Jun 10 '22 at 04:47
  • No comment on the rest of the script, but to get N random digits you should use (instead of grep) something like: num=$(tr -dc 0-9 < /dev/urandom | head -c "${#a}"). Alternatively dd bs=N count=1 or fold -bw N | head -n 1 can also read N bytes, and are both POSIX syntax (unlike head -c). Note the tr list is 0-9 - no line feeds. – dan Oct 04 '22 at 04:10
0

This script does the job:

#!/bin/bash

log2x(){
    local bytes=0 t=$1
    while ((t>0)); do
    ((t=t>>8,bytes++))
    done
    echo "$bytes"
}

mkrandom(){
    while :; do
    hexrandom=$(dd if=/dev/urandom bs=1 count=$bytes 2>/dev/null | xxd -p)
    (( 16#$hexrandom < range*mult )) && break
    done
    echo "$(( (16#$hexrandom%range)+min ))"
}

if (($#==3)); then
    min=$2 max=$(($3+1))
else
    min=217 max=34523
fi
range=$((max-min+1))

bytes=$(log2x "$range")
maxvalue=$(((1<<($bytes*8))-1))
mult=$((maxvalue/range))
#printf "maxvalue=%d mult=%d range=%d\n" "$maxvalue" "$mult" "$range"


while ((i++<$1)); do
    mkrandom 
done

Call as ./script count min max:

./script 5 23 323
261
319
189
204
93