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