From: Duncan Burke <[email protected]> In the case where an identity has multiple subkeys for encryption, reencryption was partially broken. To prevent unnecessary reencryption, the current keys for a file are compared with the expected keys for a given gpg id. This requires the means to translate a gpg id into a normalised long keyid.
By default, passing the id of a particular subkey as a recipient does not necessarily cause gpg to encrypt with that key, rather, gpg takes the parent identity and chooses a subkey independently. From the code in the function gpgsm_add_to_certlist (gnupg/sm/certlist.c), it appears gpg simply takes the last subkey in the list with the encryption flag set. gpg can be forced to use a specific encryption key by appending '!' to the end of the key id. For example: pub 4096R/4A241115 2014-04-26 [expires: 2018-09-18] sub 4096R/D132D31E 2014-09-19 sub 2048R/8CD8DCB7 2014-09-19 Both D132D31E and 8CD8DCB7 have the encryption flag set. $ pass init 4A241115 $ pass generate foo 12 $ pass generate dir/bar 12 $ gpg -v --list-only .password-store/foo.gpg Version: GnuPG v2 gpg: armor header: gpg: public key is 8CD8DCB7 $ pass init D132D31E Password store initialized for D132D31E $ pass init D132D31E! Password store initialized for D132D31E! dir/bar: reencrypting to 2698F2A9D132D31E foo: reencrypting to 2698F2A9D132D31E $ pass init -p dir D132D31E! 8CD8DCB7! Password store initialized for D132D31E!, 8CD8DCB7! dir/bar: reencrypting to 2698F2A9D132D31E 71B10D108CD8DCB7 $ pass generate dir/foobar 12 $ gpg -v --list-only .password-store/dir/foobar.gpg Version: GnuPG v2 gpg: armor header: gpg: public key is D132D31E gpg: public key is 8CD8DCB7 Signed-off-by: Duncan Burke <[email protected]> --- src/password-store.sh | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/password-store.sh b/src/password-store.sh index 2287a46..cf57fd3 100755 --- a/src/password-store.sh +++ b/src/password-store.sh @@ -108,7 +108,20 @@ reencrypt_path() { IFS=";" eval 'GPG_RECIPIENTS+=( $group )' # http://unix.stackexchange.com/a/92190 unset GPG_RECIPIENTS[$index] done - gpg_keys="$($GPG --list-keys --keyid-format long "${GPG_RECIPIENTS[@]}" | sed -n 's/sub *.*\/\([A-F0-9]\{16\}\) .*/\1/p' | LC_ALL=C sort -u)" + gpg_keys="" + for index in "${!GPG_RECIPIENTS[@]}"; do + local key_list="$GPG --list-keys --keyid-format long --with-colons ${GPG_RECIPIENTS[$index]}" + if [[ ${GPG_RECIPIENTS[$index]: -1} == "!" ]]; then + # The exact subkey is specified + local gpg_id="$(tr '[:lower:]' '[:upper:]' <<< ${GPG_RECIPIENTS[index]%!})" + gpg_keys+=$(${key_list} | sed -n "s/^sub:\\([^:]*:\\)\\{3\\}\\([^:]*${gpg_id}\\):.*$/\\2 /p" | head -n 1) + else + # gnupg takes the last subkey with encryption enabled + # see gpgsm_add_to_certlist (gnupg/sm/certlist.c) + gpg_keys+=$(${key_list} | sed -n "s/^sub:\\([^:]*:\\)\\{3\\}\\([^:]*\\):\\([^:]*:\\)\\{6\\}e:$/\\2 /p" | tail -n 1) + fi + done + gpg_keys="$(printf "%s\n" ${gpg_keys[*]} | LC_ALL=C sort -u)" fi current_keys="$($GPG -v --no-secmem-warning --no-permission-warning --list-only --keyid-format long "$passfile" 2>&1 | cut -d ' ' -f 5 | LC_ALL=C sort -u)" -- 2.0.0 _______________________________________________ Password-Store mailing list [email protected] http://lists.zx2c4.com/mailman/listinfo/password-store
