From contacto at emiliogarcia.com.ar Wed Jun 4 02:17:47 2025 From: contacto at emiliogarcia.com.ar (Emilio Garcia) Date: Tue, 3 Jun 2025 23:17:47 -0300 Subject: [PATCH] Add -e option in show to encrypt clipboard and help Message-ID: <20250604021747.119438-1-contacto@emiliogarcia.com.ar> From: danielemiliogarcia Many modern password managers offer features to protect copied passwords from being exposed via clipboard sniffers. This patch introduces the `-e` flag to the `show` command, which copies the encrypted password to the clipboard instead of the plaintext. This improves security in environments where clipboard sniffing is a concern. It is the responsibility of the receiving application to decrypt the password. The encryption is handled via GPG, using system defaults, which can optionally integrate with hardware security modules (HSMs). I use this feature with a browser extension that performs the decryption. More information: Chrome extension: https://chromewebstore.google.com/detail/clippass/hnpffflijhlfemngdipepjkjkcgfolpf Source code: https://github.com/danielemiliogarcia/clippass Usage demo: https://www.youtube.com/watch?v=kHnr-87B-f0 Signed-off-by: danielemiliogarcia --- src/password-store.sh | 79 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 5 deletions(-) diff --git a/src/password-store.sh b/src/password-store.sh index 22e818f..805282b 100755 --- a/src/password-store.sh +++ b/src/password-store.sh @@ -197,6 +197,72 @@ clip() { echo "Copied $2 to clipboard. Will clear in $CLIP_TIME seconds." } +clipenc() { + local plaintext="$1" + if [[ -z $plaintext ]]; then + die "Error: No plaintext provided for encryption." + fi + + # Determine GPG recipients dynamically + set_gpg_recipients "" # Use the top-level .gpg-id or the appropriate directory + if [[ ${#GPG_RECIPIENTS[@]} -eq 0 ]]; then + die "Error: No GPG recipients found. Please run 'pass init' first." + fi + + # Prepare GPG recipient arguments + local gpg_recipients=("${GPG_RECIPIENT_ARGS[@]}") + + # Handle clipboard tools for Wayland and X11 + if [[ -n $WAYLAND_DISPLAY ]]; then + local copy_enc_cmd=( bash -c "echo -n \"$plaintext\" | gpg --encrypt --armour ${gpg_recipients[*]} | wl-copy" ) + local copy_cmd=( wl-copy ) + local paste_cmd=( wl-paste -n ) + [[ $X_SELECTION == primary ]] && copy_enc_cmd+=( --primary ) && paste_cmd+=( --primary ) + local display_name="$WAYLAND_DISPLAY" + elif [[ -n $DISPLAY ]]; then + local copy_enc_cmd=( bash -c "echo -n \"$plaintext\" | gpg --encrypt --armour ${gpg_recipients[*]} | xclip -selection $X_SELECTION" ) + local copy_cmd=( xclip -selection "$X_SELECTION" ) + local paste_cmd=( xclip -o -selection $X_SELECTION ) + local display_name="$DISPLAY" + else + die "Error: No X11 or Wayland display detected" + fi + + # Prevent conflicts with existing clipboard sleep processes + local sleep_argv0="password store sleep on display $display_name" + pkill -f "^$sleep_argv0" 2>/dev/null && sleep 0.5 + + # Store previous clipboard contents + local before="$("${paste_cmd[@]}" 2>/dev/null | $BASE64)" + + # Execute the copy encrypted command + "${copy_enc_cmd[@]}" || die "Error: Could not encrypt data and copy to the clipboard" + # we can't re-encrypt to compare, cause every encryption will be different as it uses random nonces, so we will backup the value + local what_was_put_in_clip="$("${paste_cmd[@]}" | $BASE64)" + + # Manage clipboard history and auto-clear + # Run in a disowned subprocess that sends outputs to /dev/null + ( + ( exec -a "$sleep_argv0" bash <<<"trap 'kill %1' TERM; sleep '$CLIP_TIME' & wait" ) + local now="$("${paste_cmd[@]}" | $BASE64)" + + if [[ $now != "$what_was_put_in_clip" ]]; then + # Clipboard content changed, so we assume the user has manually copied something else + # Do not restore clipboard content + before="$now" + fi + + # Clear clipboard history (if supported by KDE Klipper) + qdbus org.kde.klipper /klipper org.kde.klipper.klipper.clearClipboardHistory &>/dev/null + + # Restore previous clipboard contents + echo "$before" | $BASE64 -d | "${copy_cmd[@]}" + ) >/dev/null 2>&1 & disown + + echo "Encrypted data copied to clipboard for recipients: ${GPG_RECIPIENTS[*]}" + echo "Will clear in $CLIP_TIME seconds." +} + qrcode() { if [[ -n $DISPLAY || -n $WAYLAND_DISPLAY ]]; then if type feh >/dev/null 2>&1; then @@ -284,8 +350,8 @@ cmd_usage() { List passwords. $PROGRAM find pass-names... List passwords that match pass-names. - $PROGRAM [show] [--clip[=line-number],-c[line-number]] pass-name - Show existing password and optionally put it on the clipboard. + $PROGRAM [show] [--clip[=line-number],-c[line-number]] [--enc[=line-number],-e[line-number]] [--qrcode[=line-number],-q[line-number]] pass-name + Show existing password and optionally: put it on the clipboard or put it encrypted on the clipboard or show it as a QR code. If put on the clipboard, it will be cleared in $CLIP_TIME seconds. $PROGRAM grep [GREPOPTIONS] search-string Search for password files containing search-string when decrypted. @@ -367,23 +433,24 @@ 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::e:: -l qrcode::,clip::,enc:: -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 ;; + -e|--enc) enc=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]] [--enc[=line-number],-e[line-number]] [--qrcode[=line-number],-q[line-number]] [pass-name]" 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 + if [[ $clip -eq 0 && $qrcode -eq 0 && $enc -eq 0 ]]; then pass="$($GPG -d "${GPG_OPTS[@]}" "$passfile" | $BASE64)" || exit $? echo "$pass" | $BASE64 -d else @@ -392,6 +459,8 @@ cmd_show() { [[ -n $pass ]] || die "There is no password to put on the clipboard at line ${selected_line}." if [[ $clip -eq 1 ]]; then clip "$pass" "$path" + elif [[ $enc -eq 1 ]]; then + clipenc "$pass" "$path" elif [[ $qrcode -eq 1 ]]; then qrcode "$pass" "$path" fi -- 2.25.1 From loomis.forrest at gmail.com Thu Jun 5 21:07:17 2025 From: loomis.forrest at gmail.com (Forrest Loomis) Date: Thu, 5 Jun 2025 17:07:17 -0400 Subject: [PATCH] enable storing very long passwords Message-ID: When trying to store an incredibly long JWT token, the token would be truncated. Use readline (read -e) when retrieving the user password. This allows us to input much longer passwords. --- src/password-store.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/password-store.sh b/src/password-store.sh index 22e818f..88b4a1b 100755 --- a/src/password-store.sh +++ b/src/password-store.sh @@ -463,9 +463,9 @@ cmd_insert() { elif [[ $noecho -eq 1 ]]; then local password password_again while true; do - read -r -p "Enter password for $path: " -s password || exit 1 + read -e -r -p "Enter password for $path: " -s password || exit 1 echo - read -r -p "Retype password for $path: " -s password_again || exit 1 + read -e -r -p "Retype password for $path: " -s password_again || exit 1 echo if [[ $password == "$password_again" ]]; then echo "$password" | $GPG -e "${GPG_RECIPIENT_ARGS[@]}" -o "$passfile" "${GPG_OPTS[@]}" || die "Password encryption aborted." @@ -476,7 +476,7 @@ cmd_insert() { done else local password - read -r -p "Enter password for $path: " -e password + read -e -r -p "Enter password for $path: " -e password echo "$password" | $GPG -e "${GPG_RECIPIENT_ARGS[@]}" -o "$passfile" "${GPG_OPTS[@]}" || die "Password encryption aborted." fi git_add_file "$passfile" "Add given password for $path to store." -- 2.49.0 From asmadeus at codewreck.org Thu Jun 5 21:36:32 2025 From: asmadeus at codewreck.org (Dominique Martinet) Date: Fri, 6 Jun 2025 06:36:32 +0900 Subject: xclip -loops for pass In-Reply-To: References: Message-ID: martin f. krafft wrote on Wed, Apr 23, 2025 at 07:21:54PM +0200: > And this made me realise: `pass` uses `xclip` internally, right? Why the 45 > second wait and overwrite? `xclip -loops 1` makes the password available > once exactly, and after a paste, the clipboard is empty. That's a great idea, I've been using a local extension to copy arbitrary lines to clipboard (e.g. with '\nid: ' I'd be able to get the id on clipboard), but getting both in a single command is even better -- I've updated it for that so now I can request `id ''` and get both one after another. I'm not publishing this anywhere because lazy but feel free to use that code, pass is easily extensible so it's probably better as an extension than part of the core. (it's hardcoded wl-copy, the --sensitive flag is not part of any release yet and it is probably required if you have clipboard managers so YMMV) Cheers, -- Dominique Martinet | Asmadeus -------------- next part -------------- #!/bin/bash # License: WTFPL ### Allow copying arbitrary line to clipboard ### install # - copy to ${PASSWORD_STORE_DIR:-~/.password-store}/.extensions/cl.bash # - enable (in bashrc or equivalent): # export PASSWORD_STORE_ENABLE_EXTENSIONS=true # - optionally bash completion: # export PASSWORD_STORE_EXTENSION_COMMANDS+=(cl) # __password_store_extension_complete_cl() { [[ $COMP_CWORD = 2 ]] && _pass_complete_entries; } ### clip_once() { echo "Copying $2 to clipboard. Will clear after one paste" echo -n "$1" | wl-copy --sensitive --paste-once --foreground } cmd_cl_copy() { local pass=$1 shift local full found if [ "$#" = 0 ]; then set -- '' fi full=$(cmd_show "$pass") || exit for line; do if [ -n "$line" ]; then found=$(printf "%s\n" "$full" | sed -n -e 's/ #.*//' -e "s/^${line}[:=]\s*//p") else found=$(printf "%s\n" "$full" | head -n 1) fi if [ -z "$found" ]; then printf "%s\n" "$line not found in pass:" printf "%s\n" "$full" | sed -e 's/^/ /' exit 1 fi clip_once "$found" "'$pass $line'" done } cmd_cl_help() { cat <<-EOF Usage: $PROGRAM cl pass-entry [key] Copy keyed line(s) to clipboard. If no key same as 'pass show -c' EOF exit } case "$1" in --help|-help|-h) cmd_cl_help;; esac cmd_cl_copy "$@" From password-store at city17.xyz Sat Jun 7 16:54:44 2025 From: password-store at city17.xyz (jman) Date: Sat, 07 Jun 2025 18:54:44 +0200 Subject: New project fork? In-Reply-To: <54d257f1-792b-4827-b297-d1d90fc8aea1@c4llv07e.xyz> (c4llv07e's message of "Thu, 22 May 2025 05:28:00 +0000") References: <8qmpdcar.fsf@c4llv07e.xyz> <870d7237-168e-4657-bc21-44c83752ca5f@solidoak.dev> <54d257f1-792b-4827-b297-d1d90fc8aea1@c4llv07e.xyz> Message-ID: <87sekb331n.fsf@nyarlathotep> c4llv07e writes: > There's some bugs and some problems with current implementation, fixing > which wouldn't increase complexity. There's some problems with man page > that were fixed in patch from Dec 2024; bash completion is bugged and > doesn't work with pass-otp. Specifically the issue with bash completion should be fixed by a patch sent on June 2024 https://lists.zx2c4.com/pipermail/password-store/2024-June/004860.html (It doesn't apply cleanly for me, I think it's a diff with broken formatting) Found it patch mentioned in the pass-otp repository https://github.com/tadfisher/pass-otp/issues/137 Regards, From info at rjekker.nl Tue Jun 10 21:18:28 2025 From: info at rjekker.nl (Code Sensei (info)) Date: Tue, 10 Jun 2025 21:18:28 +0000 Subject: [PATCH] emacs: don't decrypt twice Message-ID: <63BB9ED7-081D-48D6-A26E-986888BACEC7@rjekker.nl> password-store-get and password-store-get-field both decrypt the password entry twice when called with a callback, when once would be enough. This is annoying when using a smartcard or yubikey that is configured to wait for user action (e.g. touch activation) before decryption. This patch fixes that. --- contrib/emacs/password-store.el | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/contrib/emacs/password-store.el b/contrib/emacs/password-store= .el index c7cc991..70d46f3 100644 --- a/contrib/emacs/password-store.el +++ b/contrib/emacs/password-store.el @@ -245,12 +245,10 @@ ENTRY is the name of a password-store entry." =20 Returns the first line of the password data. When CALLBACK is non-`NIL', call CALLBACK with the first line instead." - (let* ((inhibit-message t) - (secret (auth-source-pass-get 'secret entry))) + (let ((inhibit-message t) + (secret (auth-source-pass-get 'secret entry))) (if (not callback) secret - (password-store--run-show - entry - (lambda (_) (funcall callback secret)))))) + (funcall callback secret)))) =20 ;;;###autoload (defun password-store-get-field (entry field &optional callback) @@ -259,13 +257,10 @@ FIELD is a string, for instance \"url\". When CALLBA= CK is non-`NIL', call it with the line associated to FIELD instead. If FIELD equals to symbol secret, then this function reduces to `password-store-get'." - (let* ((inhibit-message t) - (secret (auth-source-pass-get field entry))) + (let ((inhibit-message t) + (secret (auth-source-pass-get field entry))) (if (not callback) secret - (password-store--run-show - entry - (lambda (_) (and secret (funcall callback secret))))))) - + (and secret (funcall callback secret))))) =20 ;;;###autoload (defun password-store-clear (&optional field) --=20 2.48.1