1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.
Dismiss Notice

Welcome To SNBForums

SNBForums is a community for anyone who wants to learn about or discuss the latest in wireless routers, network storage and the ins and outs of building and maintaining a small network.

If you'd like to post a question, simply register and have at it!

While you're at it, please check out SmallNetBuilder for product reviews and our famous Router Charts, Ranker and plenty more!

Script for creating random numbers, and more

Discussion in 'Asuswrt-Merlin' started by smasher, Nov 18, 2019.

  1. smasher

    smasher Regular Contributor

    Joined:
    Jun 21, 2019
    Messages:
    83
    Script for creating random numbers, hex strings, octal strings, random passwords...

    This is one of those scripts I should have written a long time ago.
    • Need a 32-character hex string: randoms -f 32
    • Need a 32-character hex string with upper-case letters: randoms -F 32
    • Need 50 random decimal numbers, 8 digits each: randoms 8 50
    • Need a 12 character random password: randoms -p 12
    This can be used to print arbitrarily long random strings, and/or an arbitrary number of random strings.
    Code:
    #!/bin/sh
    
    ## atom's "randoms" script
    ## v1.01, 18 Nov 2019, (c) atom(@)smasher.org
    ## v1.01a, 19 Nov 2019, (c) atom(@)smasher.org
    ## Distributed under the GNU General Public License
    ## http://www.gnu.org/copyleft/gpl.html
    ## originally posted - https://www.snbforums.com/threads/script-for-creating-random-numbers-and-more.60182/
    
    ## test for output format option
    ## thanks Dabombber for this section of code :)
    case "$1" in
        '-f')
        ## this gives hexadecimal output
        chars_match='0-9a-f'
        shift
        ;;
        '-F')
        ## this gives hexadecimal output, upper-case letters
        chars_match='0-9A-F'
        shift
        ;;
        '-8')
        ## this gives octal output
        chars_match='0-7'
        shift
        ;;
        '-p')
        ## random passwords
        chars_match='0-9a-zA-Z<>/\[email protected]#$%^&*()+=_,.-'
        ## for random passwords, edit the character-set as desired
        ## nb, '!-~' include all "normal" "printable" ASCII characters
        shift
        ;;
        *)
        ## default is decimal output
        chars_match='0-9'
        ;;
    esac
    
    #### 1st argument
    ## $1 is number of digits output per line.
    ## 18 is a default, because that's what usually what expr can safely handle.
    ## this makes it easy to use this script for deriving random numbers within
    ##     a given range, eg for getting random numbers between 1-37, inclusive:
    ##     expr $( randoms ) % 37 + 1
    digits_output=${1:-18}
    
    #### 2nd argument
    ## $2 is number of output lines
    ## defaults to 1
    lines_output=${2:-1}
    
    ## this displays on "-h", "--help", or any improper use
    help () {
        echo "$0: usage:"
        echo "    $0 [-f|-F|-8|-p] [d [l]]"
        echo
        echo '    -f: hexadecimal output'
        echo '    -F: hexadecimal output, with upper-case letters'
        echo '    -8: octal output'
        echo '    -p: random passwords'
        echo '    d: number of digits (or characters) output per line'
        echo '    l: number of output lines'
        echo '    ** Default output is decimal format, 18 digits, 1 line'
        echo
        echo 'Examples:'
        echo '* Print 5 random decimal strings, each with 3 digits:'
        echo "    $0 3 5"
        echo
        echo '* Print 1 random hexadecimal string with 32 digits:'
        echo "    $0 -f 32"
        echo
        echo '* Print 1 random decimal string with 300 digits:'
        echo "    $0 300"
        echo
        echo '* Print 1 random password string with 12 characters:'
        echo "    $0 -p 12"
        echo
        exit 1
    }
    
    ## test that numeric arguments are sensible
    ## or print help (to stderr) and exit (exit status 1)
    [ 0 -lt ${digits_output} ] 2> /dev/random || help 1>&2
    [ 0 -lt ${lines_output}  ] 2> /dev/random || help 1>&2
    
    n=1
    ## go through this loop once per line of output
    while [ ${lines_output} -ge ${n} ]
    do
        ## this gives output strings of arbitrary length
        tr -cd "${chars_match}" < /dev/urandom | head -c ${digits_output}
        echo
        n=$(( $n + 1 ))
    done
    
    exit 0
    
    ####################################
    ## NOTE THE "exit" DIRECTLY ABOVE ##
    ## WHAT'S BELOW HERE WILL NOT RUN ##
    ####################################
    
    n=1
    ## for systems with wonky /dev/urandom, this (below) may be more robust.
    ## it's actually concerning how often "openssl enc -rc4" does better
    ##    than /dev/urandom, according to dieharder, although there are still
    ##    advantages to using /dev/urandom (such as portability and periodic reseeding)
    ## this is included here for reference and experimentation. have fun with it.
    ### rc4 is fast and does a good job passing the dieharder tests.
    ### "tail -c +17" strips out the "Salted__xxxxxxxx" string.
    ### "2> /dev/random" pipes openssl's errors into /dev/random; use /dev/null if needed.
    ### "head -c 16 /dev/urandom" derives a 128 bit password, read by "-pass stdin".
    head -c 16 /dev/urandom | openssl enc -rc4 -pass stdin -in /dev/zero 2> /dev/random | tail -c +17 | while :
    do
        ## go through this loop once per line of output
        ## this gives output strings of arbitrary length
        tr -cd "${chars_match}" | head -c ${digits_output}
        echo
        n=$(( $n + 1 ))
        [ ${lines_output} -ge ${n} ] || exit 0
    done
    
    
    nb the RT-AC86U (Merlin 384.13) does not have rc4. If you want to use that part of the script, use rc5 instead. As it is, it runs perfectly well on the AC68U, and it's also great on other Linux/BSD/unix machines.
     
    Last edited: Nov 19, 2019
    Dabombber, L&LD and oso2276 like this.
  2. smasher

    smasher Regular Contributor

    Joined:
    Jun 21, 2019
    Messages:
    83
    To modify this for passwords (or other strings) that include all printable ASCII characters, set "chars_match='!-~'", which is equivalent to:
    Code:
     tr -cd '!-~' < /dev/urandom
    
     
    L&LD likes this.
  3. Dabombber

    Dabombber Regular Contributor

    Joined:
    Apr 29, 2016
    Messages:
    117
    Nice. A while back when I needed a random hex string I ran into a bunch of linux/asuswrt oddities, this would have been handy then.

    A couple of things, the hex variable seems like a pointless intermediary step when you could set the characters directly, something like
    Code:
    case "$1" in
        '-f')
            ## this gives hexadecimal output
            chars_match='0-9a-f'
            shift
        ;;
        '-F')
            ## this gives hexadecimal output, upper-case letters
            chars_match='0-9A-F'
            shift
        ;;
        '-8')
            ## this gives octal output
            chars_match='0-7'
            shift
        ;;
        '-p')
            ## random passwords
            chars_match='0-9a-zA-Z<>/\[email protected]#$%^&*()+=_,.-'
            shift
        ;;
        *)
            ## default is decimal output
            chars_match='0-9'
        ;;
    esac

    Should [[d] l] be [d [l]]? Since the digits can be set without lines but not vice versa (https://en.wikipedia.org/wiki/Usage_message#Examples).

    I think printable characters also includes space and the class without the space is graphable characters, but that's just semantics. It's a shame asuswrt's tr isn't compiled with character classes enabled, but even then busybox's implementation seems to ignore [: print:] and [:graph:].
     
    L&LD and smasher like this.
  4. smasher

    smasher Regular Contributor

    Joined:
    Jun 21, 2019
    Messages:
    83
    Cool! Updated! Thanks for that! I was thinking about either a case statement or getopts, but then I forgot about it.

    Good catch! Got that updated, too!

    Yeah, I was pondering that... I was thinking of "printable" in the sense that it consumes ink, on a page. I did make some updates to the comments about that.

    Weird that "space" (0x20) is technically a "printable character", but "horizontal tab" (0x09) is technically a "control character". In any case, anyone can edit that to include spaces, tabs, or even escapes, deletes, backspaces, and other characters that may not be intuitive to type into a web-site login page ;)

    https://en.wikipedia.org/wiki/ASCII#Printable_characters

    Anyway, spaces are a part of strong passwords, but those are different types of passwords - https://xkcd.com/936/ - I do have a script that makes those types of passwords, but it relies on dictionary files, so it's not feasible for a typical busybox device.

    Yeah, that would be convenient, but we can do it the hard way.

    Thanks :)
     
    L&LD likes this.