Improve your password generation script

I created a small password generation script. I'm wondering what improvements could be made to it other than handling input errors, usage information, etc. This is the main functionality that I am interested in improving.

This is what it does (and what I like):

  • Be sure to keep the lowercase characters (L), uppercase characters (U), numbers (N), and characters (S) used in passwords.
  • I would like me to find a new password for legnth 10 for me in no more than two seconds.
  • The variable length of the password string must be used as an argument.
  • Only a password containing at least one L, U, N and S should be accepted.

Here is the code:

#!/bin/bash

PASSWORDLENGTH=$1
RNDSOURCE=/dev/urandom
L="acdefghjkmnpqrtuvwxy"
U="ABDEFGHJLQRTY"
N="012345679"
S="\-/\\)?=+.%#"

until [ $(echo $password | grep [$L] | grep [$U] | grep [$N] | grep -c [$S] ) == 1 ]; do
    password=$(cat $RNDSOURCE | tr -cd "$L$U$N$S" | head -c $PASSWORDLENGTH)
    echo In progress: $password # It simply for debug purposes, ignore it
done
echo Final password: $password

      

My questions:

  • Is there a better way to check if a password is acceptable than the way I do it?
  • How about generating a password?
  • Any improvements to the coding style? (Short variable names are temporary. Although I use uppercase names for "constant" [I know not formally] and lowercase variables for variables. Do you like that?)

Vote for the most improved version. :-)

This was just an exercise for me, mostly for fun and as a learning experience, although I will start using it instead of the KeepassX generation I am using now. It will be interesting to see what improvements and suggestions the more experienced Bashistas bring (I made that word).


I created a little basic script to measure performance: (If anyone finds this funny)

#!/bin/bash

SAMPLES=100
SCALE=3

echo -e "PL\tMax\tMin\tAvg"
for p in $(seq 4 50); do
    bcstr=""; max=-98765; min=98765
    for s in $(seq 1 $SAMPLES); do
        gt=$(\time -f %e ./genpassw.sh $p 2>&1 1>/dev/null)
        bcstr="$gt + $bcstr"
        max=$(echo "if($max < $gt ) $gt else $max" | bc)
        min=$(echo "if($min > $gt ) $gt else $min" | bc)
    done
    bcstr="scale=$SCALE;($bcstr 0)/$SAMPLES"
    avg=$(echo $bcstr | bc)
    echo -e "$p\t$max\t$min\t$avg"
done

      

+2


source to share


3 answers


You are throwing away a bunch of randomness in your input stream. Save these bytes and convert them to your character set. Replace the password = ... operator in your loop as follows:

ALL="$L$U$N$S"
password=$(tr "\000-\377" "$ALL$ALL$ALL$ALL$ALL" < $RNDSOURCE | head -c $PASSWORDLENGTH)

      

Repeating $ ALL means the "map to" setting is> = 255 characters.

I also removed the gratuitous use of cat.



(Edited to clarify that what is shown above is not intended to replace the complete script, just the inner loop.)

Edit: Here's a much faster strategy that isn't called by external programs:

#!/bin/bash

PASSWORDLENGTH=$1
RNDSOURCE=/dev/urandom
L="acdefghjkmnpqrtuvwxy"
U="ABDEFGHJLQRTY"
N="012345679"
# (Use this with tr.)
#S='\-/\\)?=+.%#'
# (Use this for bash.)
S='-/\)?=+.%#'

ALL="$L$U$N$S"

# This function echoes a random index into it argument.
function rndindex() { echo $(($RANDOM % ${#1})); }

# Make sure the password contains at least one of each class.
password="${L:$(rndindex $L):1}${U:$(rndindex $U):1}${N:$(rndindex $N):1}${S:$(rndindex $S):1}"

# Add random other characters to the password until it is the desired length.
while [[ ${#password} -lt $PASSWORDLENGTH ]]
do
  password=$password${ALL:$(rndindex $ALL):1}
done

# Now shuffle it.
chars=$password
password=""
while [[ ${#password} -lt $PASSWORDLENGTH ]]
do
  n=$(rndindex $chars)
  ch=${chars:$n:1}
  password="$password$ch"
  if [[ $n == $(( ${#chars} - 1 )) ]]; then
      chars="${chars:0:$n}"
  elif [[ $n == 0 ]]; then
      chars="${chars:1}"
  else
      chars="${chars:0:$n}${chars:$((n+1))}"
  fi
done
echo $password

      

Timing tests show this is 5 to 20 times faster than the original script and the timing is more predictable from one run to the next.

0


source


you could just use uuidgen

or pwgen

to generate your random passwords, perhaps shuffle a few letters later or something like



0


source


secpwgen

very good (it can also generate easier-to-remember passwords diceware

), but has almost disappeared from the net. I managed to find a copy 1.3 source

and put it on github .

Currently part of Alpine Linux .

0


source







All Articles