From d6ab76262f81724b831a61333782bb5e1e2341dd Mon Sep 17 00:00:00 2001 From: Kunal Mehta Date: Thu, 12 Oct 2023 15:18:02 -0400 Subject: [PATCH 1/3] redwood: Directly get a key's key ID Fixes #6992. --- redwood/src/decryption.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/redwood/src/decryption.rs b/redwood/src/decryption.rs index 4def5602b7..089b1fdc38 100644 --- a/redwood/src/decryption.rs +++ b/redwood/src/decryption.rs @@ -7,7 +7,6 @@ use sequoia_openpgp::crypto::{Password, SessionKey}; use sequoia_openpgp::parse::stream::*; use sequoia_openpgp::policy::Policy; use sequoia_openpgp::types::SymmetricAlgorithm; -use sequoia_openpgp::KeyID; pub(crate) struct Helper<'a> { pub(crate) policy: &'a dyn Policy, @@ -51,7 +50,7 @@ impl<'a> DecryptionHelper for Helper<'a> { for pkesk in pkesks { // Note: this check won't work for messages encrypted with --throw-keyids, // but we don't generate any messages that use it. - if pkesk.recipient() == &KeyID::from(key.fingerprint()) { + if pkesk.recipient() == &key.keyid() { // Decrypt the secret key with the specified passphrase. let mut pair = key .clone() From 02cdf2f0ecda29bb784a7ca8101b6ed8c95d4c26 Mon Sep 17 00:00:00 2001 From: Kunal Mehta Date: Thu, 12 Oct 2023 15:44:59 -0400 Subject: [PATCH 2/3] redwood: Improve writing of encrypted files First, use "create_new" mode when opening the file so if it already exists, an error will be raised instead of silently truncating the existing file. This required adjusting our tests a bit since NamedTempFile creates a file before we try writing to it. Then use a BufWriter to buffer filesystem writes instead of writing each byte as it comes in. Fixes #6989. Fixes #6990. --- redwood/src/lib.rs | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/redwood/src/lib.rs b/redwood/src/lib.rs index 1c11de5a34..03abe47074 100644 --- a/redwood/src/lib.rs +++ b/redwood/src/lib.rs @@ -14,7 +14,7 @@ use sequoia_openpgp::serialize::{ use sequoia_openpgp::Cert; use std::borrow::Cow; use std::fs::File; -use std::io::{self, Read}; +use std::io::{self, BufWriter, Read, Write}; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::string::FromUtf8Error; @@ -157,8 +157,14 @@ fn encrypt( // In reverse order, we set up a writer that will write an encrypted and // armored message to a newly-created file at `destination`. - let mut sink = File::create(destination)?; - let message = Message::new(&mut sink); + // TODO: Use `File::create_new()` once it's stabilized: https://github.com/rust-lang/rust/issues/105135 + let sink = File::options() + .read(true) + .write(true) + .create_new(true) + .open(destination)?; + let mut writer = BufWriter::new(sink); + let message = Message::new(&mut writer); let message = Armorer::new(message).build()?; let message = Encryptor::for_recipients(message, recipient_keys).build()?; let mut message = LiteralWriter::new(message).build()?; @@ -168,6 +174,7 @@ fn encrypt( // Flush any remaining buffers message.finalize()?; + writer.flush()?; Ok(()) } @@ -204,7 +211,7 @@ pub fn decrypt( mod tests { use super::*; use sequoia_openpgp::Cert; - use tempfile::NamedTempFile; + use tempfile::TempDir; const PASSPHRASE: &str = "correcthorsebatterystaple"; const SECRET_MESSAGE: &str = "Rust is great 🦀"; @@ -261,7 +268,7 @@ mod tests { // Attempting to encrypt to multiple recipients should fail if any one of them // has no supported keys - let tmp = NamedTempFile::new().unwrap(); + let tmp_dir = TempDir::new().unwrap(); let expected_err_msg = format!( "No supported keys for certificate {}", BAD_KEY_FINGERPRINT @@ -270,7 +277,7 @@ mod tests { let err = encrypt_message( vec![good_key, BAD_KEY.to_string()], SECRET_MESSAGE.to_string(), - tmp.path().to_path_buf(), + tmp_dir.path().join("message.asc"), ) .unwrap_err(); assert_eq!(err.to_string(), expected_err_msg); @@ -310,15 +317,17 @@ mod tests { let (_public_key3, secret_key3, _) = generate_source_key_pair(PASSPHRASE, "foo3@example.org").unwrap(); - let tmp = NamedTempFile::new().unwrap(); + let tmp_dir = TempDir::new().unwrap(); + let tmp = tmp_dir.path().join("message.asc"); + println!("{}", tmp.to_string_lossy()); // Encrypt a message to keys 1 and 2 but not 3 encrypt_message( vec![public_key1, public_key2], SECRET_MESSAGE.to_string(), - tmp.path().to_path_buf(), + tmp.clone(), ) .unwrap(); - let ciphertext = std::fs::read_to_string(tmp.path()).unwrap(); + let ciphertext = std::fs::read_to_string(tmp).unwrap(); // Verify ciphertext looks like an encrypted message assert!(ciphertext.starts_with("-----BEGIN PGP MESSAGE-----\n")); // Decrypt as key 1 @@ -376,12 +385,12 @@ mod tests { "OpenPGP error: unexpected EOF", ), ]; - let tmp: NamedTempFile = NamedTempFile::new().unwrap(); + let tmp_dir = TempDir::new().unwrap(); for (key, error) in bad_keys { let err = encrypt_message( key, // missing or malformed recipient key "Look ma, no key".to_string(), - tmp.path().to_path_buf(), + tmp_dir.path().join("message.asc"), ) .unwrap_err(); assert_eq!(err.to_string(), error); From 814a37cc2640dc8afc71dc4338516211ece8a32d Mon Sep 17 00:00:00 2001 From: Kunal Mehta Date: Thu, 12 Oct 2023 15:50:05 -0400 Subject: [PATCH 3/3] redwood: Correctly check for secret key material We were not checking if any subkeys had secret key material too, which is_tsk() checks for us. Fixes #6988. --- redwood/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redwood/src/lib.rs b/redwood/src/lib.rs index 03abe47074..9f38e34737 100644 --- a/redwood/src/lib.rs +++ b/redwood/src/lib.rs @@ -105,7 +105,7 @@ pub fn is_valid_public_key(input: &str) -> Result { // We don't need the keys, just need to check there's at least one and no error keys::keys_from_cert(POLICY, &cert)?; // And there is no secret key material - if cert.keys().secret().next().is_some() { + if cert.is_tsk() { return Err(Error::HasSecretKeyMaterial); } Ok(cert.fingerprint().to_string())