From 318febc9ec78ab35ae4021472bd8ce29a5389c4b Mon Sep 17 00:00:00 2001 From: Tad Fisher Date: Sun, 19 Mar 2017 22:01:42 -0700 Subject: Remove otp_insert_spec and related insert commands --- otp.bash | 175 ++++++++++------------------------------------------------ pass-otp.1 | 66 ++++++---------------- test/insert.t | 38 ------------- 3 files changed, 45 insertions(+), 234 deletions(-) diff --git a/otp.bash b/otp.bash index 301ad99..664a37b 100755 --- a/otp.bash +++ b/otp.bash @@ -18,17 +18,6 @@ OATH=$(which oathtool) -otp_urlencode() { - local LANG=C - for ((i=0; i<${#1}; i++)); do - if [[ ${1:$i:1} =~ ^[a-zA-Z0-9\.\~_-]$ ]]; then - printf "%s" "${1:$i:1}" - else - printf '%%%02X' "'${1:$i:1}" - fi - done -} - # Parse a Key URI per: https://github.com/google/google-authenticator/wiki/Key-Uri-Format # Vars are consumed by caller # shellcheck disable=SC2034 @@ -47,6 +36,7 @@ otp_parse_uri() { otp_accountname=${BASH_REMATCH[6]} [[ -z $otp_accountname ]] && otp_accountname=${BASH_REMATCH[4]} || otp_issuer=${BASH_REMATCH[4]} + [[ -z $otp_accountname ]] && die "Invalid key URI (missing accountname): $otp_uri" local p=${BASH_REMATCH[7]} local IFS=\&; local params=(${p[@]}); unset IFS @@ -72,49 +62,6 @@ otp_parse_uri() { [[ "$otp_type" == 'hotp' ]] && [[ ! "$otp_counter" =~ $pattern ]] && die "Invalid key URI (missing counter): $otp_uri" } -otp_build_uri() { - local type="$1" issuer="$2" accountname="$3" secret="$4" algorithm="$5" \ - digits="$6" period="$7" counter="$8" - - local uri="otpauth://$type/" - - local pattern='^[^:]+$' - if [[ -n "$issuer" ]]; then - [[ "$issuer" =~ $pattern ]] || die "Invalid character in issuer: ':'" - issuer=$(otp_urlencode "$issuer") - fi - - [[ -z "$accountname" ]] && die "Missing accountname" - [[ "$accountname" =~ $pattern ]] || die "Invalid character in accountname: ':'" - accountname=$(otp_urlencode "$accountname") - - if [[ -n "$issuer" ]]; then - uri+="$issuer:$accountname" - else - uri+="$accountname" - fi - - [[ -z "$secret" ]] && die "Missing secret"; uri+="?secret=$secret" - - case "$1" in - totp) - [[ -n "$algorithm" ]] && uri+="&algorithm=$algorithm" - [[ -n "$digits" ]] && uri+="&digits=$digits" - [[ -n "$period" ]] && uri+="&period=$period" - ;; - - hotp) - [[ -z "$counter" ]] && die "Missing counter"; uri+="&counter=$counter" - ;; - - *) die "Invalid OTP type '$1'" ;; - esac - - [[ -n "$issuer" ]] && uri+="&issuer=$issuer" - - echo "$uri" -} - otp_insert() { local path="${1%/}" local passfile="$PREFIX/$path.gpg" @@ -132,103 +79,23 @@ otp_insert() { $GPG -e "${GPG_RECIPIENT_ARGS[@]}" -o "$passfile" "${GPG_OPTS[@]}" <<<"$contents" || die "OTP secret encryption aborted." - [[ -z "$message" ]] && message="Add OTP secret for $path to store." - git_add_file "$passfile" "$message" } -otp_insert_uri() { - local opts force=0 - opts="$($GETOPT -o f -l force -n "$PROGRAM" -- "$@")" - local err=$? - eval set -- "$opts" - while true; do case $1 in - -f|--force) force=1; shift ;; - --) shift; break ;; - esac done - - [[ $err -ne 0 || $# -ne 2 ]] && die "Usage: $PROGRAM $COMMAND insert [--force,-f] uri pass-name" - - local uri="$1" - - otp_parse_uri "$uri" - - otp_insert "$2" $force "$otp_uri" -} - -otp_insert_spec() { - local opts contents secret issuer accountname algorithm period digits counter force=0 - local type="$1"; shift - - opts="$($GETOPT -o s:i:n:a:p:d:f -l secret:,issuer:,accountname:,algorithm:,period:,digits:,force -n "$PROGRAM" -- "$@")" - local err=$? - eval set -- "$opts" - while true; do case "$1" in - -s|--secret) secret="$2"; shift 2 ;; - -i|--issuer) issuer="$2"; shift 2 ;; - -n|--accountname) accountname="$2"; shift 2 ;; - -a|--algorithm) algorithm="$2"; shift 2 ;; - -p|--period) period="$2"; shift 2 ;; - -d|--digits) digits="$2"; shift 2 ;; - -f|--force) force=1; shift ;; - --) shift; break ;; - esac done - - [[ $type == "totp" && ($err -ne 0 || $# -ne 1) ]] && - die "Usage: $PROGRAM $COMMAND insert totp [--secret=key,s key] [--issuer=issuer,-i issuer] [--accountname=name,-n name] [--algorithm=algorithm,-a algorithm] [--period=seconds,-p seconds] [--digits=digits,-d digits] [--force,-f] pass-name" - - [[ $type == "hotp" && ($err -ne 0 || $# -ne 2) ]] && - die "Usage: $PROGRAM $COMMAND insert hotp [--secret=key,s key] [--issuer=issuer,-i issuer] [--accountname=accountname,-n accountname] [--digits=digits,-d digits] [--force,-f] pass-name counter" - - local path="$1" counter="$2" - - [[ -n "$algorithm" ]] && case $algorithm in - sha1|sha256|sha512) ;; - *) die "Invalid algorithm '$algorithm'. May be one of 'sha1', 'sha256', or 'sha512'" ;; - esac - - [[ -n "$digits" ]] && case $digits in - 6|8) ;; - *) die "Invalid digits '$digits'. May be one of '6' or '8'" ;; - esac - - if [[ -z $secret ]]; then - read -r -p "Enter secret (base32-encoded): " -s secret || die "Missing secret" - fi - - # Populate issuer and accountname from either options or path - if [[ -z $accountname ]]; then - accountname="$(basename "$path")" - if [[ -z "$issuer" ]]; then - issuer="$(basename "$(dirname "$path")")" - [[ "$issuer" == "." ]] && unset issuer - fi - fi - - local uri; uri=$(otp_build_uri "$type" "$issuer" "$accountname" "$secret" "$algorithm" "$period" "$digits" "$counter") - - otp_insert "$1" $force "$uri" -} - cmd_otp_usage() { cat <<-_EOF Usage: $PROGRAM otp [show] [--clip,-c] pass-name 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 totp [--secret=key,-s key] [--algorithm alg,-a alg] - [--period=seconds,-p seconds] - [--digits=digits,-d digits] [--force,-f] pass-name - Insert new TOTP secret. Prompt before overwriting existing password + $PROGRAM otp insert [--force,-f] uri pass-name + Insert new OTP key URI. Prompt before overwriting existing password unless forced. - $PROGRAM otp insert hotp [--secret=secret,-s secret] - [--digits=digits,-d digits] [--force,-f] - pass-name counter - Insert new HOTP secret with initial counter. Prompt before overwriting - existing password unless forced. $PROGRAM otp uri [--clip,-c] [--qrcode,-q] pass-name - Create a secret key URI suitable for importing into other TOTP clients. - Optionally, put it on the clipboard, or display a QR code. + Display the key URI stored in pass-name. Optionally, put it on the + clipboard, or display a QR code. + $PROGRAM otp validate uri + Test if the given URI is a valid OTP key URI. More information may be found in the pass-otp(1) man page. _EOF @@ -236,13 +103,27 @@ _EOF } cmd_otp_insert() { - case "$1" in - totp|hotp) otp_insert_spec "$@" ;; - *) otp_insert_uri "$@" ;; - esac + local opts force=0 + opts="$($GETOPT -o f -l force -n "$PROGRAM" -- "$@")" + local err=$? + eval set -- "$opts" + while true; do case $1 in + -f|--force) force=1; shift ;; + --) shift; break ;; + esac done + + [[ $err -ne 0 || $# -ne 2 ]] && die "Usage: $PROGRAM $COMMAND insert [--force,-f] uri pass-name" + + local uri="$1" + + otp_parse_uri "$uri" + + otp_insert "$2" $force "$otp_uri" "Add OTP secret for $2 to store." } cmd_otp_code() { + [[ -z "$OATH" ]] && die "Failed to generate OTP code: oathtool is not installed." + local opts clip=0 opts="$($GETOPT -o c -l clip -n "$PROGRAM" -- "$@")" local err=$? @@ -272,15 +153,15 @@ cmd_otp_code() { totp) cmd="$OATH -b --totp" [[ -n "$otp_algorithm" ]] && cmd+="=$otp_algorithm" - [[ -n "$otp_period" ]] && cmd+=" --time-step-size=$period"s - [[ -n "$otp_digits" ]] && cmd+=" --digits=$digits" + [[ -n "$otp_period" ]] && cmd+=" --time-step-size=$otp_period"s + [[ -n "$otp_digits" ]] && cmd+=" --digits=$otp_digits" cmd+=" $otp_secret" ;; hotp) local counter=$((otp_counter+1)) cmd="$OATH -b --hotp --counter=$counter" - [[ -n "$otp_digits" ]] && cmd+=" --digits=$digits" + [[ -n "$otp_digits" ]] && cmd+=" --digits=$otp_digits" cmd+=" $otp_secret" ;; esac diff --git a/pass-otp.1 b/pass-otp.1 index 8e67621..ca298dc 100644 --- a/pass-otp.1 +++ b/pass-otp.1 @@ -23,18 +23,18 @@ utility with the command for adding OTP secrets, generating OTP codes, and displaying secret key URIs using the standard \fIotpauth://\fP scheme. -If no COMMAND is specified, COMMAND defaults to \fBshow\fP. +If no COMMAND is specified, COMMAND defaults to \fBcode\fP. .SH COMMANDS .TP -\fBotp show\fP [ \fI--clip\fP, \fI-c\fP ] \fIpass-name\fP +\fBotp code\fP [ \fI--clip\fP, \fI-c\fP ] \fIpass-name\fP Generate and print an OTP code from the secret key stored in \fIpass-name\fP. If \fI--clip\fP or \fI-c\fP is specified, do not print the code but instead copy it to the clipboard using .BR xclip (1) and then restore the clipboard after 45 (or \fIPASSWORD_STORE_CLIP_TIME\fP) -seconds. +seconds. This command is alternatively named \fBshow\fP. .TP \fBotp insert\fP [ \fI--force\fP, \fI-f\fP ] \fIuri\fP \fIpass-name\fP @@ -49,31 +49,11 @@ Prompt before overwriting an existing password, unless \fBadd\fP. .TP -\fBotp insert totp\fP [ \fI--secret\fP=\fIkey\fP, \fI-s\fP \fIkey\fP ] [ \fI--issuer\fP=\fIissuer\fP, \fI-i\fP \fIissuer\fP ] [ \fI--accountname\fP=\fIname\fP, \fI-n\fP \fIname\fP ] [ \fI--algorithm\fP=\fIalgorithm\fP, \fI-a\fP \fIalgorithm\fP ] [ \fI--period\fP=\fIperiod\fP, \fI-p\fP \fIperiod\fP ] [ \fI--digits\fP=\fIdigits\fP, \fI-d\fP \fIdigits\fP ] [ \fI--force\fP, \fI-f\fP ] \fIpass-name\fP +\fBotp uri\fP [ \fI--clip\fP, \fI-c\fP | \fI--qrcode\fP, \fI-q\fP ] \fIpass-name\fP -Insert a new TOTP secret into the password store called \fIpass-name\fP. If -\fI--secret\fP or \fI-s\fP are not specified, this will read \fIKEY\fP from -standard in. Prompt before overwriting an existing password, unless -\fI--force\fP or \fI-f\fP is specified. This command is alternatively named -\fBadd totp\fP. - -.TP -\fBotp insert hotp\fP [ \fI--secret\fP=\fIkey\fP, \fI-s\fP \fIkey\fP ] [ \fI--issuer\fP=\fIissuer\fP, \fI-i\fP \fIissuer\fP ] [ \fI--accountname\fP=\fIname\fP, \fI-n\fP \fIname\fP ] [ \fI--digits\fP=\fIdigits\fP, \fI-d\fP \fIdigits\fP ] [ \fI--force\fP, \fI-f\fP ] \fIpass-name\fP \fIcounter\fP - -Insert a new HOTP secret into the password store called \fIpass-name\fP. A -\fIcounter\fP argument is required, which is an integer specifying the initial -HOTP counter stored alongside the secret. If -\fI--secret\fP or \fI-s\fP are not specified, this will read \fIKEY\fP from -standard in. Prompt before overwriting an existing password, unless -\fI--force\fP or \fI-f\fP is specified. This command is alternatively named -\fBadd hotp\fP. - -.TP -\fBotp uri\fP [ \fI--clip\fP, \fI-c\fP | \fI--qrcode\fP, \fI-q\fP ] pass-name - -Create and print a URI encoding the secret key and OTP parameters using the -standard \fIotpauth://\fP scheme. If \fI--clip\fP or \fI-c\fP is specified, do -not print the URI but instead copy it to the clipboard using +Print the key URI stored in \fIpass-name\fP to stdout. If \fI--clip\fP or +\fI-c\fP is specified, do not print the URI but instead copy it to the clipboard +using .BR xclip (1) and then restore the clipboard after 45 (or \fIPASSWORD_STORE_CLIP_TIME\fP) seconds. If \fI--qrcode\fP or \fI-q\fP is specified, do not print the URI but @@ -81,6 +61,14 @@ instead display a QR code using .BR qrencode (1) either to the terminal or graphically if supported. +.TP +\fBotp validate\fP \fIuri\fP + +Test a URI string for validity according to the Key Uri Format. For more +information about this format, see the documentation at +.UR https://\:github.\:com/\:google/\:google-authenticator/\:wiki/\:Key-Uri-Format +.UE . + .SH OPTIONS .TP @@ -89,30 +77,10 @@ Put the OTP code in the clipboard. .TP \fB\-f\fP, \fB--force\fP -Force to update and do not wait for user instruction. - -.TP -\fB-s\fP \fIkey\fP, \fB--secret\fR=\fIkey\fP -Provide a secret \fIkey\fP. This key must be base32-encoded. - -.TP -\fB-a\fP \fIalgorithm\fP, \fB--algorithm\fP=\fIalgorithm\fP -Specify the \fIalgorithm\fP for a TOTP secret. Accepted values are \fIsha1\fP, -\fIsha256\fP, and \fIsha512\fP. This option defaults to \fIsha1\fP. - -.TP -\fB-p\fP \fIperiod\fP, \fB--period\fP=\fIperiod\fP -Specify the \fIperiod\fP for a TOTP secret, in seconds. This option defaults to -\fI30\fP. - -.TP -\fB-d\fP \fIdigits\fP, \fB--digits\fP=\fIdigits\fP -Specify the number of \fIdigits\fP this secret should generate when used with -\fBshow\fP. Accepted values are \fI6\fP and \fI8\fP. This option defaults to -\fI6\fP. +Force update and do not wait for user instruction. .TP -\fB\-h\fB, \-\-help\fR +\fBhelp\fP, \fB\-h\fB, \-\-help\fR Show usage message. .SH SEE ALSO diff --git a/test/insert.t b/test/insert.t index 228a8ec..da6477c 100755 --- a/test/insert.t +++ b/test/insert.t @@ -32,42 +32,4 @@ test_expect_success 'Force overwrites key URI' ' [[ $("$PASS" show passfile) == "$uri2" ]] ' -test_expect_success 'Inserts a basic TOTP key' ' - uri="otpauth://totp/passfile?secret=AAAAAAAAAAAAAAAAAAAAA" - - test_pass_init && - "$PASS" otp insert totp -s AAAAAAAAAAAAAAAAAAAAA passfile && - [[ $("$PASS" show passfile) == "$uri" ]] -' - -test_expect_success 'Inserts a TOTP key with issuer in path' ' - uri="otpauth://totp/example.com:passfile?secret=AAAAAAAAAAAAAAAAAAAAA&issuer=example.com" - - test_pass_init && - "$PASS" otp insert totp -s AAAAAAAAAAAAAAAAAAAAA example.com/passfile && - [[ $("$PASS" show example.com/passfile) == "$uri" ]] -' - -test_expect_success 'Inserts a TOTP key with issuer in nested path' ' - uri="otpauth://totp/foo:passfile?secret=AAAAAAAAAAAAAAAAAAAAA&issuer=foo" - - test_pass_init && - "$PASS" otp insert totp -s AAAAAAAAAAAAAAAAAAAAA example.com/foo/passfile && - [[ $("$PASS" show example.com/foo/passfile) == "$uri" ]] -' - -test_expect_success 'Inserts a TOTP key with spaces in path' ' - uri="otpauth://totp/example%20dot%20com:pass%20file?secret=AAAAAAAAAAAAAAAAAAAAA&issuer=example%20dot%20com" - test_pass_init && - "$PASS" otp insert totp -s AAAAAAAAAAAAAAAAAAAAA "example dot com/pass file" && - [[ $("$PASS" show "example dot com/pass file") == "$uri" ]] -' - -test_expect_success 'Commits insert to git' ' - test_pass_init && - pass git init && - "$PASS" otp insert totp -s AAAAAAAAAAAAAAAAAAAAA passfile && - git log --no-decorate -1 | grep "Add OTP secret for passfile to store." -' - test_done -- cgit v1.2.3