diff options
-rwxr-xr-x | otp.bash | 44 | ||||
-rwxr-xr-x | test/uri.t | 27 |
2 files changed, 71 insertions, 0 deletions
@@ -18,6 +18,45 @@ OATH=$(which oathtool) +# Parse a Key URI per: https://github.com/google/google-authenticator/wiki/Key-Uri-Format +# Vars are consumed by caller +# shellcheck disable=SC2034 +otp_parse_uri() { + local uri="$*" + + uri="${uri//\`/%60}" + uri="${uri//\"/%22}" + + local pattern='^otpauth:\/\/(totp|hotp)(\/(([^:?]+)?(:([^:?]*))?))?(\?([^#&?]+))(&([^#&?]+))*$' + [[ "$uri" =~ $pattern ]] || die "Cannot parse OTP key URI: $uri" + + otp_uri=${BASH_REMATCH[0]} + 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]} + + local parameters=(${BASH_REMATCH[@]:7}) + pattern='^([^?&=]+)(=(.+))$' + for param in "${parameters[@]}"; do + if [[ "$param" =~ $pattern ]]; then + case ${BASH_REMATCH[1]} in + secret) otp_secret=${BASH_REMATCH[3]} ;; + digits) otp_digits=${BASH_REMATCH[3]} ;; + algorithm) otp_algorithm=${BASH_REMATCH[3]} ;; + period) otp_period=${BASH_REMATCH[3]} ;; + counter) otp_counter=${BASH_REMATCH[3]} ;; + issuer) otp_issuer=${BASH_REMATCH[3]} ;; + *) ;; + esac + fi + done + + [[ -z "$otp_secret" ]] && die "Invalid key URI (missing secret): $otp_uri" + [[ "$otp_type" == 'hotp' && -z "$otp_counter" ]] && die "Invalid key URI (missing counter): $otp_uri" +} + otp_increment_counter() { local ret=$1 local counter=$2 contents="$3" path="$4" passfile="$5" @@ -265,11 +304,16 @@ cmd_otp_uri() { fi } +cmd_otp_validate() { + otp_parse_uri "$1" +} + 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 "$@" ;; esac exit 0 diff --git a/test/uri.t b/test/uri.t new file mode 100755 index 0000000..9fbe66c --- /dev/null +++ b/test/uri.t @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +export test_description='Tests pass otp URI parsing' + +. ./setup.sh + +test_expect_success 'Parses a basic TOTP URI' ' + "$PASS" otp validate "otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example" +' + +test_expect_success 'Parses a complex TOTP URI' ' + "$PASS" otp validate otpauth://totp/ACME%20Co:john.doe@email.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30 +' + +test_expect_success 'Fails for bogus URL' ' + test_must_fail "$PASS" otp validate https://www.google.com/ +' + +test_expect_success 'Fails for missing secret' ' + test_must_fail "$PASS" otp validate otpauth://totp/ACME%20Co:john.doe@email.com?issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30 +' + +test_expect_success 'Fails for missing counter' ' + test_must_fail "$PASS" otp validate otpauth://hotp?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ +' + +test_done |