From eecfd18bbe4987e1f49f060d0329b4668bda747f Mon Sep 17 00:00:00 2001 From: Tad Fisher Date: Sun, 19 Mar 2017 21:00:12 -0700 Subject: Parse key URIs to generate OTP codes --- otp.bash | 91 ++++++++++++++++++++++++++++++------------------------------- test/code.t | 27 ++++++++++++++++++ 2 files changed, 71 insertions(+), 47 deletions(-) create mode 100755 test/code.t diff --git a/otp.bash b/otp.bash index b95e981..a2007c9 100755 --- a/otp.bash +++ b/otp.bash @@ -95,10 +95,10 @@ otp_build_uri() { fi [[ -z "$secret" ]] && die "Missing secret"; uri+="?secret=$secret" - [[ -n "$algorithm" ]] && uri+="&algorithm=$algorithm" case "$1" in totp) + [[ -n "$algorithm" ]] && uri+="&algorithm=$algorithm" [[ -n "$digits" ]] && uri+="&digits=$digits" [[ -n "$period" ]] && uri+="&period=$period" ;; @@ -115,28 +115,12 @@ otp_build_uri() { echo "$uri" } -otp_increment_counter() { - local ret=$1 - local counter=$2 contents="$3" path="$4" passfile="$5" - - local inc=$((counter+1)) - - contents=${contents//otp_counter: $counter/otp_counter: $inc} - - set_gpg_recipients "$(dirname "$path")" - - $GPG -e "${GPG_RECIPIENT_ARGS[@]}" -o "$passfile" "${GPG_OPTS[@]}" <<<"$contents" || die "OTP secret encryption aborted." - - git_add_file "$passfile" "Update HOTP counter value for $path." - - eval "$ret='$inc'" -} - otp_insert() { local path="${1%/}" local passfile="$PREFIX/$path.gpg" local force=$2 local contents="$3" + local message="$4" check_sneaky_paths "$path" set_git "$passfile" @@ -148,7 +132,9 @@ otp_insert() { $GPG -e "${GPG_RECIPIENT_ARGS[@]}" -o "$passfile" "${GPG_OPTS[@]}" <<<"$contents" || die "OTP secret encryption aborted." - git_add_file "$passfile" "Add given OTP secret for $path to store." + [[ -z "$message" ]] && message="Add OTP secret for $path to store." + + git_add_file "$passfile" "$message" } otp_insert_uri() { @@ -256,8 +242,8 @@ cmd_otp_insert() { esac } -cmd_otp_show() { - local opts contents clip=0 secret="" type="" algorithm="" counter="" period=30 digits=6 +cmd_otp_code() { + local opts clip=0 opts="$($GETOPT -o c -l clip -n "$PROGRAM" -- "$@")" local err=$? eval set -- "$opts" @@ -266,7 +252,7 @@ cmd_otp_show() { --) shift; break ;; esac done - [[ $err -ne 0 || $# -ne 1 ]] && die "Usage: $PROGRAM $COMMAND show [--clip,-c] pass-name" + [[ $err -ne 0 || $# -ne 1 ]] && die "Usage: $PROGRAM $COMMAND [--clip,-c] pass-name" local path="$1" local passfile="$PREFIX/$path.gpg" @@ -274,31 +260,39 @@ cmd_otp_show() { [[ ! -f $passfile ]] && die "Passfile not found" contents=$($GPG -d "${GPG_OPTS[@]}" "$passfile") - while read -r -a line; do case ${line[0]} in - otp_secret:) secret=${line[1]} ;; - otp_type:) type=${line[1]} ;; - otp_algorithm:) algorithm=${line[1]} ;; - otp_period:) period=${line[1]} ;; - otp_counter:) counter=${line[1]} ;; - otp_digits:) digits=${line[1]} ;; - *) true ;; - esac done <<< "$contents" - - [[ -z $secret ]] && die "Missing otp_secret: line in $passfile" - [[ -z $type ]] && die "Missing otp_type: line in $passfile" - [[ $type = "totp" && -z $algorithm ]] && die "Missing otp_algorithm: line in $passfile" - [[ $type = "hotp" && -z $counter ]] && die "Missing otp_counter: line in $passfile" - - local out - case $type in - totp) out=$($OATH -b --totp="$algorithm" --time-step-size="$period"s --digits="$digits" "$secret") ;; - hotp) otp_increment_counter counter "$counter" "$contents" "$path" "$passfile" > /dev/null \ - || die "Failed to increment HOTP counter for $passfile" - out=$($OATH -b --hotp --counter="$counter" --digits="$digits" "$secret") + while read -r -a line; do + if [[ "$line" == otpauth://* ]]; then + otp_parse_uri "$line" + break + fi + done <<< "$contents" + + local cmd + case "$otp_type" in + 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" + cmd+=" $otp_secret" + ;; + + hotp) + local counter=$((otp_counter+1)) + cmd="$OATH -b --hotp --counter=$counter" + [[ -n "$otp_digits" ]] && cmd+=" --digits=$digits" + cmd+=" $otp_secret" ;; - *) die "Invalid OTP type '$type'. May be one of 'totp' or 'hotp'" ;; esac + local out; out=$($cmd) || die "Failed to generate OTP code for $path" + + if [[ "$otp_type" == "hotp" ]]; then + # Increment HOTP counter in-place + local uri=${otp_uri/&counter=$otp_counter/&counter=$counter} + otp_insert "$path" 1 "$uri" "Increment HOTP counter for $path." + fi + if [[ $clip -ne 0 ]]; then clip "$out" "OTP code for $path" else @@ -326,7 +320,10 @@ cmd_otp_uri() { contents=$($GPG -d "${GPG_OPTS[@]}" "$passfile") while read -r -a line; do - [[ "$line" == otpauth://* ]] && otp_parse_uri "$line" + if [[ "$line" == otpauth://* ]]; then + otp_parse_uri "$line" + break + fi done <<< "$contents" if [[ clip -eq 1 ]]; then @@ -344,10 +341,10 @@ cmd_otp_validate() { case "$1" in help|--help|-h) shift; cmd_otp_usage "$@" ;; - show) shift; cmd_otp_show "$@" ;; insert|add) shift; cmd_otp_insert "$@" ;; uri) shift; cmd_otp_uri "$@" ;; validate) shift; cmd_otp_validate "$@" ;; - *) cmd_otp_show "$@" ;; + code|show) shift; cmd_otp_code "$@" ;; + *) cmd_otp_code "$@" ;; esac exit 0 diff --git a/test/code.t b/test/code.t new file mode 100755 index 0000000..095cdd5 --- /dev/null +++ b/test/code.t @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +export test_description='Tests pass otp code generation' + +. ./setup.sh + +test_expect_success 'Generates TOTP code' ' + uri="otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example" + + test_pass_init && + "$PASS" otp insert "$uri" passfile && + code=$("$PASS" otp passfile) && + [[ ${#code} -eq 6 ]] +' + +test_expect_success 'Generates HOTP code and increments counter' ' + uri="otpauth://hotp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&counter=10&issuer=Example" + inc="otpauth://hotp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&counter=11&issuer=Example" + + test_pass_init && + "$PASS" otp insert "$uri" passfile && + code=$("$PASS" otp passfile) && + [[ ${#code} -eq 6 ]] && + [[ $("$PASS" otp uri passfile) == "$inc" ]] +' + +test_done -- cgit v1.2.3