From 745bcf6d8fc49df0e9dabcc7c5a76826f2a1757e Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Mon, 13 Jul 2026 10:33:55 +0200 Subject: [PATCH] Backport fix for password file usage during decryption https://gitlab.com/sequoia-pgp/sequoia-sq/-/work_items/563 Related: RHEL-171291 --- rust-sequoia-sq.spec | 3 + sequoia-sq-decrypt-password.patch | 197 ++++++++++++++++++++++++++++++ 2 files changed, 200 insertions(+) create mode 100644 sequoia-sq-decrypt-password.patch diff --git a/rust-sequoia-sq.spec b/rust-sequoia-sq.spec index 4d88d47..420c995 100644 --- a/rust-sequoia-sq.spec +++ b/rust-sequoia-sq.spec @@ -40,6 +40,9 @@ Source3: vendor.toml Patch: sequoia-sq-fix-metadata.diff # Backward comatibility for algorithm names from pre-release snapshot Patch1: rust-sequoia-sq-mldsa-alias.patch +# b8498a61d397bee6dc5574d44041a263a5a5c0ad +# 7f929fc58f9ec97f409a165904fc606b579ee49c +Patch2: sequoia-sq-decrypt-password.patch %if 0%{?rhel} diff --git a/sequoia-sq-decrypt-password.patch b/sequoia-sq-decrypt-password.patch new file mode 100644 index 0000000..1650562 --- /dev/null +++ b/sequoia-sq-decrypt-password.patch @@ -0,0 +1,197 @@ +From b8498a61d397bee6dc5574d44041a263a5a5c0ad Mon Sep 17 00:00:00 2001 +From: "Neal H. Walfield" +Date: Fri, 18 Apr 2025 12:36:29 +0200 +Subject: [PATCH] Change try_decrypt to take a reference to the key. + + - `Helper::try_decrypt` only needs a reference to the key. + + - Change it to take a reference instead of taking ownership of the + key. +--- + src/commands/decrypt.rs | 13 ++++++------- + 1 file changed, 6 insertions(+), 7 deletions(-) + +diff --git a/src/commands/decrypt.rs b/src/commands/decrypt.rs +index 70091912..ef453743 100644 +--- a/src/commands/decrypt.rs ++++ b/src/commands/decrypt.rs +@@ -8,7 +8,7 @@ use sequoia_openpgp as openpgp; + use openpgp::types::SymmetricAlgorithm; + use openpgp::fmt::hex; + use openpgp::KeyHandle; +-use openpgp::crypto::{self, SessionKey, Decryptor}; ++use openpgp::crypto::{self, SessionKey}; + use openpgp::{Fingerprint, Cert, KeyID, Result}; + use openpgp::packet; + use openpgp::packet::prelude::*; +@@ -187,7 +187,7 @@ impl<'c, 'store, 'rstore> Helper<'c, 'store, 'rstore> + /// to decrypt the packet parser using `decrypt`. + fn try_decrypt(&self, pkesk: &PKESK, + sym_algo: Option, +- mut keypair: Box, ++ keypair: &mut dyn crypto::Decryptor, + decrypt: &mut dyn FnMut(Option, &SessionKey) -> bool) + -> Option> + { +@@ -277,10 +277,10 @@ impl<'c, 'store, 'rstore> DecryptionHelper for Helper<'c, 'store, 'rstore> + slf.vhelper.sq.decrypt_key(Some(cert), key.clone(), prompt, true) + .ok() + .and_then(|key| { +- let keypair = Box::new(key.into_keypair() +- .expect("decrypted secret key material")); ++ let mut keypair = key.into_keypair() ++ .expect("decrypted secret key material"); + +- slf.try_decrypt(pkesk, sym_algo, keypair, decrypt) ++ slf.try_decrypt(pkesk, sym_algo, &mut keypair, decrypt) + }) + }; + +@@ -409,9 +409,8 @@ impl<'c, 'store, 'rstore> DecryptionHelper for Helper<'c, 'store, 'rstore> + } + } + +- let keypair = Box::new(key); + if let Some(fp) = self.try_decrypt( +- &pkesk, sym_algo, keypair, decrypt) ++ &pkesk, sym_algo, &mut key, decrypt) + { + return Ok(fp); + } else { +-- +2.55.0 + + + +From 7f929fc58f9ec97f409a165904fc606b579ee49c Mon Sep 17 00:00:00 2001 +From: "Neal H. Walfield" +Date: Fri, 18 Apr 2025 12:38:32 +0200 +Subject: [PATCH] Use the password cache to unlock keys on the softkey backend. + + - `--password-file` can be used to seed the password cache. + + - Currently, the password cache is not used to unlock keys on the + keystore, because the keys may be protected by a retry counter, + and we can't be sure that the passwords in the cache should be + used for any given key. + + - We know, however, that keys managed by the softkeys backend are + never protected by a retry counter. As such, if a key is managed + by the softkey backend, try the password cache. + + - If the password cache is not empty, and a key is managed by + another backend, print a warning that the password cache is + being ignored. + + - Fixes #563. +--- + src/commands/decrypt.rs | 81 ++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 80 insertions(+), 1 deletion(-) + +diff --git a/src/commands/decrypt.rs b/src/commands/decrypt.rs +index ef453743..086482ca 100644 +--- a/src/commands/decrypt.rs ++++ b/src/commands/decrypt.rs +@@ -364,6 +364,22 @@ impl<'c, 'store, 'rstore> DecryptionHelper for Helper<'c, 'store, 'rstore> + Err(err) => { + match err.downcast() { + Ok(keystore::Error::InaccessibleDecryptionKey(keys)) => { ++ // Get a reference to the softkeys backend. ++ let mut softkeys = if let Ok(backends) = ks.backends() { ++ let mut softkeys = None; ++ for mut backend in backends.into_iter() { ++ if let Ok(id) = backend.id() { ++ if id == "softkeys" { ++ softkeys = Some(backend); ++ break; ++ } ++ } ++ } ++ softkeys ++ } else { ++ None ++ }; ++ + for key_status in keys.into_iter() { + let pkesk = key_status.pkesk().clone(); + let mut key = key_status.into_key(); +@@ -375,11 +391,74 @@ impl<'c, 'store, 'rstore> DecryptionHelper for Helper<'c, 'store, 'rstore> + .set_transport_encryption(), + true); + ++ // If we have any cached ++ // passwords, and the key is not ++ // protected by a retry counter, ++ // try the cached passwords. ++ // ++ // Right now,we only try the ++ // password cache with keys ++ // managed by the softkeys ++ // backend, which we know are not ++ // protected by a retry counter. ++ // It would be better to query the ++ // key, but the key store doesn't ++ // expose that yet information yet ++ // so we use this heuristic for ++ // now. ++ let password_cache ++ = self.sq.password_cache.lock().unwrap(); ++ if ! password_cache.is_empty() { ++ // There's currently no way to ++ // go from a key handle to the ++ // backend. ++ let mut on_softkeys = false; ++ if let Some(softkeys) = softkeys.as_mut() { ++ let devices = softkeys.devices(); ++ if let Ok(devices) = devices { ++ for mut device in devices.into_iter() { ++ let keys = device.keys(); ++ if let Ok(keys) = keys { ++ for mut a_key in keys.into_iter() { ++ if let Ok(a_id) = a_key.id() { ++ if key.id().ok() == Some(a_id) { ++ // Same id. We have a match. ++ on_softkeys = true; ++ break; ++ } ++ } ++ } ++ } ++ } ++ } ++ } ++ ++ if on_softkeys { ++ for password in password_cache.iter() { ++ if let Ok(()) = key.unlock(password.clone()) { ++ if let Some(fp) = self.try_decrypt( ++ &pkesk, sym_algo, &mut key, decrypt) ++ { ++ return Ok(fp); ++ } ++ } ++ } ++ } else { ++ eprintln!( ++ "{}, {} is locked, but not \ ++ trying cached passwords, \ ++ because the key may be \ ++ protected by a retry counter.", ++ keyid, userid.display()); ++ } ++ } ++ drop(password_cache); ++ + loop { + if self.sq.batch { + eprintln!( + "{}, {} is locked, but not \ +- prompting for password, \ ++ prompting for a password, \ + because you passed --batch.", + keyid, userid.display()); + break; +-- +GitLab + +