diff --git a/profile.d/pwd.sh b/profile.d/pwd.sh index fba6b2a..7b52b48 100644 --- a/profile.d/pwd.sh +++ b/profile.d/pwd.sh @@ -273,43 +273,67 @@ export -f genpwd pwdscore() { local verbose=0 - local PARSED + local read_stdin=0 + local password="" - PARSED=$(getopt -o hv --long help,verbose -n 'pwdscore' -- "$@") - if [[ $? -ne 0 ]]; then return 1; fi - eval set -- "$PARSED" - - while true; do + while [[ $# -gt 0 ]]; do case "$1" in -h|--help) printf "pwdscore: Score a password from 1 to 100.\n\n" - printf "Usage: pwdscore [options] \n\n" + printf "Usage: pwdscore [options] \n" + printf " pwdscore [options] --stdin\n" + printf " pwdscore [options] # prompt on terminal\n\n" printf "Options:\n" printf "\t-h, --help\t\tDisplay this help screen\n" printf "\t-v, --verbose\t\tShow details about the computed score\n" + printf "\t-i, --stdin\t\tRead the password from standard input\n\n" + printf "Note:\n" + printf " Passwords containing '!' should be quoted, or passed via --stdin.\n" return 0 ;; -v|--verbose) verbose=1 shift ;; + -i|--stdin) + read_stdin=1 + shift + ;; --) shift break ;; - *) - disp E "Invalid options, use \"pwdscore --help\" to display usage." + -*) + disp E "Invalid option '$1'. Use \"pwdscore --help\" to display usage." return 1 ;; + *) + break + ;; esac done - [[ $# -ne 1 ]] && { - disp E "Please provide exactly one password to score." - return 1 - } + if (( read_stdin )); then + [[ $# -eq 0 ]] || { + disp E "Do not pass a positional password when using --stdin." + return 1 + } + IFS= read -r password || true + elif [[ $# -eq 0 ]]; then + if [[ -t 0 ]]; then + read -r -s -p 'Password: ' password < /dev/tty || true + printf '\n' > /dev/tty + else + IFS= read -r password || true + fi + else + [[ $# -eq 1 ]] || { + disp E "Please provide exactly one password to score." + return 1 + } + password="$1" + fi - local password="$1" local lower=${password,,} local length=${#password} local score=0 @@ -319,6 +343,9 @@ pwdscore() local c1=0 c2=0 c3=0 local ch="" local has_lower=0 has_upper=0 has_digit=0 has_symbol=0 + local pool_size=0 + local entropy_bits="0.0" + local entropy_score=0 local -A seen=() if [[ -z $password ]]; then @@ -338,29 +365,66 @@ pwdscore() score=$(( length * 2 )) fi - [[ $password =~ [a-z] ]] && { has_lower=1; ((score += 12)); } - [[ $password =~ [A-Z] ]] && { has_upper=1; ((score += 12)); } - [[ $password =~ [0-9] ]] && { has_digit=1; ((score += 12)); } - [[ $password =~ [^[:alnum:]] ]] && { has_symbol=1; ((score += 14)); } + if [[ $password =~ [a-z] ]]; then + has_lower=1 + pool_size=$(( pool_size + 26 )) + score=$(( score + 12 )) + fi + if [[ $password =~ [A-Z] ]]; then + has_upper=1 + pool_size=$(( pool_size + 26 )) + score=$(( score + 12 )) + fi + if [[ $password =~ [0-9] ]]; then + has_digit=1 + pool_size=$(( pool_size + 10 )) + score=$(( score + 12 )) + fi + if [[ $password =~ [^[:alnum:]] ]]; then + has_symbol=1 + pool_size=$(( pool_size + 33 )) + score=$(( score + 14 )) + fi for (( i=0; i 1 )); then + entropy_bits=$(awk -v len="$length" -v pool="$pool_size" \ + 'BEGIN { printf "%.1f", len * (log(pool) / log(2)) }') + + entropy_score=$(awk -v bits="$entropy_bits" 'BEGIN { + if (bits < 28) print -35; + else if (bits < 36) print -25; + else if (bits < 60) print -10; + else if (bits < 80) print 0; + else if (bits < 100) print 5; + else print 10; + }') + score=$(( score + entropy_score )) + fi if [[ $lower =~ (password|admin|root|qwerty|azerty|welcome|letmein|secret|changeme) ]]; then - ((score -= 25)) + score=$(( score - 25 )) fi if [[ $lower =~ (1234|abcd|qwer|0000|1111|aaaa) ]]; then - ((score -= 15)) + score=$(( score - 15 )) + fi + if [[ $password =~ (.)\1\1 ]]; then + score=$(( score - 10 )) + fi + if (( length < 8 )); then + score=$(( score - 10 )) + fi + if (( unique_count * 2 < length )); then + score=$(( score - 10 )) fi - [[ $password =~ (.)\1\1 ]] && ((score -= 10)) - (( length < 8 )) && ((score -= 10)) - (( unique_count * 2 < length )) && ((score -= 10)) for (( idx=0; idx 100 )) && score=100 + if (( score < 1 )); then + score=1 + elif (( score > 100 )); then + score=100 + fi if (( score >= 90 )); then rating='excellent' @@ -397,6 +464,8 @@ pwdscore() printf 'Digits: %s\n' "$([[ $has_digit -eq 1 ]] && echo yes || echo no)" printf 'Symbols: %s\n' "$([[ $has_symbol -eq 1 ]] && echo yes || echo no)" printf 'Unique chars: %d\n' "$unique_count" + printf 'Entropy: ~%s bits\n' "$entropy_bits" + printf 'Entropy modifier: %+d\n' "$entropy_score" else printf '%d\n' "$score" fi