From b8009511b32a5d502148ad8a8397c9cfd7c75a40 Mon Sep 17 00:00:00 2001 From: Osamu Matsumoto Date: Mon, 22 Jan 2018 06:23:32 +0900 Subject: Add secret option (#43) * Support insert/append secret directly * Validate issuer and accout name option in secret directly mode * Add test for secret option * Fix up docs --- otp.bash | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 93 insertions(+), 19 deletions(-) (limited to 'otp.bash') diff --git a/otp.bash b/otp.bash index 95f171a..6a28fbd 100755 --- a/otp.bash +++ b/otp.bash @@ -18,6 +18,26 @@ OATH=$(which oathtool) +## source: https://gist.github.com/cdown/1163649 +urlencode() { + local l=${#1} + for (( i = 0 ; i < l ; i++ )); do + local c=${1:i:1} + case "$c" in + [a-zA-Z0-9.~_-]) printf "%c" "$c";; + ' ') printf + ;; + *) printf '%%%.2X' "'$c" + esac + done +} + +urldecode() { + # urldecode + + local url_encoded="${1//+/ }" + printf '%b' "${url_encoded//%/\\x}" +} + # Parse a Key URI per: https://github.com/google/google-authenticator/wiki/Key-Uri-Format # Vars are consumed by caller # shellcheck disable=SC2034 @@ -34,8 +54,8 @@ otp_parse_uri() { otp_type=${BASH_REMATCH[1]} otp_label=${BASH_REMATCH[3]} - otp_accountname=${BASH_REMATCH[6]} - [[ -z $otp_accountname ]] && otp_accountname=${BASH_REMATCH[4]} || otp_issuer=${BASH_REMATCH[4]} + otp_accountname=$(urldecode "${BASH_REMATCH[6]}") + [[ -z $otp_accountname ]] && otp_accountname=$(urldecode "${BASH_REMATCH[4]}") || otp_issuer=$(urldecode "${BASH_REMATCH[4]}") [[ -z $otp_accountname ]] && die "Invalid key URI (missing accountname): $otp_uri" local p=${BASH_REMATCH[7]} @@ -50,7 +70,7 @@ otp_parse_uri() { algorithm) otp_algorithm=${BASH_REMATCH[2]} ;; period) otp_period=${BASH_REMATCH[2]} ;; counter) otp_counter=${BASH_REMATCH[2]} ;; - issuer) otp_issuer=${BASH_REMATCH[2]} ;; + issuer) otp_issuer=$(urldecode "${BASH_REMATCH[2]}") ;; *) ;; esac fi @@ -82,6 +102,29 @@ otp_read_uri() { otp_parse_uri "$uri" } +otp_read_secret() { + local uri prompt="$1" echo="$2" issuer accountname + issuer="$(urlencode "$3")" + accountname="$(urlencode "$4")" + + if [[ -t 0 ]]; then + if [[ $echo -eq 0 ]]; then + read -r -p "Enter secret for $prompt: " -s secret || exit 1 + echo + read -r -p "Retype secret for $prompt: " -s secret_again || exit 1 + echo + [[ "$secret" == "$secret_again" ]] || die "Error: the entered secrets do not match." + else + read -r -p "Enter secret for $prompt: " -e secret + fi + else + read -r secret + fi + + uri="otpauth://totp/$issuer:$accountname?secret=$secret&issuer=$issuer" + otp_parse_uri "$uri" +} + otp_insert() { local path="$1" passfile="$2" contents="$3" message="$4" @@ -104,15 +147,30 @@ Usage: Generate an OTP code and optionally put it on the clipboard. If put on the clipboard, it will be cleared in $CLIP_TIME seconds. - $PROGRAM otp insert [--force,-f] [--echo,-e] [pass-name] - Prompt for and insert a new OTP key URI. If pass-name is not supplied, - use the URI label. Optionally, echo the input. Prompt before overwriting - existing password unless forced. This command accepts input from stdin. + $PROGRAM otp insert [--force,-f] [--echo,-e] + [[--secret, -s] [--issuer,-i issuer] [--account,-a account]] + [pass-name] + Prompt for and insert a new OTP key. + + If 'secret' is specified, prompt for the OTP secret, assuming SHA1 + algorithm, 30-second period, and 6 OTP digits; one of 'issuer' or + 'account' is also required. Otherwise, prompt for a key URI; if + 'pass-name' is not supplied, use the URI label. + + Optionally, echo the input. Prompt before overwriting existing URI + unless forced. This command accepts input from stdin. - $PROGRAM otp append [--force,-f] [--echo,-e] pass-name - Appends an OTP key URI to an existing password file. Optionally, echo - the input. Prompt before overwriting an existing URI unless forced. This - command accepts input from stdin. + $PROGRAM otp append [--force,-f] [--echo,-e] + [[--secret, -s] [--issuer,-i issuer] [--account,-a account]] + pass-name + Appends an OTP key URI to an existing password file. + + If 'secret' is specified, prompt for the OTP secret, assuming SHA1 + algorithm, 30-second period, and 6 OTP digits; one of 'issuer' or + 'account' is also required. Otherwise, prompt for a key URI. + + Optionally, echo the input. Prompt before overwriting an existing URI + unless forced. This command accepts input from stdin. $PROGRAM otp uri [--clip,-c] [--qrcode,-q] pass-name Display the key URI stored in pass-name. Optionally, put it on the @@ -127,17 +185,20 @@ _EOF } cmd_otp_insert() { - local opts force=0 echo=0 - opts="$($GETOPT -o fe -l force,echo -n "$PROGRAM" -- "$@")" + local opts force=0 echo=0 from_secret=0 + opts="$($GETOPT -o fesi:a: -l force,echo,secret,issuer:,account: -n "$PROGRAM" -- "$@")" local err=$? eval set -- "$opts" while true; do case $1 in -f|--force) force=1; shift ;; -e|--echo) echo=1; shift ;; + -s|--secret) from_secret=1; shift;; + -i|--issuer) issuer=$2; shift; shift;; + -a|--account) account=$2; shift; shift;; --) shift; break ;; esac done - [[ $err -ne 0 ]] && die "Usage: $PROGRAM $COMMAND insert [--force,-f] [--echo,-e] [pass-name]" + [[ $err -ne 0 ]] && die "Usage: $PROGRAM $COMMAND insert [--force,-f] [--echo,-e] [--secret, -s] [--issuer,-i issuer] [--account,-a account] [pass-name]" local prompt path uri if [[ $# -eq 1 ]]; then @@ -147,7 +208,12 @@ cmd_otp_insert() { prompt="this token" fi - otp_read_uri "$prompt" $echo + if [[ $from_secret -eq 1 ]]; then + ([[ -z "$issuer" ]] || [[ -z "$account" ]]) && die "Missing issuer or account" + otp_read_secret "$prompt" $echo "$issuer" "$account" + else + otp_read_uri "$prompt" $echo + fi if [[ -z "$path" ]]; then [[ -n "$otp_issuer" ]] && path+="$otp_issuer/" @@ -162,17 +228,20 @@ cmd_otp_insert() { } cmd_otp_append() { - local opts force=0 echo=0 - opts="$($GETOPT -o fe -l force,echo -n "$PROGRAM" -- "$@")" + local opts force=0 echo=0 from_secret=0 + opts="$($GETOPT -o fesi:a: -l force,echo,secret,issuer:account: -n "$PROGRAM" -- "$@")" local err=$? eval set -- "$opts" while true; do case $1 in -f|--force) force=1; shift ;; -e|--echo) echo=1; shift ;; + -s|--secret) from_secret=1; shift;; + -i|--issuer) issuer=$2; shift; shift;; + -a|--account) account=$2; shift; shift;; --) shift; break ;; esac done - [[ $err -ne 0 || $# -ne 1 ]] && die "Usage: $PROGRAM $COMMAND append [--force,-f] [--echo,-e] pass-name" + [[ $err -ne 0 || $# -ne 1 ]] && die "Usage: $PROGRAM $COMMAND append [--force,-f] [--echo,-e] [--secret, -s] [--issuer,-i issuer] [--account,-a account] pass-name" local prompt uri local path="${1%/}" @@ -189,7 +258,12 @@ cmd_otp_append() { [[ -n "$existing" ]] && yesno "An OTP secret already exists for $path. Overwrite it?" - otp_read_uri "$path" $echo + if [[ $from_secret -eq 1 ]]; then + ([[ -z "$issuer" ]] || [[ -z "$account" ]]) && die "Missing issuer or account" + otp_read_secret "$prompt" $echo "$issuer" "$account" + else + otp_read_uri "$prompt" $echo + fi local replaced if [[ -n "$existing" ]]; then -- cgit v1.2.3