Hello, nice to meet you. This patch will integrate oathtool functionality, if oathtool is available in the user's environment. The intention is to init a subdirectory with it's own .git and .gpg-id, so the user can have a 2FA directory that is separated from ppasswords.
The introduced functionality is an --otp argument for cmd_show which when given the -o or --otp argument (additionally a token length) can show the generated token, clip will copy the generated token but qrcode will provides the qrcode of the original key so that it can be added to an authenticator app. Having 2FA keys stored in pass suits my workflow so I imagine it might be helpful to others? If there is anything I can help with let me know. Thank you for your time. --- man/pass.1 | 8 +++++++- src/password-store.sh | 32 ++++++++++++++++++++++++++------ tests/t0020-show-tests.sh | 14 ++++++++++++++ 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/man/pass.1 b/man/pass.1 index 01a3fbe..73ce926 100644 --- a/man/pass.1 +++ b/man/pass.1 @@ -244,6 +244,11 @@ Copy existing password to clipboard .br Copied Email/[email protected] to clipboard. Will clear in 45 seconds. .TP +Show existing password as OTP 2FA code +.B zx2c4@laptop ~ $ pass -o 2FA/zx2c4.com +.br +398652 +.TP Add password to store .B zx2c4@laptop ~ $ pass insert Business/cheese-whiz-factory .br @@ -466,7 +471,8 @@ The location of the text editor used by \fBedit\fP. .BR tr (1), .BR git (1), .BR xclip (1), -.BR qrencode (1). +.BR qrencode (1), +.BR oathtool (1). .SH AUTHOR .B pass diff --git a/src/password-store.sh b/src/password-store.sh index d89d455..ffa80a1 100755 --- a/src/password-store.sh +++ b/src/password-store.sh @@ -11,6 +11,7 @@ GPG="gpg" export GPG_TTY="${GPG_TTY:-$(tty 2>/dev/null)}" which gpg2 &>/dev/null && GPG="gpg2" [[ -n $GPG_AGENT_INFO || $GPG == "gpg2" ]] && GPG_OPTS+=( "--batch" "--use-agent" ) +which oathtool &>/dev/null && OATHTOOL="oathtool" PREFIX="${PASSWORD_STORE_DIR:-$HOME/.password-store}" EXTENSIONS="${PASSWORD_STORE_EXTENSIONS_DIR:-$PREFIX/.extensions}" @@ -348,33 +349,52 @@ cmd_init() { cmd_show() { local opts selected_line clip=0 qrcode=0 - opts="$($GETOPT -o q::c:: -l qrcode::,clip:: -n "$PROGRAM" -- "$@")" + opts="$($GETOPT -o q::c::o:: -l qrcode::,clip::,otp:: -n "$PROGRAM" -- "$@")" local err=$? eval set -- "$opts" while true; do case $1 in -q|--qrcode) qrcode=1; selected_line="${2:-1}"; shift 2 ;; + -o|--otp) otp=1; otp_length="${2:-6}"; shift 2 ;; -c|--clip) clip=1; selected_line="${2:-1}"; shift 2 ;; --) shift; break ;; esac done - [[ $err -ne 0 || ( $qrcode -eq 1 && $clip -eq 1 ) ]] && die "Usage: $PROGRAM $COMMAND [--clip[=line-number],-c[line-number]] [--qrcode[=line-number],-q[line-number]] [pass-name]" + [[ $err -ne 0 || ( $qrcode -eq 1 && $clip -eq 1 && $otp -eq 1 ) ]] && die "Usage: $PROGRAM $COMMAND [--clip[=line-number],-c[line-number]] [--qrcode[=line-number],-q[line-number]] [--otp[=length]],-o[length]] [pass-name]" + if [[ $otp -eq 1 ]] ; then + [[ -z $OATHTOOL ]] && die "this feature requires oathtool" + [[ $otp_length =~ ^[6-8]$ ]] || die "OTP length must be 6, 7 or 8 digits." + fi + local pass local path="$1" local passfile="$PREFIX/$path.gpg" check_sneaky_paths "$path" if [[ -f $passfile ]]; then if [[ $clip -eq 0 && $qrcode -eq 0 ]]; then - pass="$($GPG -d "${GPG_OPTS[@]}" "$passfile" | $BASE64)" || exit $? - echo "$pass" | $BASE64 -d + if [[ $otp -eq 1 ]]; then + pass="$($GPG -d "${GPG_OPTS[@]}" "$passfile")" || exit $? + echo $($OATHTOOL --base32 --totp "$pass" -d $otp_length) + else + pass="$($GPG -d "${GPG_OPTS[@]}" "$passfile" | $BASE64)" || exit $? + echo "$pass" | $BASE64 -d + fi else [[ $selected_line =~ ^[0-9]+$ ]] || die "Clip location '$selected_line' is not a number." pass="$($GPG -d "${GPG_OPTS[@]}" "$passfile" | tail -n +${selected_line} | head -n 1)" || exit $? [[ -n $pass ]] || die "There is no password to put on the clipboard at line ${selected_line}." if [[ $clip -eq 1 ]]; then - clip "$pass" "$path" + if [[ $otp -eq 1 ]]; then + clip "$($OATHTOOL --base32 --totp "$pass" -d $otp_length)" "$path" + else + clip "$pass" "$path" + fi elif [[ $qrcode -eq 1 ]]; then - qrcode "$pass" "$path" + if [[ $otp -eq 1 ]]; then + qrcode "otpauth://totp/?secret=$pass&issuer=$path" "$path" + else + qrcode "$pass" "$path" + fi fi fi elif [[ -d $PREFIX/$path ]]; then diff --git a/tests/t0020-show-tests.sh b/tests/t0020-show-tests.sh index a4b782f..4139a10 100755 --- a/tests/t0020-show-tests.sh +++ b/tests/t0020-show-tests.sh @@ -19,4 +19,18 @@ test_expect_success 'Test "show" of nonexistant password' ' test_must_fail "$PASS" show cred2 ' +which oathtool &>/dev/null && OATHTOOL="oathtool" +if [[ ! -z $OATHTOOL ]] ; then + + test_expect_success 'Test "show" command with 6 digit otp' ' + "$PASS" insert -e "2FA/Token6"<<<"QXXFF4RRTTIIT4MMZZXZ555WWWWQQQFF" && + [[ $("$PASS" show "2FA/Token6" -o6) =~ ^[0-9]{6}$ ]] + ' + + test_expect_success 'Test "show" command with 8 digit otp' ' + "$PASS" insert -e "2FA/Token8"<<<"QXXFF4RRTTIIT4MMZZXZ555WWWWQQQFF" && + [[ $("$PASS" show "2FA/Token8" -o8) =~ ^[0-9]{8}$ ]] + ' +fi + test_done -- 2.17.2 _______________________________________________ Password-Store mailing list [email protected] https://lists.zx2c4.com/mailman/listinfo/password-store
