I wonder if anyone else would find this feature useful? If you're not familiar with the practice, this guy explains it well:
https://youtu.be/boj9q26gadE

The gist is: even though using a weak, recycled, memorized password alone is not secure, _adding_ one to a strong, generated, persisted password enhances security by mitigating the risk that the contents of the password store are exposed.

He calls it double-blind password management; I have also heard the terms password splitting, secret salting, and peppering. These last two are often used in a cryptographic context, but are nonetheless applicable here, and I figured "pepper" was short and simple enough for a flag.

If you add the `--pepper` or `-p` flag to `show`, then GPG pinentry will prompt you for a string/password/pepper—optionally remembering it for the session—and append it to the password being retrieved.

Obviously, this cannot mitigate the risk from password-store itself, plugins, or clipboard snooping, but I like the convenience of securely caching the pepper string and copy-pasting both parts together.

Not done:
- browserpass integration
- config option to cache one pepper for all entries rather than one per entry


Cheers,
Paul

>From a5c1beebb093d18afb65f4174610714c5c61cd95 Mon Sep 17 00:00:00 2001
From: Paul Erickson <[email protected]>
Date: Thu, 25 Nov 2021 15:49:50 -0600
Subject: [PATCH] show: allow user to "pepper", or add to password during
 retrieval
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Sometimes called password splitting, one keeps part of a password in
their password manager and part outside, i.e. typically appending some
weak, reused password to their strong, unique, generated passwords.
If one doesn't completely distrust their password manager, this option
adds convenience by prompting the user and letting the OS cache it—just
like GPG—and appending it to the stored password (e.g. for use with -c).
---
 src/completion/pass.bash-completion |  2 +-
 src/completion/pass.fish-completion |  1 +
 src/completion/pass.zsh-completion  |  4 +++-
 src/password-store.sh               | 28 +++++++++++++++++++++-------
 4 files changed, 26 insertions(+), 9 deletions(-)

diff --git a/src/completion/pass.bash-completion b/src/completion/pass.bash-completion
index 2d23cbf..2da8053 100644
--- a/src/completion/pass.bash-completion
+++ b/src/completion/pass.bash-completion
@@ -101,7 +101,7 @@ _pass()
 				_pass_complete_entries
 				;;
 			show|-*)
-				COMPREPLY+=($(compgen -W "-c --clip" -- ${cur}))
+				COMPREPLY+=($(compgen -W "-c --clip -p --pepper" -- ${cur}))
 				_pass_complete_entries 1
 				;;
 			insert)
diff --git a/src/completion/pass.fish-completion b/src/completion/pass.fish-completion
index 0f57dd2..7c026c8 100644
--- a/src/completion/pass.fish-completion
+++ b/src/completion/pass.fish-completion
@@ -97,6 +97,7 @@ complete -c $PROG -f -n '__fish_pass_uses_command edit' -a "(__fish_pass_print_e
 
 complete -c $PROG -f -n '__fish_pass_needs_command' -a show -d 'Command: show existing password'
 complete -c $PROG -f -n '__fish_pass_uses_command show' -s c -l clip -d 'Put password in clipboard'
+complete -c $PROG -f -n '__fish_pass_uses_command show' -s p -l pepper -d 'Add a string to the end of the password'
 complete -c $PROG -f -n '__fish_pass_uses_command show' -a "(__fish_pass_print_entries)"
 # When no command is given, `show` is defaulted.
 complete -c $PROG -f -n '__fish_pass_needs_command' -s c -l clip -d 'Put password in clipboard'
diff --git a/src/completion/pass.zsh-completion b/src/completion/pass.zsh-completion
index d911e12..cc780f1 100644
--- a/src/completion/pass.zsh-completion
+++ b/src/completion/pass.zsh-completion
@@ -117,7 +117,9 @@ _pass () {
 _pass_cmd_show () {
 	_arguments : \
 		"-c[put it on the clipboard]" \
-		"--clip[put it on the clipboard]"
+		"--clip[put it on the clipboard]"\
+		"-p[prompt for a string to append]" \
+		"--pepper[prompt for a string to append]"
 	_pass_complete_entries
 }
 _pass_complete_entries_helper () {
diff --git a/src/password-store.sh b/src/password-store.sh
index aef8d72..2572a2d 100755
--- a/src/password-store.sh
+++ b/src/password-store.sh
@@ -262,7 +262,7 @@ cmd_version() {
 	============================================
 	= pass: the standard unix password manager =
 	=                                          =
-	=                  v1.7.4                  =
+	=                  v1.8.0                  =
 	=                                          =
 	=             Jason A. Donenfeld           =
 	=               [email protected]            =
@@ -284,7 +284,7 @@ cmd_usage() {
 	        List passwords.
 	    $PROGRAM find pass-names...
 	    	List passwords that match pass-names.
-	    $PROGRAM [show] [--clip[=line-number],-c[line-number]] pass-name
+	    $PROGRAM [show] [--clip[=line-number],-c[line-number]] [--pepper,-p] pass-name
 	        Show existing password and optionally put it on the clipboard.
 	        If put on the clipboard, it will be cleared in $CLIP_TIME seconds.
 	    $PROGRAM grep [GREPOPTIONS] search-string
@@ -366,29 +366,43 @@ cmd_init() {
 }
 
 cmd_show() {
-	local opts selected_line clip=0 qrcode=0
-	opts="$($GETOPT -o q::c:: -l qrcode::,clip:: -n "$PROGRAM" -- "$@")"
+	local opts selected_line clip=0 qrcode=0 pepper=0
+	opts="$($GETOPT -o q::c::p:: -l qrcode::,clip::,pepper:: -n "$PROGRAM" -- "$@")"
 	local err=$?
 	eval set -- "$opts"
 	while true; do case $1 in
 		-q|--qrcode) qrcode=1; selected_line="${2:-1}"; shift 2 ;;
 		-c|--clip) clip=1; selected_line="${2:-1}"; shift 2 ;;
+		-p|--pepper) pepper=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 ) ]] && die "Usage: $PROGRAM $COMMAND [--clip[=line-number],-c[line-number]] [--qrcode[=line-number],-q[line-number]] [--pepper,-p] [pass-name]"
 
 	local pass
 	local path="$1"
 	local passfile="$PREFIX/$path.gpg"
+	local suffix
 	check_sneaky_paths "$path"
 	if [[ -f $passfile ]]; then
+		if [[ $pepper -eq 1 ]]; then
+			suffix=$(
+				pinentry <<-EOF | grep -Po '^D \K.*$'
+					settitle Password Store Pepper
+					setprompt Password Store Pepper
+					setdesc Enter string to append to the stored password
+					option allow-external-password-cache
+					setkeyinfo password-store-pepper-$path
+					getpin
+				EOF
+			)
+		fi
 		if [[ $clip -eq 0 && $qrcode -eq 0 ]]; then
-			pass="$($GPG -d "${GPG_OPTS[@]}" "$passfile" | $BASE64)" || exit $?
+			pass="$($GPG -d "${GPG_OPTS[@]}" "$passfile" | sed "1{s/$/$suffix/}" | $BASE64)" || exit $?
 			echo "$pass" | $BASE64 -d
 		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 $?
+			pass="$($GPG -d "${GPG_OPTS[@]}" "$passfile" | tail -n +${selected_line} | head -n 1)$suffix" || 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"
-- 
2.30.2

Reply via email to