From 3d996ff3bc0e939caa6c0edb81c75e763d8c8344 Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Tue, 16 Oct 2018 15:06:10 +0200 Subject: openpgp: Improve the streaming writer API. - Also, improve documentation and doctests. --- ffi/include/sequoia/openpgp.h | 6 +-- ffi/src/openpgp.rs | 10 ++--- openpgp/examples/encrypt-for.rs | 7 +++- openpgp/examples/notarize.rs | 7 +++- openpgp/examples/sign-detached.rs | 7 +++- openpgp/examples/sign.rs | 7 +++- openpgp/examples/wrap-literal.rs | 7 +++- openpgp/src/autocrypt.rs | 5 ++- openpgp/src/parse/stream.rs | 5 ++- openpgp/src/serialize/mod.rs | 2 +- openpgp/src/serialize/stream.rs | 77 ++++++++++++++++++++++++++++----------- tool/src/commands/mod.rs | 13 +++++-- tool/tests/sq-sign.rs | 5 ++- 13 files changed, 105 insertions(+), 53 deletions(-) diff --git a/ffi/include/sequoia/openpgp.h b/ffi/include/sequoia/openpgp.h index 476f76b2..e95ed440 100644 --- a/ffi/include/sequoia/openpgp.h +++ b/ffi/include/sequoia/openpgp.h @@ -947,11 +947,9 @@ sq_status_t sq_packet_parser_decrypt (sq_context_t ctx, typedef struct sq_writer_stack *sq_writer_stack_t; /*/ -/// Wraps a `std::io::Write`r for use with the streaming subsystem. -/// -/// XXX: This interface will likely change. +/// Streams an OpenPGP message. /*/ -sq_writer_stack_t sq_writer_stack_wrap (sq_writer_t writer); +sq_writer_stack_t sq_writer_stack_message (sq_writer_t writer); /*/ /// Writes up to `len` bytes of `buf` into `writer`. diff --git a/ffi/src/openpgp.rs b/ffi/src/openpgp.rs index b2d357e6..6ecc4b78 100644 --- a/ffi/src/openpgp.rs +++ b/ffi/src/openpgp.rs @@ -1524,7 +1524,7 @@ pub extern "system" fn sq_packet_parser_result_eof<'a> use self::openpgp::serialize::{ writer, stream::{ - wrap, + Message, Cookie, ArbitraryWriter, Signer, @@ -1535,11 +1535,9 @@ use self::openpgp::serialize::{ }; -/// Wraps a `std::io::Write`r for use with the streaming subsystem. -/// -/// XXX: This interface will likely change. +/// Streams an OpenPGP message. #[no_mangle] -pub extern "system" fn sq_writer_stack_wrap +pub extern "system" fn sq_writer_stack_message (writer: *mut Box) -> *mut writer::Stack<'static, Cookie> { @@ -1547,7 +1545,7 @@ pub extern "system" fn sq_writer_stack_wrap let writer = unsafe { Box::from_raw(writer) }; - box_raw!(wrap(writer)) + box_raw!(Message::new(writer)) } /// Writes up to `len` bytes of `buf` into `writer`. diff --git a/openpgp/examples/encrypt-for.rs b/openpgp/examples/encrypt-for.rs index a5d7150a..bc70e307 100644 --- a/openpgp/examples/encrypt-for.rs +++ b/openpgp/examples/encrypt-for.rs @@ -8,7 +8,7 @@ extern crate openpgp; use openpgp::armor; use openpgp::constants::DataFormat; use openpgp::serialize::stream::{ - wrap, LiteralWriter, Encryptor, EncryptionMode, + Message, LiteralWriter, Encryptor, EncryptionMode, }; fn main() { @@ -45,8 +45,11 @@ fn main() { let sink = armor::Writer::new(io::stdout(), armor::Kind::Message, &[][..]) .expect("Failed to create an armored writer"); + // Stream an OpenPGP message. + let message = Message::new(sink); + // We want to encrypt a literal data packet. - let encryptor = Encryptor::new(wrap(sink), + let encryptor = Encryptor::new(message, &[], // No symmetric encryption. &recipients, mode) diff --git a/openpgp/examples/notarize.rs b/openpgp/examples/notarize.rs index c26e79af..6643e573 100644 --- a/openpgp/examples/notarize.rs +++ b/openpgp/examples/notarize.rs @@ -12,7 +12,7 @@ use openpgp::{ parse::PacketParserResult, serialize::Serialize, }; -use openpgp::serialize::stream::{wrap, LiteralWriter, Signer}; +use openpgp::serialize::stream::{Message, LiteralWriter, Signer}; fn main() { let args: Vec = env::args().collect(); @@ -38,9 +38,12 @@ fn main() { let sink = armor::Writer::new(io::stdout(), armor::Kind::Message, &[][..]) .expect("Failed to create an armored writer."); + // Stream an OpenPGP message. + let message = Message::new(sink); + // Now, create a signer that emits a detached signature. let mut signer = Signer::new( - wrap(sink), &tsks.iter().collect::>()) + message, &tsks.iter().collect::>()) .expect("Failed to create signer"); // Create a parser for the message to be notarized. diff --git a/openpgp/examples/sign-detached.rs b/openpgp/examples/sign-detached.rs index fca195b0..bc071ba6 100644 --- a/openpgp/examples/sign-detached.rs +++ b/openpgp/examples/sign-detached.rs @@ -5,7 +5,7 @@ use std::io; extern crate openpgp; use openpgp::armor; -use openpgp::serialize::stream::{wrap, Signer}; +use openpgp::serialize::stream::{Message, Signer}; fn main() { let args: Vec = env::args().collect(); @@ -31,9 +31,12 @@ fn main() { let sink = armor::Writer::new(io::stdout(), armor::Kind::Signature, &[][..]) .expect("Failed to create armored writer."); + // Stream an OpenPGP message. + let message = Message::new(sink); + // Now, create a signer that emits a detached signature. let mut signer = Signer::detached( - wrap(sink), &tsks.iter().collect::>()) + message, &tsks.iter().collect::>()) .expect("Failed to create signer"); // Copy all the data. diff --git a/openpgp/examples/sign.rs b/openpgp/examples/sign.rs index 03117ed5..6b05a08c 100644 --- a/openpgp/examples/sign.rs +++ b/openpgp/examples/sign.rs @@ -6,7 +6,7 @@ use std::io; extern crate openpgp; use openpgp::armor; use openpgp::constants::DataFormat; -use openpgp::serialize::stream::{wrap, LiteralWriter, Signer}; +use openpgp::serialize::stream::{Message, LiteralWriter, Signer}; fn main() { let args: Vec = env::args().collect(); @@ -32,9 +32,12 @@ fn main() { let sink = armor::Writer::new(io::stdout(), armor::Kind::Message, &[][..]) .expect("Failed to create an armored writer."); + // Stream an OpenPGP message. + let message = Message::new(sink); + // Now, create a signer that emits a signature. let signer = Signer::new( - wrap(sink), &tsks.iter().collect::>()) + message, &tsks.iter().collect::>()) .expect("Failed to create signer"); // Then, create a literal writer to wrap the data in a literal diff --git a/openpgp/examples/wrap-literal.rs b/openpgp/examples/wrap-literal.rs index b7cea74e..9d9560e3 100644 --- a/openpgp/examples/wrap-literal.rs +++ b/openpgp/examples/wrap-literal.rs @@ -9,7 +9,7 @@ use std::io; extern crate openpgp; use openpgp::armor; use openpgp::constants::DataFormat; -use openpgp::serialize::stream::{wrap, LiteralWriter}; +use openpgp::serialize::stream::{Message, LiteralWriter}; fn main() { let args: Vec = env::args().collect(); @@ -24,9 +24,12 @@ fn main() { let sink = armor::Writer::new(io::stdout(), armor::Kind::Message, &[][..]) .expect("Failed to create armored writer."); + // Stream an OpenPGP message. + let message = Message::new(sink); + // Then, create a literal writer to wrap the data in a literal // message packet. - let mut literal = LiteralWriter::new(wrap(sink), DataFormat::Binary, + let mut literal = LiteralWriter::new(message, DataFormat::Binary, None, None) .expect("Failed to create literal writer"); diff --git a/openpgp/src/autocrypt.rs b/openpgp/src/autocrypt.rs index 68f670fc..da2fba6d 100644 --- a/openpgp/src/autocrypt.rs +++ b/openpgp/src/autocrypt.rs @@ -31,7 +31,7 @@ use parse::{ PacketParserResult, PacketParser, }; use serialize::stream::{ - wrap, LiteralWriter, Encryptor, EncryptionMode, + Message, LiteralWriter, Encryptor, EncryptionMode, }; use constants::DataFormat; use Password; @@ -399,7 +399,8 @@ impl AutocryptSetupMessage { let w = armor::Writer::new(w, armor::Kind::Message, &headers[..])?; // Passphrase-Format header with value numeric9x4 - let w = Encryptor::new(wrap(w), + let m = Message::new(w); + let w = Encryptor::new(m, &[ self.passcode.as_ref().unwrap() ], &[], EncryptionMode::ForTransport)?; diff --git a/openpgp/src/parse/stream.rs b/openpgp/src/parse/stream.rs index 3532eabb..d7c64d4b 100644 --- a/openpgp/src/parse/stream.rs +++ b/openpgp/src/parse/stream.rs @@ -1068,7 +1068,7 @@ mod test { fn verify_long_message() { use constants::DataFormat; use tpk::{TPKBuilder, CipherSuite}; - use serialize::stream::{LiteralWriter, Signer, wrap}; + use serialize::stream::{LiteralWriter, Signer, Message}; use std::io::Write; let tpk = TPKBuilder::default() @@ -1079,7 +1079,8 @@ mod test { // sign 30MiB message let mut buf = vec![]; { - let signer = Signer::new(wrap(&mut buf), &[&tpk]).unwrap(); + let m = Message::new(&mut buf); + let signer = Signer::new(m, &[&tpk]).unwrap(); let mut ls = LiteralWriter::new(signer, DataFormat::Binary, None, None).unwrap(); ls.write_all(&mut vec![42u8; 30 * 1024 * 1024]).unwrap(); diff --git a/openpgp/src/serialize/mod.rs b/openpgp/src/serialize/mod.rs index e5259db3..bf7c7080 100644 --- a/openpgp/src/serialize/mod.rs +++ b/openpgp/src/serialize/mod.rs @@ -915,7 +915,7 @@ impl Serialize for CompressedData { self.common.body.as_ref().map(|body| body.len())); } - let o = stream::wrap(o); + let o = stream::Message::new(o); let mut o = stream::Compressor::new(o, self.algo)?; // Serialize the packets. diff --git a/openpgp/src/serialize/stream.rs b/openpgp/src/serialize/stream.rs index 0d509f04..d7a633d6 100644 --- a/openpgp/src/serialize/stream.rs +++ b/openpgp/src/serialize/stream.rs @@ -1,4 +1,13 @@ //! Streaming packet serialization. +//! +//! This is the preferred interface to generate OpenPGP messages. It +//! takes advantage of OpenPGP's streaming nature to avoid unnecessary +//! buffering. This interface provides a convenient, yet low-level +//! way to sign or encrypt. +//! +//! See the [encryption example]. +//! +//! [encryption example]: struct.Encryptor.html#example use std::fmt; use std::io::{self, Write}; @@ -65,13 +74,18 @@ impl Cookie { } } -/// Wraps a `std::io::Write`r for use with the streaming subsystem. +/// Streams an OpenPGP message. /// -/// XXX: This interface will likely change. -pub fn wrap<'a, W: 'a + io::Write>(w: W) -> writer::Stack<'a, Cookie> { - writer::Generic::new(w, Cookie::new(0)) +/// Wraps a `std::io::Write`r for use with the streaming subsystem. +pub struct Message { } +impl Message { + /// Streams an OpenPGP message. + pub fn new<'a, W: 'a + io::Write>(w: W) -> writer::Stack<'a, Cookie> { + writer::Generic::new(w, Cookie::new(0)) + } +} impl<'a> From<&'a mut io::Write> for writer::Stack<'a, Cookie> { fn from(w: &'a mut io::Write) -> Self { @@ -91,13 +105,13 @@ impl<'a> From<&'a mut io::Write> for writer::Stack<'a, Cookie> { /// ``` /// use std::io::Write; /// use openpgp::packet::Tag; -/// use openpgp::serialize::stream::{wrap, ArbitraryWriter}; +/// use openpgp::serialize::stream::{Message, ArbitraryWriter}; /// # use openpgp::Result; /// # f().unwrap(); /// # fn f() -> Result<()> { /// let mut o = vec![]; /// { -/// let mut w = ArbitraryWriter::new(wrap(&mut o), Tag::Literal)?; +/// let mut w = ArbitraryWriter::new(Message::new(&mut o), Tag::Literal)?; /// w.write_all(b"t")?; // type /// w.write_all(b"\x00")?; // filename length /// w.write_all(b"\x00\x00\x00\x00")?; // date @@ -198,16 +212,18 @@ impl<'a> Signer<'a> { /// ``` /// use std::io::Write; /// use openpgp::constants::DataFormat; - /// use openpgp::serialize::stream::{wrap, Signer, LiteralWriter}; + /// use openpgp::serialize::stream::{Message, Signer, LiteralWriter}; /// # use openpgp::{Result, TPK}; /// # let tsk = TPK::from_bytes(include_bytes!( /// # "../../tests/data/keys/testy-new-private.pgp")) /// # .unwrap(); /// # f(tsk).unwrap(); /// # fn f(tsk: TPK) -> Result<()> { + /// /// let mut o = vec![]; /// { - /// let signer = Signer::new(wrap(&mut o), &[&tsk])?; + /// let message = Message::new(&mut o); + /// let signer = Signer::new(message, &[&tsk])?; /// let mut ls = LiteralWriter::new(signer, DataFormat::Text, None, None)?; /// ls.write_all(b"Make it so, number one!")?; /// ls.finalize()?; @@ -241,16 +257,18 @@ impl<'a> Signer<'a> { /// /// ``` /// use std::io::Write; - /// use openpgp::serialize::stream::{wrap, Signer, LiteralWriter}; + /// use openpgp::serialize::stream::{Message, Signer, LiteralWriter}; /// # use openpgp::{Result, TPK}; /// # let tsk = TPK::from_bytes(include_bytes!( /// # "../../tests/data/keys/testy-new-private.pgp")) /// # .unwrap(); /// # f(tsk).unwrap(); /// # fn f(tsk: TPK) -> Result<()> { + /// /// let mut o = vec![]; /// { - /// let mut signer = Signer::detached(wrap(&mut o), &[&tsk])?; + /// let message = Message::new(&mut o); + /// let mut signer = Signer::detached(message, &[&tsk])?; /// signer.write_all(b"Make it so, number one!")?; /// // In reality, just io::copy() the file to be signed. /// signer.finalize()?; @@ -491,14 +509,17 @@ impl<'a> writer::Stackable<'a, Cookie> for Signer<'a> { /// ``` /// use std::io::Write; /// use openpgp::constants::DataFormat; -/// use openpgp::serialize::stream::{wrap, LiteralWriter}; +/// use openpgp::serialize::stream::{Message, LiteralWriter}; /// # use openpgp::Result; /// # f().unwrap(); /// # fn f() -> Result<()> { +/// /// let mut o = vec![]; /// { -/// let mut w = LiteralWriter::new(wrap(&mut o), DataFormat::Text, None, None)?; +/// let message = Message::new(&mut o); +/// let mut w = LiteralWriter::new(message, DataFormat::Text, None, None)?; /// w.write_all(b"Hello world.")?; +/// w.finalize()?; /// } /// assert_eq!(b"\xcb\x12t\x00\x00\x00\x00\x00Hello world.", o.as_slice()); /// # Ok(()) @@ -652,17 +673,20 @@ impl<'a> writer::Stackable<'a, Cookie> for LiteralWriter<'a> { /// ``` /// use std::io::Write; /// use openpgp::constants::DataFormat; -/// use openpgp::serialize::stream::{wrap, Compressor, LiteralWriter}; +/// use openpgp::serialize::stream::{Message, Compressor, LiteralWriter}; /// use openpgp::constants::CompressionAlgorithm; /// # use openpgp::Result; /// # f().unwrap(); /// # fn f() -> Result<()> { +/// /// let mut o = vec![]; /// { -/// let w = Compressor::new(wrap(&mut o), +/// let message = Message::new(&mut o); +/// let w = Compressor::new(message, /// CompressionAlgorithm::Uncompressed)?; /// let mut w = LiteralWriter::new(w, DataFormat::Text, None, None)?; /// w.write_all(b"Hello world.")?; +/// w.finalize()?; /// } /// assert_eq!(b"\xc8\x15\x00\xcb\x12t\x00\x00\x00\x00\x00Hello world.", /// o.as_slice()); @@ -798,7 +822,7 @@ impl<'a> Encryptor<'a> { /// #[macro_use] extern crate openpgp; // For armored! /// use openpgp::constants::DataFormat; /// use openpgp::serialize::stream::{ - /// wrap, Encryptor, EncryptionMode, LiteralWriter, + /// Message, Encryptor, EncryptionMode, LiteralWriter, /// }; /// # use openpgp::Result; /// # fn main() { f().unwrap(); } @@ -840,14 +864,17 @@ impl<'a> Encryptor<'a> { /// -----END PGP PUBLIC KEY BLOCK-----" /// # */ /// )).unwrap(); + /// /// let mut o = vec![]; - /// let encryptor = Encryptor::new(wrap(&mut o), + /// let message = Message::new(&mut o); + /// let encryptor = Encryptor::new(message, /// &[&"совершенно секретно".into()], /// &[&tpk], /// EncryptionMode::AtRest) /// .expect("Failed to create encryptor"); /// let mut w = LiteralWriter::new(encryptor, DataFormat::Text, None, None)?; /// w.write_all(b"Hello world.")?; + /// w.finalize()?; /// # Ok(()) /// # } /// ``` @@ -1121,7 +1148,8 @@ mod test { fn arbitrary() { let mut o = vec![]; { - let mut ustr = ArbitraryWriter::new(wrap(&mut o), Tag::Literal).unwrap(); + let m = Message::new(&mut o); + let mut ustr = ArbitraryWriter::new(m, Tag::Literal).unwrap(); ustr.write_all(b"t").unwrap(); // type ustr.write_all(b"\x00").unwrap(); // fn length ustr.write_all(b"\x00\x00\x00\x00").unwrap(); // date @@ -1170,8 +1198,9 @@ mod test { let mut o = vec![]; { + let m = Message::new(&mut o); let c = Compressor::new( - wrap(&mut o), CompressionAlgorithm::Uncompressed).unwrap(); + m, CompressionAlgorithm::Uncompressed).unwrap(); let mut ls = LiteralWriter::new(c, T, None, None).unwrap(); write!(ls, "one").unwrap(); let c = ls.finalize_one().unwrap().unwrap(); // Pop the LiteralWriter. @@ -1228,8 +1257,9 @@ mod test { let mut o = vec![]; { + let m = Message::new(&mut o); let c0 = Compressor::new( - wrap(&mut o), CompressionAlgorithm::Uncompressed).unwrap(); + m, CompressionAlgorithm::Uncompressed).unwrap(); let c = Compressor::new( c0, CompressionAlgorithm::Uncompressed).unwrap(); let mut ls = LiteralWriter::new(c, T, None, None).unwrap(); @@ -1266,7 +1296,8 @@ mod test { zeros.resize(4 * 1024, 0); let mut o = vec![]; { - let c = Compressor::new(wrap(&mut o), + let m = Message::new(&mut o); + let c = Compressor::new(m, CompressionAlgorithm::BZip2).unwrap(); let mut ls = LiteralWriter::new(c, T, None, None).unwrap(); // Write 64 megabytes of zeroes. @@ -1290,8 +1321,9 @@ mod test { let mut o = vec![]; { + let m = Message::new(&mut o); let signer = Signer::new( - wrap(&mut o), + m, &tsks.iter().map(|(_, tsk)| tsk).collect::>()) .unwrap(); let mut ls = LiteralWriter::new(signer, T, None, None).unwrap(); @@ -1326,8 +1358,9 @@ mod test { // Write a simple encrypted message... let mut o = vec![]; { + let m = Message::new(&mut o); let encryptor = Encryptor::new( - wrap(&mut o), &passwords.iter().collect::>(), + m, &passwords.iter().collect::>(), &[], EncryptionMode::ForTransport) .unwrap(); let mut literal = LiteralWriter::new(encryptor, DataFormat::Binary, diff --git a/tool/src/commands/mod.rs b/tool/src/commands/mod.rs index d7e0637c..1d1af435 100644 --- a/tool/src/commands/mod.rs +++ b/tool/src/commands/mod.rs @@ -20,7 +20,7 @@ use openpgp::parse::stream::{ }; use openpgp::serialize::Serialize; use openpgp::serialize::stream::{ - wrap, Signer, LiteralWriter, Encryptor, EncryptionMode, + Message, Signer, LiteralWriter, Encryptor, EncryptionMode, }; extern crate sequoia_store as store; @@ -61,8 +61,11 @@ pub fn encrypt(store: &mut store::Store, let passwords_: Vec<&openpgp::Password> = passwords.iter().collect(); + // Stream an OpenPGP message. + let message = Message::new(output); + // We want to encrypt a literal data packet. - let mut sink = Encryptor::new(wrap(output), + let mut sink = Encryptor::new(message, &passwords_, &recipients, EncryptionMode::AtRest) @@ -156,7 +159,9 @@ fn sign_data(input: &mut io::Read, output_path: Option<&str>, sig.serialize(&mut output)?; } - let sink = wrap(output); + // Stream an OpenPGP message. + let sink = Message::new(output); + // Build a vector of references to hand to Signer. let keys: Vec<&openpgp::TPK> = secrets.iter().collect(); let signer = if detached { @@ -202,7 +207,7 @@ fn sign_message(input: &mut io::Read, output_path: Option<&str>, output }; - let mut sink = wrap(output); + let mut sink = Message::new(output); // Build a vector of references to hand to Signer. let keys: Vec<&openpgp::TPK> = secrets.iter().collect(); diff --git a/tool/tests/sq-sign.rs b/tool/tests/sq-sign.rs index e69420f3..0f2e5e40 100644 --- a/tool/tests/sq-sign.rs +++ b/tool/tests/sq-sign.rs @@ -9,7 +9,7 @@ use tempfile::TempDir; extern crate openpgp; use openpgp::{Packet, PacketPile, Reader, TPK}; use openpgp::constants::{CompressionAlgorithm, DataFormat, SignatureType}; -use openpgp::serialize::stream::{wrap, Signer, Compressor, LiteralWriter}; +use openpgp::serialize::stream::{Message, Signer, Compressor, LiteralWriter}; fn p(filename: &str) -> String { format!("../openpgp/tests/data/{}", filename) @@ -208,7 +208,8 @@ fn sq_sign_append_on_compress_then_sign() { // message by foot. let tsk = TPK::from_file(&p("keys/dennis-simon-anton-private.pgp")) .unwrap(); - let signer = Signer::new(wrap(File::create(&sig0).unwrap()), &[&tsk]) + let signer = Signer::new(Message::new(File::create(&sig0).unwrap()), + &[&tsk]) .unwrap(); let compressor = Compressor::new(signer, CompressionAlgorithm::Uncompressed) .unwrap(); -- cgit v1.2.3