Thursday, December 15, 2016

Command-line password generator for Linux

Linux has its own cryptographic generator which can easily be harnessed by the common user to generate extremely strong passwords. This password generator can be found at /dev/random. /dev/random generates pseudorandom numbers based on the available entropy of the system (see wiki page). A derivate of /dev/random called /dev/urandom does the same thing but it is more accessible while being slightly (theoretically) less secure. So what stops us from grabbing outout from /dev/urandom using cat? An output of the content of /dev/urandom will demonstrate that it is practically gibberish with very few usable random characters. The output of

cat /dev/urandom 

should convince the user.  However, it is possible to trim out the unwanted characters and only keep alphabets, numbers, and special characters with a simple filtering by the tr command.


cat /dev/urandom | head -c 10 | tr -dc "A-Za-z0-9_!"

This command will produce the desired output but it will generate very small passwords, which are truly random but which do not meet the length criteria in most cases. Therefore, this procedure needs to be repeated a large number of times until the required length of random passwords can be generated. It is better to write a bash script which will automate this procedure.

The following bash script generates random passwords and allows the user to control the length and number of random passwords that are generated. It also allows special characters to be included or excluded with a switch (argument). Since I am always in a hurry and don't like passing too many arguments, I wrote the script so that it can accept arguments in a shorthand manner in addition to the longhand manner (Unix style) of passing arguments. Shorthand way: ./randompass.sh 10 2 1 will generate 2 random passwords of length 10 and they will all include special characters.


~$ ./randompass.sh 10 2 1

ST5%l36HpG
4Tvq%Ia1UZ

Or I could use the longhand (Unix-style) way of passing the arguments: ./randompass.sh -l 10 -n 2 -s 1


~$ ./randompass.sh -l 10 -n 2 -s 1

IofK1_qA%l
%5aw_YY1XZ


The result will be the same. I have included the full code below which is well commented. I also provide explanation and cite sources below the code.


#!/bin/bash

# generate random passwords using urandom CSPRNG.  

# randompass.sh v5.2 by Jones

# ---- SET INITIAL VALUES ----
length=16
num=4
special=1
usage="
Usage: ./randompass.sh [options ...]
  -l password length. DEFAULT=16
  -n number of passwords to generate. DEFAULT=4
  -s special characters. DEFAULT=1. Set to 0 to disable selecting _!%
  -h help
  
Example 1: ./randompass.sh -l 8 -n 2 -s 0
  
OR

./randompass.sh [password length] [number of passwords to generate] [include special characters] 

Example 2: ./randompass.sh 8 2 0   "

if [[ "$1" =~ ^[0-9]+$ ]]; then  
 # ---- SET INITIAL VALUES ----
 length=$1
 num=$2
 special=$3

 # Second and third argument might be empty which means $num and $special
 # will be empty at this point too. Set the values of $num and $special 
 #to the default values in case they are empty
 [ "$2" == "" ] && num=4
 [ "$3" == "" ] && special=1
else
 # If user uses arguments then use getopts.
 while getopts l:n:s:h option
 do
  case "${option}"
  in
          l) length=${OPTARG};;
          n) num=${OPTARG};;
          s) special=${OPTARG};;
          h) echo "$usage" ;;
  esac
 done
fi

# ---- CHECK ARG VALIDITY ----

# check if the first argument is an integer. If it is not then throw error. 
#=~ matches REGEXP ^[0-9]+$ which is one or more occurences of the numbers 0-9
if [[ !("$length" =~ ^[0-9]+$ )]]; then
 echo "Enter numbers only for the password length"
 echo "$usage"
 exit
fi

# check if the second argument is an integer. If it is not then throw error.
if [[ !("$num" =~ ^[0-9]+$) ]]; then
 echo "Enter numbers only for the number of passwords to generate"
 echo "$usage"
 exit
fi

# check if the third argument is an integer. If it is not then throw error. 
if [[ !("$special" =~ ^[0-1]+$) ]]; then
 echo "Enter only 0 or 1 for the third argument."
 echo "$usage"
 exit
fi


# ---- GENERATE THE PASSWORDS ----
if [[ $special == "1" ]]; then # if special chars needed: DEFAULT
 i="0"
# echo "first loop for 1"
 while [ $i -lt $num ] 
 # Keep looping till next password, which satisfies the required criteria, 
 # is successfully generated. May require many more passess for stricter 
 # requirements, usually smaller length passwords.
 do 
 pass=`tr -dc "A-Za-z0-9_!%" < /dev/urandom | head -c ${length} | xargs`

 if [ `echo $pass | grep "%" | grep "[0-9]" ` ]; then
   echo $pass
   i=$[$i+1]
 fi
 done
else  # if special chars are NOT needed
 i="0"
# echo "second loop for 0"
 while [ $i -lt $num ]
 do 
 pass=`tr -dc "A-Za-z0-9" < /dev/urandom | head -c ${length} | xargs`

 if [ `echo $pass | grep "[0-9]" ` ]; then
   echo $pass
   i=$[$i+1]
 fi
 done
fi



 

Parsing the command-line

The block of code which parses the command-line options using getopts is found at lifewire. The post at that link provides a clear explanation of what the block of code while getopts l:n:s:h option does. So I will only mention that the while loop parses through the command-line arguments and if it finds any matches to the options that the script expects, then it assigns the value passed to the option to the corresponding variables, defined in the script.

Checking validity of arguments passed

Since the script can only accept numeric arguments, their values need to be checked for non-numeric entries. If found an error message is displayed. To check if the values are numeric we can treat the value as a string and verify that every character lies between 0-9. This ensures that there are no non-numeric characters in that string which in turn implies that the string is a number. "$num" =~ ^[0-9]+$  does exactly that. =~ operator is used for REGEX matching of lvalue $num to rvalue ^[0-9]+$ (which is a REGEX that matches every character to characters between 0-9). A similar test is applied for the special variable which acts as a switch to include or exclude special characters. However, it only takes values 0 or 1. Hence its REGEX is ^0-1+$. For more information on regular expressions see this quick reference.




OK GOOGLE on Samsung Galaxy S7 doesn’t work

To make Ok Google detection work on Galaxy S7 (Galaxy series phones) we need to perform a couple of steps. 1. As long as Samsung S vo...