Parse key URIs to generate OTP codes

This commit is contained in:
Tad Fisher 2017-03-19 21:00:12 -07:00
parent 698ae45a78
commit eecfd18bbe
2 changed files with 70 additions and 46 deletions

View File

@ -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"
while read -r -a line; do
if [[ "$line" == otpauth://* ]]; then
otp_parse_uri "$line"
break
fi
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")
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

27
test/code.t Executable file
View File

@ -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