Skip to content

Commit

Permalink
redwood: Improve writing of encrypted files
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
legoktm committed Oct 13, 2023
1 parent d6ab762 commit 02cdf2f
Showing 1 changed file with 20 additions and 11 deletions.
31 changes: 20 additions & 11 deletions redwood/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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()?;
Expand All @@ -168,6 +174,7 @@ fn encrypt(

// Flush any remaining buffers
message.finalize()?;
writer.flush()?;

Ok(())
}
Expand Down Expand Up @@ -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 🦀";
Expand Down Expand Up @@ -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
Expand All @@ -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);
Expand Down Expand Up @@ -310,15 +317,17 @@ mod tests {
let (_public_key3, secret_key3, _) =
generate_source_key_pair(PASSPHRASE, "[email protected]").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
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 02cdf2f

Please sign in to comment.