From 1ddc1dd61b45b41801c1d1c364cd6789314cb8f3 Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Tue, 19 Nov 2019 15:50:23 +0100 Subject: openpgp: Use the builder pattern for stream::Signer. - See #375. --- openpgp/examples/generate-sign-verify.rs | 4 +- openpgp/examples/notarize.rs | 16 ++-- openpgp/examples/sign-detached.rs | 15 ++-- openpgp/examples/sign.rs | 16 ++-- openpgp/src/crypto/asymmetric.rs | 11 +++ openpgp/src/parse/stream.rs | 5 +- openpgp/src/serialize/stream.rs | 132 ++++++++++++++----------------- 7 files changed, 99 insertions(+), 100 deletions(-) (limited to 'openpgp') diff --git a/openpgp/examples/generate-sign-verify.rs b/openpgp/examples/generate-sign-verify.rs index bddacc71..e22e5760 100644 --- a/openpgp/examples/generate-sign-verify.rs +++ b/openpgp/examples/generate-sign-verify.rs @@ -40,14 +40,14 @@ fn generate() -> openpgp::Result { fn sign(sink: &mut dyn Write, plaintext: &str, tsk: &openpgp::TPK) -> openpgp::Result<()> { // Get the keypair to do the signing from the TPK. - let mut keypair = tsk.keys_valid().signing_capable().nth(0).unwrap().2 + let keypair = tsk.keys_valid().signing_capable().nth(0).unwrap().2 .clone().mark_parts_secret().into_keypair()?; // Start streaming an OpenPGP message. let message = Message::new(sink); // We want to sign a literal data packet. - let signer = Signer::new(message, vec![&mut keypair], None)?; + let signer = Signer::new(message, keypair).build()?; // Emit a literal data packet. let mut literal_writer = LiteralWriter::new(signer, None, None, None)?; diff --git a/openpgp/examples/notarize.rs b/openpgp/examples/notarize.rs index eedcc01e..c627d4f1 100644 --- a/openpgp/examples/notarize.rs +++ b/openpgp/examples/notarize.rs @@ -7,7 +7,6 @@ use std::io; extern crate sequoia_openpgp as openpgp; use crate::openpgp::{ armor, - crypto, Packet, parse::{Parse, PacketParserResult}, serialize::Serialize, @@ -60,14 +59,13 @@ fn main() { // Stream an OpenPGP message. let message = Message::new(sink); - // Now, create a signer that emits a detached signature. - let mut signer = Signer::new( - message, - keys.iter_mut() - .map(|s| -> &mut dyn crypto::Signer<_> { s }) - .collect(), - None) - .expect("Failed to create signer"); + // Now, create a signer that emits the signature(s). + let mut signer = + Signer::new(message, keys.pop().expect("No key for signing")); + for s in keys { + signer = signer.add_signer(s); + } + let mut signer = signer.build().expect("Failed to create signer"); // Create a parser for the message to be notarized. let mut input = io::stdin(); diff --git a/openpgp/examples/sign-detached.rs b/openpgp/examples/sign-detached.rs index d4d8d40d..98202bd2 100644 --- a/openpgp/examples/sign-detached.rs +++ b/openpgp/examples/sign-detached.rs @@ -6,7 +6,6 @@ extern crate rpassword; extern crate sequoia_openpgp as openpgp; use crate::openpgp::armor; -use crate::openpgp::crypto; use crate::openpgp::parse::Parse; use crate::openpgp::serialize::stream::{Message, Signer}; @@ -56,12 +55,14 @@ fn main() { // Stream an OpenPGP message. let message = Message::new(sink); - // Now, create a signer that emits a detached signature. - let mut signer = Signer::detached( - message, - keys.iter_mut().map(|s| -> &mut dyn crypto::Signer<_> { s }).collect(), - None) - .expect("Failed to create signer"); + // Now, create a signer that emits the detached signature(s). + let mut signer = + Signer::new(message, keys.pop().expect("No key for signing")); + for s in keys { + signer = signer.add_signer(s); + } + let mut signer = + signer.detached().build().expect("Failed to create signer"); // Copy all the data. io::copy(&mut io::stdin(), &mut signer) diff --git a/openpgp/examples/sign.rs b/openpgp/examples/sign.rs index 7a7bcd91..2304865f 100644 --- a/openpgp/examples/sign.rs +++ b/openpgp/examples/sign.rs @@ -5,7 +5,6 @@ use std::io; extern crate sequoia_openpgp as openpgp; use crate::openpgp::armor; -use crate::openpgp::crypto; use crate::openpgp::parse::Parse; use crate::openpgp::serialize::stream::{Message, LiteralWriter, Signer}; @@ -55,14 +54,13 @@ fn main() { // Stream an OpenPGP message. let message = Message::new(sink); - // Now, create a signer that emits a signature. - let signer = Signer::new( - message, - keys.iter_mut() - .map(|s| -> &mut dyn crypto::Signer<_> { s }) - .collect(), - None) - .expect("Failed to create signer"); + // Now, create a signer that emits the signature(s). + let mut signer = + Signer::new(message, keys.pop().expect("No key for signing")); + for s in keys { + signer = signer.add_signer(s); + } + let signer = signer.build().expect("Failed to create signer"); // Then, create a literal writer to wrap the data in a literal // message packet. diff --git a/openpgp/src/crypto/asymmetric.rs b/openpgp/src/crypto/asymmetric.rs index 9232bd14..144bcc4c 100644 --- a/openpgp/src/crypto/asymmetric.rs +++ b/openpgp/src/crypto/asymmetric.rs @@ -27,6 +27,17 @@ pub trait Signer -> Result; } +impl Signer for Box> { + fn public(&self) -> &Key { + self.as_ref().public() + } + + fn sign(&mut self, hash_algo: HashAlgorithm, digest: &[u8]) + -> Result { + self.as_mut().sign(hash_algo, digest) + } +} + /// Decrypts a message. /// /// This is a low-level mechanism to decrypt an arbitrary OpenPGP diff --git a/openpgp/src/parse/stream.rs b/openpgp/src/parse/stream.rs index 3f33e833..fff01171 100644 --- a/openpgp/src/parse/stream.rs +++ b/openpgp/src/parse/stream.rs @@ -1900,10 +1900,11 @@ mod test { let mut buf = vec![]; { let key = tpk.keys_all().signing_capable().nth(0).unwrap().2; - let mut keypair = key.clone().mark_parts_secret().into_keypair().unwrap(); + let keypair = + key.clone().mark_parts_secret().into_keypair().unwrap(); let m = Message::new(&mut buf); - let signer = Signer::new(m, vec![&mut keypair], None).unwrap(); + let signer = Signer::new(m, keypair).build().unwrap(); let mut ls = LiteralWriter::new(signer, None, None, None).unwrap(); ls.write_all(&mut vec![42u8; 30 * 1024 * 1024]).unwrap(); diff --git a/openpgp/src/serialize/stream.rs b/openpgp/src/serialize/stream.rs index 9d386675..38c14fa7 100644 --- a/openpgp/src/serialize/stream.rs +++ b/openpgp/src/serialize/stream.rs @@ -205,8 +205,8 @@ pub struct Signer<'a> { // take our inner reader. If that happens, we only update the // digests. inner: Option>, - signers: Vec<&'a mut dyn crypto::Signer>, - intended_recipients: Option>, + signers: Vec + 'a>>, + intended_recipients: Vec, detached: bool, hash: crypto::hash::Context, cookie: Cookie, @@ -239,7 +239,7 @@ impl<'a> Signer<'a> { /// let mut o = vec![]; /// { /// let message = Message::new(&mut o); - /// let signer = Signer::new(message, vec![&mut signing_keypair], None)?; + /// let signer = Signer::new(message, signing_keypair).build()?; /// let mut ls = LiteralWriter::new(signer, None, None, None)?; /// ls.write_all(b"Make it so, number one!")?; /// ls.finalize()?; @@ -273,34 +273,48 @@ impl<'a> Signer<'a> { /// # Ok(()) /// # } /// ``` - pub fn new(inner: writer::Stack<'a, Cookie>, - signers: Vec<&'a mut dyn crypto::Signer< - key::UnspecifiedRole>>, - hash_algo: H) - -> Result> - where H: Into> + pub fn new(inner: writer::Stack<'a, Cookie>, signer: S) -> Self + where S: crypto::Signer + 'a { - Self::make(inner, signers, None, false, - hash_algo.into().unwrap_or_default()) + let inner = writer::BoxStack::from(inner); + let level = inner.cookie_ref().level + 1; + Signer { + inner: Some(inner), + signers: vec![Box::new(signer)], + intended_recipients: Vec::new(), + detached: false, + hash: HashAlgorithm::default().context().unwrap(), + cookie: Cookie { + level: level, + private: Private::Signer, + }, + position: 0, + } } - /// Creates a signer with intended recipients. + /// Sets the hash algorithm to use for the signatures. + pub fn hash_algo(mut self, algo: HashAlgorithm) -> Result { + self.hash = algo.context()?; + Ok(self) + } + + /// Adds an additional signer. + pub fn add_signer(mut self, signer: S) -> Self + where S: crypto::Signer + 'a + { + self.signers.push(Box::new(signer)); + self + } + + /// Adds an intended recipient. /// /// This signer emits signatures indicating the intended /// recipients of the encryption container containing the /// signature. This prevents forwarding a signed message using a /// different encryption context. - pub fn with_intended_recipients(inner: writer::Stack<'a, Cookie>, - signers: Vec<&'a mut dyn crypto::Signer< - key::UnspecifiedRole>>, - recipients: &[&'a TPK], - hash_algo: H) - -> Result> - where H: Into> - { - Self::make(inner, signers, - Some(recipients.iter().map(|r| r.fingerprint()).collect()), - false, hash_algo.into().unwrap_or_default()) + pub fn add_intended_recipient(mut self, recipient: &TPK) -> Self { + self.intended_recipients.push(recipient.fingerprint()); + self } /// Creates a signer for a detached signature. @@ -329,7 +343,7 @@ impl<'a> Signer<'a> { /// { /// let message = Message::new(&mut o); /// let mut signer = - /// Signer::detached(message, vec![&mut signing_keypair], None)?; + /// Signer::new(message, signing_keypair).detached().build()?; /// signer.write_all(b"Make it so, number one!")?; /// // In reality, just io::copy() the file to be signed. /// signer.finalize()?; @@ -365,56 +379,33 @@ impl<'a> Signer<'a> { /// # Ok(()) /// # } /// ``` - pub fn detached(inner: writer::Stack<'a, Cookie>, - signers: Vec<&'a mut dyn crypto::Signer>, - hash_algo: H) - -> Result> - where H: Into> - { - Self::make(inner, signers, None, true, - hash_algo.into().unwrap_or_default()) + pub fn detached(mut self) -> Self { + self.detached = true; + self } - fn make(inner: writer::Stack<'a, Cookie>, - signers: Vec<&'a mut dyn crypto::Signer>, - intended_recipients: Option>, detached: bool, - hash_algo: HashAlgorithm) - -> Result> + /// Finalizes the signer, returning the writer stack. + pub fn build(mut self) -> Result> { - let mut inner = writer::BoxStack::from(inner); - - if signers.len() == 0 { - return Err(Error::InvalidArgument( - "No signing keys given".into()).into()); - } + assert!(self.signers.len() > 0, "The constructor adds a signer."); + assert!(self.inner.is_some(), "The constructor adds an inner writer."); - if ! detached { + if ! self.detached { // For every key we collected, build and emit a one pass // signature packet. - for (i, keypair) in signers.iter().enumerate() { + for (i, keypair) in self.signers.iter().enumerate() { let key = keypair.public(); let mut ops = OnePassSig3::new(SignatureType::Binary); ops.set_pk_algo(key.pk_algo()); - ops.set_hash_algo(hash_algo); + ops.set_hash_algo(self.hash.algo()); ops.set_issuer(key.keyid()); - ops.set_last(i == signers.len() - 1); - Packet::OnePassSig(ops.into()).serialize(&mut inner)?; + ops.set_last(i == self.signers.len() - 1); + Packet::OnePassSig(ops.into()) + .serialize(self.inner.as_mut().unwrap())?; } } - let level = inner.cookie_ref().level + 1; - Ok(writer::Stack::from(Box::new(Signer { - inner: Some(inner), - signers: signers, - intended_recipients: intended_recipients, - detached: detached, - hash: hash_algo.context()?, - cookie: Cookie { - level: level, - private: Private::Signer, - }, - position: 0, - }))) + Ok(writer::Stack::from(Box::new(self))) } fn emit_signatures(&mut self) -> Result<()> { @@ -435,12 +426,13 @@ impl<'a> Signer<'a> { // Issuer subpacket to be present. .set_issuer(signer.public().keyid())?; - if let Some(ref ir) = self.intended_recipients { - sig = sig.set_intended_recipients(ir.clone())?; + if ! self.intended_recipients.is_empty() { + sig = sig.set_intended_recipients( + self.intended_recipients.clone())?; } // Compute the signature. - let sig = sig.sign_hash(*signer, self.hash.algo(), hash)?; + let sig = sig.sign_hash(signer.as_mut(), self.hash.algo(), hash)?; // And emit the packet. Packet::Signature(sig).serialize(sink)?; @@ -1405,13 +1397,11 @@ mod test { }).collect::>>(); let m = Message::new(&mut o); - let signer = Signer::new( - m, - signers.iter_mut() - .map(|s| -> &mut dyn crypto::Signer<_> {s}) - .collect(), - None) - .unwrap(); + let mut signer = Signer::new(m, signers.pop().unwrap()); + for s in signers.into_iter() { + signer = signer.add_signer(s); + } + let signer = signer.build().unwrap(); let mut ls = LiteralWriter::new(signer, None, None, None).unwrap(); ls.write_all(b"Tis, tis, tis. Tis is important.").unwrap(); let signer = ls.finalize_one().unwrap().unwrap(); -- cgit v1.2.3