Skip to content

Instantly share code, notes, and snippets.

@kou1okada
Last active February 7, 2025 02:36
Show Gist options
  • Save kou1okada/4b32a367395cffedeb20415cc931479e to your computer and use it in GitHub Desktop.
Save kou1okada/4b32a367395cffedeb20415cc931479e to your computer and use it in GitHub Desktop.
getpw.bash - Get password with masked echo back.

getpw.bash - Get password with masked echo back

Examples

Call as a command:

: ${PASSWORD:=$(getpw.bash)}

Call as a function:

source "SOMEWHERE/getpw.bash"
: ${PASSWORD:=$(getpw)}

Pwned Passwords searching:

pwned.bash

References

#!/usr/bin/env bash
#
# getpw.bash - GET PassWord
# Copyright (c) 2018-2025 Koichi OKADA. All right reserved.
# This script distributed under the MIT license.
#
function __getpw_usage () # [<NAME>]
# Generate usage from comment.
#Args:
# <NAME>
# The name of the function to generate usage.
# Default: XXXXX (part of this function name like __XXXXX_usage).
{
local NAME="${1:-$(sed -E 's/__(\S*)_usage/\1/g' <<<"$FUNCNAME")}"
local SOURCE
for SOURCE in "${BASH_SOURCE[@]}" ""; do
grep -E "^function\\s*($NAME)\\s*\\(\\)\\s*#\\s*(.*)" "$SOURCE" >&/dev/null && break
done
[ -z "$SOURCE" ] && return
gawk -vNAME="$NAME" ${AWK:+}'
phase == 1 {
if (match($0,"^#(.*)",m)) {
lines[n++] = m[1];
} else {
phase = 2;
}
}
phase == 0 && match($0,"^function\\s*("NAME")\\s*\\(\\)\\s*#\\s*(.*)",m) {
lines[n++] = "Usage: "m[1](m[2]==""?"":" "m[2]);
phase = 1;
}
END {
for (i in lines) {
print lines[i];
}
}
' "$SOURCE"
gawk -vNAME="$NAME" ${AWK:+}'
phase == 2 {
if (match($0, "^\\s*#(.*)",m)) {
lines[n++] = m[1];
} else {
phase = 1;
}
}
phase == 1 && match($0,"^\\s*(\\S+)\\)\\s*#\\s*(.*)",m) {
lines[n++] = gensub("\\|", ", ", "g", m[1])(m[2]==""?"":" "m[2]);
phase = 2;
}
phase == 0 && match($0,"^function\\s*__"NAME"_optparse\\s*\\(\\)",m) {
phase = 1;
}
0 < phase && match($0, "^}") {
phase = 0;
}
END {
if (n) print "Options:"
for (i in lines) {
print " "lines[i];
}
}
' "$SOURCE"
}
function __getpw_optparse () # [<ARGS> ...]
{
while (( 0 < $#)); do
case "$1" in
-h|--help)#
# show this help.
OPTS["help"]="$1"
shift
;;
-n)#
# do not append a newline.
OPTS["n"]="$1"
shift
;;
--)
shift
ARGS+=( "$@" )
shift $#
;;
-*)
printf "\e[31;1mError:\e[0m Unknown option: %s\n" "$1"
return 2
;;
*)
ARGS+=( "$@" )
shift
;;
esac
done
}
function getpw () # [OPTIONS] [PROMPT]
# Get password with masked echo back.
{
local -a ARGS
local -Ag OPTS
__getpw_optparse "$@" || return
[ -n "${OPTS["help"]}" ] && { __getpw_usage; return; }
local prompt="${ARGS[0]:-Password: }"
local password=""
local ch
while IFS="" read -p "$prompt" -rsn1 ch; do
case "$ch" in
"")
break
;;
$'\x15')
prompt="${password//?/$'\b \b'}"
password=""
;;
$'\x08'|$'\x7f')
[ -n "$password" ] && prompt=$'\b \b' || prompt=""
password="${password%?}"
;;
*)
prompt="*"
password+="$ch"
;;
esac
done
echo >/dev/tty
echo ${OPTS["n"]:+"${OPTS["n"]}" }"$password"
}
[ "$(readlink -f -- "$0")" = "$(readlink -f -- "$BASH_SOURCE")" ] && getpw "$@"
#!/usr/bin/env bash
#
# pwned.sh - Have I Been Pwned?
# Copyright (c) 2025 Koichi OKADA. All right reserved.
# This script distributed under the MIT license.
#
SCRIPT_PATH="$0"
SCRIPT_FILE="${SCRIPT_PATH##*/}"
SCRIPT_NAME="${SCRIPT_FILE%.*}"
SCRIPT_DIR="${SCRIPT_PATH%/*}"
SCRIPT_REALPATH="$(readlink -f "$SCRIPT_PATH")"
SCRIPT_REALFILE="${SCRIPT_REALPATH##*/}"
SCRIPT_REALNAME="${SCRIPT_REALFILE%.*}"
SCRIPT_REALDIR="${SCRIPT_REALPATH%/*}"
function __pwned_optparse () # [<ARGS> ...]
{
while (( 0 < $#)); do
case "$1" in
-h|--help)#
# show this help.
OPTS["help"]="$1"
shift
;;
--)
shift
ARGS+=( "$@" )
shift $#
;;
-*)
printf "\e[31;1mError:\e[0m Unknown option: %s\n" "$1"
return 2
;;
*)
ARGS+=( "$@" )
shift
;;
esac
done
}
function pwned () # [<OPTIONS> ...] [<PASSWORD> ...]
# Pwned Passwords searching
# Secure retrieval with k-Anonymity model to keep passwords secret.
# For details, see Have I Been Pwned: API v3.
# https://haveibeenpwned.com/API/v3#SearchingPwnedPasswordsByRange
#Arguments:
# <PASSWORD>
# Password for checking by have i been pwned?.
# If stdin is redirect or pipe,
# read passwords separated by line break.
{
local -a ARGS
local -Ag OPTS
__pwned_optparse "$@" || return
[ -n "${OPTS["help"]}" ] && { __getpw_usage "$FUNCNAME"; return; }
if test -t 0; then # tty
(( ${#ARGS[@]} <= 0 )) \
&& { read < <("$SCRIPT_REALDIR/getpw.bash"); ARGS+=( "$REPLY" ); }
else # redirect or pipe
readarray -tO"${#ARGS[@]}" ARGS
fi
local pw result results
for pw in "${ARGS[@]}"; do
read -n 40 hash < <(sha1sum < <(echo -n "$pw") )
prefix="${hash:0:5}"
suffix="${hash:5:35}"
readarray -t result < <(
curl -s "https://api.pwnedpasswords.com/range/$prefix" \
| grep -i "$suffix"
)
[ -n "$result" ] && printf "$prefix%s\n" "${result[@]}"
results+=( "${result[@]}" )
done
(( 0 < ${#results[@]} ))
}
source "$SCRIPT_REALDIR/getpw.bash"
pwned "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment