#!/usr/bin/env bash # # gen-passwd.sh - a simple password generator. # # (C) Bruno Raoult ("br"), 2022 # Licensed under the GNU General Public License v3.0 or later. # Some rights reserved. See COPYING. # # You should have received a copy of the GNU General Public License along with this # program. If not, see . # # SPDX-License-Identifier: GPL-3.0-or-later # #%MAN_BEGIN% # NAME # gen-passwwd.sh - a simple password generator. # # SYNOPSIS # gen-passwd.sh [OPTIONS] TYPE [LENGTH] # # DESCRIPTION # Generate a random TYPE password with length LENGTH. # Available types are : # dice # A list of digits in range [1-6]. Default length is 5. The purpose of # this is only to help choosing a word in a diceware word list. # mac # A "xx-xx-xx-xx-xx-xx" type address, where 'x' are hexadecimal digits # (ranges 0-9 and a-h). # Length is the number of "bytes" (groups od 2 hehexademal digits), and # defaults to 6. The default ":" delimiter can be changed with "-s" # option. # This is the default option. # pincode # A numeric password. default LENGTH is 4, with no separator. # passphrase # Generate words from a diceware-like dictionary. Length is the number # of words ans defaults to 6. # # OPTIONS # -c, --copy # Copy password to clipboard. # # -C, --capitalize # For 'passphrase' and 'mac' type only. # Passphrase: Capitalize words (first letter of each word). Recommended # if separator is set to null-string (--separator=0). # Mac: use capital hexadecimal digits. # # -d, --dictionary=file # Use file as wordlist file. Default is # # -g, --gui # Will use a GUI (yad based) to propose the password. This GUI # simply displays the password, allows to copy it to clipboard, # and to re-generate a new password. # # -h, --help # Display usage and exit. # # -m, --man # Print a man-like help and exit. # # -s, --separator=CHAR # CHAR is used as separator when TYPE allows it. Use "0" to remove # separators. # # -v, --verbose # Print messages on what is being done. # # EXAMPLES # TODO # # TODO # Add different languages wordlists. # # AUTHOR # Bruno Raoult. # # SEE ALSO # Pages on Diceware/words lists : # EFF: https://www.eff.org/dice # diceware: https://theworld.com/~reinhold/diceware.html # # #%MAN_END% SCRIPT="$0" # full path to script CMDNAME=${0##*/} # script name SHELLVERSION=$(( BASH_VERSINFO[0] * 10 + BASH_VERSINFO[1] )) # default type, length, separator declare pw_type="mac" declare pw_length=6 declare pw_sep=":" declare pw_cap="" declare pw_dict="" declare pw_copy="" declare pw_gui="" declare pw_verbose="" declare -A pw_commands=() declare -a pw_command=() usage() { printf "usage: %s [-s CHAR][-d DICT][-Ccgmv] [TYPE] [LENGTH]\n" "$CMDNAME" return 0 } man() { sed -n '/^#%MAN_BEGIN%/,/^#%MAN_END%$/{//!s/^#[ ]\{0,1\}//p}' "$SCRIPT" | more } # log() - log messages on stderr # parameters: # -l, -s: long, or short prefix (default: none). Last one is used. # -t: timestamp # -n: no newline # This function accepts either a string, either a format string followed # by arguments : # log -s "%s" "foo" # log -s "foo" log() { local timestr="" prefix="" newline=y todo OPTIND [[ -z $pw_verbose ]] && return 0 while getopts lsnt todo; do case $todo in l) prefix=$(printf "*%.s" {1..30}) ;; s) prefix=$(printf "*%.s" {1..5}) ;; n) newline=n ;; t) timestr=$(date "+%F %T%z ") ;; *) ;; esac done shift $((OPTIND - 1)) [[ $prefix != "" ]] && printf "%s " "$prefix" >&2 [[ $timestr != "" ]] && printf "%s" "$timestr" >&2 # shellcheck disable=SC2059 printf "$@" >&2 [[ $newline = y ]] && printf "\n" >&2 return 0 } # srandom() - use RANDOM to simulate SRANDOM # $1: Reference of variable to hold result # # Note: RANDOM is 15 bits, SRANDOM is 15 bits. # # @return: 0, $1 will contain the 32 bits random number srandom() { local -n _ret=$1 (( _ret = RANDOM << 17 | RANDOM << 2 | RANDOM & 3 )) } # rnd() - get a random number integer # $1: An integer, the modulo value # # @return: 0, output a string with the random integer on stdout. # # This function uses SRANDOM for bash >= 5.1 and srandom() function # above for lower versions. rnd() { local mod=$1 ret if (( SHELLVERSION >= 51 )); then # shellcheck disable=SC2153 (( ret = SRANDOM )) else srandom ret fi printf "%d" "$(( ret % mod ))" } # rnd_hex() - get a random 2-digits hex number # # @return: 0, output a string with the random integer on stdout. rnd_hex() { printf "%02x" "$(rnd 256)" } # rnd_dice() - get a 6 faces 1-6 random number # # @return: 0, output a string {1..6} rnd_dice() { printf "%d" "$(( $(rnd 6) + 1 ))" } # rnd_digit() - get a digit random number # # @return: 0, output a string {0..9} rnd_digit() { printf "%d" "$(( $(rnd 10) ))" } # rnd_word() - get a word from file # $1: The dice rolls # $2: The word list file () # # @return: 0, output a string {0..9} rnd_word() { local roll="$1" file="$2" word="" word=$(sed -n "s/^${roll}[[:blank:]]\+//p" "$file") printf "%s" "$word" } # rnd_charset() - get a random string from a charset # $1: A string with characters to choose from # $2: An integer # # @return: 0, output a random string from charset $1, with length $2. rnd_charset() { local charset="$1" ret="" local -i len=$2 _i for ((_i=0; _i 0)); then # type type=$1 case "$type" in dice) pw_type="dice" tmp_length=5 [[ -z $tmp_sep ]] && tmp_sep=" " ;; mac) pw_type="mac" tmp_length=6 [[ -z $tmp_sep ]] && tmp_sep=":" ;; pincode) pw_type="pincode" tmp_length=4 [[ -z $tmp_sep ]] && tmp_sep="0" ;; passphrase) pw_type="passphrase" tmp_length=6 [[ -z $tmp_dict ]] && tmp_dict="eff_large_wordlist.txt" [[ -z $tmp_sep ]] && tmp_sep=" " [[ -z $tmp_cap ]] && tmp_cap="" ;; *) printf "%s: Unknown '%s' password type.\n" "$CMDNAME" "$type" usage exit 1 esac shift fi if (($# > 0)); then # length if ! [[ $1 =~ ^[0-9]+$ ]]; then printf "%s: Bad '%s' length.\n" "$CMDNAME" "$1" usage exit 1 fi tmp_length="$1" shift fi [[ -n $tmp_length ]] && pw_length=$tmp_length if ! (( pw_length )); then printf "%s: Bad '%d' length.\n" "$CMDNAME" "$tmp_length" usage exit 1 fi [[ -n $tmp_sep ]] && pw_sep=$tmp_sep [[ $pw_sep = "0" ]] && pw_sep="" [[ -n $tmp_cap ]] && pw_cap=$tmp_cap [[ -n $tmp_dict ]] && pw_dict=$tmp_dict } parse_opts "$@" pw_command=("${pw_commands[$pw_type]}" "$pw_length" "$pw_sep" "$pw_cap" "$pw_dict") #printf "command=%d %s\n" "${#pw_command[@]}" "+${pw_command[*]}+" print_command pw_command if [[ -z $pw_gui ]]; then passwd=$("${pw_command[@]}") if [[ -n $pw_copy ]]; then printf "%s" "$passwd" | xsel -bi fi printf "%s\n" "$passwd" else gui_passwd "${pw_command[@]}" fi exit 0 gui_passwd() { local passwd="" res=0 while passwd=$(rnd_mac "$@") yad --title="Password Generator" --text-align=center --text="$passwd" \ --borders=20 --button=gtk-copy:0 --button=gtk-refresh:1 --button=gtk-ok:252 \ --window-icon=dialog-password res=$? printf "res=%d\n" "$res" >& 2 if ((res == 0)); then printf "%s" "$passwd" | xsel -bi fi ((res == 1)) do true; done return $res } for i in {0..10}; do rnd_charset "abcde" done echo exit 0 gui_passwd "$@" exit 0