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
This commit is contained in:
parent
33f390ba22
commit
b8009511b3
112
otp.bash
112
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 <string>
|
||||
|
||||
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.
|
||||
|
||||
$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.
|
||||
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]
|
||||
[[--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
|
||||
|
|
38
pass-otp.1
38
pass-otp.1
|
@ -37,28 +37,46 @@ and then restore the clipboard after 45 (or \fIPASSWORD_STORE_CLIP_TIME\fP)
|
|||
seconds. This command is alternatively named \fBshow\fP.
|
||||
|
||||
.TP
|
||||
\fBotp insert\fP [ \fI--force\fP, \fI-f\fP ] [ \fI--echo\fP, \fI-e\fP ] [ \fIpass-name\fP ]
|
||||
\fBotp insert\fP [ \fI--force\fP, \fI-f\fP ] [ \fI--echo\fP, \fI-e\fP ] \
|
||||
[ [ \fI--secret\fP, \fI-s\fP ] [ \fI--issuer\fP, \fI-i\fP \fIissuer\fP ] \
|
||||
[ \fI--account\fP, \fI-a\fP \fIaccount\fP ] ] [ \fIpass-name\fP ]
|
||||
|
||||
Prompt for and insert a new OTP secret into the password store at
|
||||
\fIpass-name\fP. The secret must be formatted according to the Key Uri Format;
|
||||
see the documentation at
|
||||
\fIpass-name\fP.
|
||||
|
||||
If \fI--secret\fP is specified, prompt for the \fIsecret\fP value, assuming SHA1
|
||||
algorithm, 30-second period, and 6 OTP digits. One or both of \fIissuer\fP and
|
||||
\fIaccount\fP must also be specified.
|
||||
|
||||
If \fI--secret\fP is not specified, prompt for a key URI; see the documentation at
|
||||
.UR https://\:github.\:com/\:google/\:google-authenticator/\:wiki/\:Key-Uri-Format
|
||||
.UE .
|
||||
The URI is consumed from stdin; specify \fI--echo\fP or \fI-e\fP to echo input
|
||||
.UE
|
||||
for the key URI specification.
|
||||
|
||||
The secret is consumed from stdin; specify \fI--echo\fP or \fI-e\fP to echo input
|
||||
when running this command interactively. If \fIpass-name\fP is not specified,
|
||||
convert the \fIissuer:accountname\fP URI label to a path in the form of
|
||||
\fIisser/accountname\fP. Prompt before overwriting an existing password, unless
|
||||
\fIisser/accountname\fP. Prompt before overwriting an existing secret, unless
|
||||
\fI--force\fP or \fI-f\fP is specified. This command is alternatively named
|
||||
\fBadd\fP.
|
||||
|
||||
.TP
|
||||
\fBotp append\fP [ \fI--force\fP, \fI-f\fP ] [ \fI--echo\fP, \fI-e\fP ] \fIpass-name\fP
|
||||
\fBotp append\fP [ \fI--force\fP, \fI-f\fP ] [ \fI--echo\fP, \fI-e\fP ] \
|
||||
[ [ \fI--secret\fP, \fI-s\fP ] [ \fI--issuer\fP, \fI-i\fP \fIissuer\fP ] \
|
||||
[ \fI--account\fP, \fI-a\fP \fIaccount\fP ] ] \fIpass-name\fP
|
||||
|
||||
Append an OTP secret to the password stored in \fIpass-name\fP, preserving any
|
||||
existing lines. The secret must be formatted according to the Key Uri Format;
|
||||
see the documentation at
|
||||
existing lines.
|
||||
|
||||
If \fI--secret\fP is specified, prompt for the \fIsecret\fP value, assuming SHA1
|
||||
algorithm, 30-second period, and 6 OTP digits. One or both of \fIissuer\fP and
|
||||
\fIaccount\fP must also be specified.
|
||||
|
||||
If \fI--secret\fP is not specified, prompt for a key URI; see the documentation at
|
||||
.UR https://\:github.\:com/\:google/\:google-authenticator/\:wiki/\:Key-Uri-Format
|
||||
.UE .
|
||||
.UE
|
||||
for the key URI specification.
|
||||
|
||||
The URI is consumed from stdin; specify \fI--echo\fP or \fI-e\fP to echo input
|
||||
when running this command interactively. Prompt before overwriting an existing
|
||||
secret, unless \fI--force\fP or \fI-f\fP is specified.
|
||||
|
|
|
@ -14,6 +14,17 @@ test_expect_success 'Reads non-terminal input' '
|
|||
[[ $("$PASS" otp uri passfile) == "$uri" ]]
|
||||
'
|
||||
|
||||
test_expect_success 'Read secret non-terminal input' '
|
||||
existing="foo bar baz"
|
||||
secret=JBSWY3DPEHPK3PXP
|
||||
uri="otpauth://totp/Example:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example"
|
||||
|
||||
test_pass_init &&
|
||||
"$PASS" insert -e passfile <<< "$existing" &&
|
||||
"$PASS" otp append -s -i Example -a alice@google.com -e passfile <<< "$secret" &&
|
||||
[[ $("$PASS" otp uri passfile) == "$uri" ]]
|
||||
'
|
||||
|
||||
test_expect_success 'Reads terminal input in noecho mode' '
|
||||
existing="foo bar baz"
|
||||
uri="otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example"
|
||||
|
|
|
@ -112,4 +112,22 @@ test_expect_success 'Force overwrites key URI' '
|
|||
[[ $("$PASS" show passfile) == "$uri2" ]]
|
||||
'
|
||||
|
||||
test_expect_success 'Insert passfile from secret with options(issuer, accountname)' '
|
||||
secret="JBSWY3DPEHPK3PXP"
|
||||
uri="otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example"
|
||||
|
||||
test_pass_init &&
|
||||
"$PASS" otp insert -s -i Example -a alice@google.com passfile <<< "$secret" &&
|
||||
echo [[ $("$PASS" show passfile) == "$uri" ]]
|
||||
'
|
||||
|
||||
test_expect_success 'Insert from secret without passfile' '
|
||||
secret="JBSWY3DPEHPK3PXP"
|
||||
uri="otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example"
|
||||
|
||||
test_pass_init &&
|
||||
"$PASS" otp insert -s -i Example -a alice@google.com <<< "$secret" &&
|
||||
echo [[ $("$PASS" show Example/alice@google.com) == "$uri" ]]
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
Loading…
Reference in New Issue