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. --- guide/src/chapter_01.md | 16 ++-- ipc/examples/gpg-agent-sign.rs | 23 +++--- ipc/tests/gpg-agent.rs | 9 ++- openpgp-ffi/include/sequoia/openpgp.h | 6 ++ openpgp-ffi/src/serialize.rs | 61 ++++++++------ 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 ++++++++++++++----------------- tool/src/commands/mod.rs | 14 ++-- tool/src/commands/sign.rs | 38 ++++----- tool/tests/sq-sign.rs | 5 +- 15 files changed, 195 insertions(+), 176 deletions(-) diff --git a/guide/src/chapter_01.md b/guide/src/chapter_01.md index fad70067..fcce2101 100644 --- a/guide/src/chapter_01.md +++ b/guide/src/chapter_01.md @@ -52,13 +52,13 @@ fn main() { # // Get the keypair to do the signing from the TPK. # let key : key::UnspecifiedSecret # = tsk.keys_valid().signing_capable().nth(0).unwrap().2.clone().into(); -# let mut keypair = key.into_keypair()?; +# let keypair = key.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)?; @@ -197,13 +197,13 @@ fn generate() -> openpgp::Result { # // Get the keypair to do the signing from the TPK. # let key : key::UnspecifiedSecret # = tsk.keys_valid().signing_capable().nth(0).unwrap().2.clone().into(); -# let mut keypair = key.into_keypair()?; +# let keypair = key.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)?; @@ -342,13 +342,13 @@ fn sign(sink: &mut Write, plaintext: &str, tsk: &openpgp::TPK) // Get the keypair to do the signing from the TPK. let key : key::UnspecifiedSecret = tsk.keys_valid().signing_capable().nth(0).unwrap().2.clone().into(); - let mut keypair = key.into_keypair()?; + let keypair = key.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)?; @@ -498,13 +498,13 @@ Verified data can be read from this using [`io::Read`]. # // Get the keypair to do the signing from the TPK. # let key : key::UnspecifiedSecret # = tsk.keys_valid().signing_capable().nth(0).unwrap().2.clone().into(); -# let mut keypair = key.into_keypair()?; +# let keypair = key.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/ipc/examples/gpg-agent-sign.rs b/ipc/examples/gpg-agent-sign.rs index 1a089622..c4b3d6c0 100644 --- a/ipc/examples/gpg-agent-sign.rs +++ b/ipc/examples/gpg-agent-sign.rs @@ -38,14 +38,11 @@ fn main() { }).collect::>(); // Construct a KeyPair for every signing-capable (sub)key. - let mut keypairs = tpks.iter().flat_map(|tpk| tpk.keys_valid().signing_capable().filter_map(|(_, _, key)| { - KeyPair::new(&ctx, key).ok() - })).collect::>>(); - - // Well, this is awkward... - let signers = keypairs.iter_mut() - .map(|s| -> &mut dyn openpgp::crypto::Signer<_> { s }) - .collect(); + let mut signers = tpks.iter().flat_map(|tpk| { + tpk.keys_valid().signing_capable().filter_map(|(_, _, key)| { + KeyPair::new(&ctx, key).ok() + }) + }).collect::>>(); // Compose a writer stack corresponding to the output format and // packet structure we want. First, we want the output to be @@ -56,9 +53,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, signers, None) - .expect("Failed to create signer"); + // Now, create a signer that emits the signature(s). + let mut signer = + Signer::new(message, signers.pop().expect("No key for signing")); + for s in signers { + 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/ipc/tests/gpg-agent.rs b/ipc/tests/gpg-agent.rs index d707b7b0..dab1fb74 100644 --- a/ipc/tests/gpg-agent.rs +++ b/ipc/tests/gpg-agent.rs @@ -94,7 +94,7 @@ fn sign() { tpk.as_tsk().serialize(&mut buf).unwrap(); gpg_import(&ctx, &buf); - let mut keypair = KeyPair::new( + let keypair = KeyPair::new( &ctx, tpk.keys_valid().signing_capable().take(1).next().unwrap().2) .unwrap(); @@ -104,9 +104,10 @@ fn sign() { let message = Message::new(&mut message); // We want to sign a literal data packet. - let signer = Signer::new(message, vec![&mut keypair], - HashAlgorithm::SHA512) - .unwrap(); + let signer = Signer::new(message, keypair) + // XXX: Is this necessary? If so, it shouldn't. + .hash_algo(HashAlgorithm::SHA512).unwrap() + .build().unwrap(); // Emit a literal data packet. let mut literal_writer = LiteralWriter::new( diff --git a/openpgp-ffi/include/sequoia/openpgp.h b/openpgp-ffi/include/sequoia/openpgp.h index 6aeca8e9..6dadcbc0 100644 --- a/openpgp-ffi/include/sequoia/openpgp.h +++ b/openpgp-ffi/include/sequoia/openpgp.h @@ -1583,6 +1583,12 @@ pgp_writer_stack_t pgp_arbitrary_writer_new (pgp_error_t *errp, /// For every signing key, a signer writes a one-pass-signature /// packet, then hashes and emits the data stream, then for every key /// writes a signature packet. +/// +/// The signers are consumed. +/// +/// The hash is performed using the algorithm specified in +/// `hash_algo`. Pass 0 for the default (which is what you usually +/// want). /*/ pgp_writer_stack_t pgp_signer_new (pgp_error_t *errp, pgp_writer_stack_t inner, diff --git a/openpgp-ffi/src/serialize.rs b/openpgp-ffi/src/serialize.rs index e799888c..2b123673 100644 --- a/openpgp-ffi/src/serialize.rs +++ b/openpgp-ffi/src/serialize.rs @@ -15,7 +15,6 @@ extern crate time; use self::openpgp::constants::{ AEADAlgorithm, - HashAlgorithm, SymmetricAlgorithm, }; @@ -143,6 +142,8 @@ pub extern "C" fn pgp_arbitrary_writer_new /// packet, then hashes and emits the data stream, then for every key /// writes a signature packet. /// +/// The signers are consumed. +/// /// The hash is performed using the algorithm specified in /// `hash_algo`. Pass 0 for the default (which is what you usually /// want). @@ -162,18 +163,23 @@ pub extern "C" fn pgp_signer_new let signers = unsafe { slice::from_raw_parts(signers, signers_len) }; - let signers = signers.into_iter().map( - |s| -> &mut dyn self::openpgp::crypto::Signer<_> { - let signer = *s; - ffi_param_ref_mut!(signer).as_mut() - } - ).collect(); - let hash_algo : Option = if hash_algo == 0 { - None - } else { - Some(hash_algo.into()) - }; - ffi_try_box!(Signer::new(*inner, signers, hash_algo)) + let mut signers = signers.iter().map(|s| { + *ffi_param_move!(*s) + }).collect::>(); + + let mut signer = + Signer::new(*inner, ffi_try!(signers.pop().ok_or_else(|| { + failure::format_err!("signers is empty") + }))); + for s in signers { + signer = signer.add_signer(s); + } + + if hash_algo != 0 { + signer = ffi_try!(signer.hash_algo(hash_algo.into())); + } + + ffi_try_box!(signer.build()) } /// Creates a signer for a detached signature. @@ -195,18 +201,23 @@ pub extern "C" fn pgp_signer_new_detached let signers = unsafe { slice::from_raw_parts(signers, signers_len) }; - let signers = signers.into_iter().map( - |s| -> &mut dyn self::openpgp::crypto::Signer<_> { - let signer = *s; - ffi_param_ref_mut!(signer).as_mut() - } - ).collect(); - let hash_algo : Option = if hash_algo == 0 { - None - } else { - Some(hash_algo.into()) - }; - ffi_try_box!(Signer::detached(*inner, signers, hash_algo)) + let mut signers = signers.iter().map(|s| { + *ffi_param_move!(*s) + }).collect::>(); + + let mut signer = + Signer::new(*inner, ffi_try!(signers.pop().ok_or_else(|| { + failure::format_err!("signers is empty") + }))); + for s in signers { + signer = signer.add_signer(s); + } + + if hash_algo != 0 { + signer = ffi_try!(signer.hash_algo(hash_algo.into())); + } + + ffi_try_box!(signer.detached().build()) } /// Writes a literal data packet. 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(); diff --git a/tool/src/commands/mod.rs b/tool/src/commands/mod.rs index 21521e90..f651756b 100644 --- a/tool/src/commands/mod.rs +++ b/tool/src/commands/mod.rs @@ -148,12 +148,14 @@ pub fn encrypt(mapping: &mut store::Mapping, // Optionally sign message. if ! signers.is_empty() { - sink = Signer::with_intended_recipients( - sink, - signers.iter_mut().map(|s| -> &mut dyn crypto::Signer<_> { s }) - .collect(), - &recipients, - None)?; + let mut signer = Signer::new(sink, signers.pop().unwrap()); + for s in signers { + signer = signer.add_signer(s); + } + for r in recipients { + signer = signer.add_intended_recipient(r); + } + sink = signer.build()?; } let mut literal_writer = LiteralWriter::new(sink, None, None, None) diff --git a/tool/src/commands/sign.rs b/tool/src/commands/sign.rs index 1b2c98b3..51b098b7 100644 --- a/tool/src/commands/sign.rs +++ b/tool/src/commands/sign.rs @@ -6,7 +6,6 @@ use tempfile::NamedTempFile; extern crate sequoia_openpgp as openpgp; use crate::openpgp::armor; -use crate::openpgp::crypto; use crate::openpgp::{Packet, Result}; use crate::openpgp::packet::Signature; use crate::openpgp::parse::{ @@ -82,9 +81,9 @@ fn sign_data(input: &mut dyn io::Read, output_path: Option<&str>, }; let mut keypairs = super::get_signing_keys(&secrets)?; - let signers = keypairs.iter_mut() - .map(|s| -> &mut dyn crypto::Signer<_> { s }) - .collect(); + if keypairs.is_empty() { + return Err(failure::format_err!("No signing keys found")); + } // When extending a detached signature, prepend any existing // signatures first. @@ -95,11 +94,14 @@ fn sign_data(input: &mut dyn io::Read, output_path: Option<&str>, // Stream an OpenPGP message. let sink = Message::new(output); - let signer = if detached { - Signer::detached(sink, signers, None) - } else { - Signer::new(sink, signers, None) - }.context("Failed to create signer")?; + let mut signer = Signer::new(sink, keypairs.pop().unwrap()); + for s in keypairs { + signer = signer.add_signer(s); + } + if detached { + signer = signer.detached(); + } + let signer = signer.build().context("Failed to create signer")?; let mut writer = if detached { // Detached signatures do not need a literal data packet, just @@ -140,13 +142,9 @@ fn sign_message(input: &mut dyn io::Read, output_path: Option<&str>, }; let mut keypairs = super::get_signing_keys(&secrets)?; - // We need to create the signers here, so that we can take() them - // once in the parsing loop. We cannot create the references in - // the loop, because the borrow checker does not understand that - // it happens only once. - let mut signers = Some(keypairs.iter_mut() - .map(|s| -> &mut dyn crypto::Signer<_> { s }) - .collect::>>()); + if keypairs.is_empty() { + return Err(failure::format_err!("No signing keys found")); + } let mut sink = Message::new(output); @@ -212,9 +210,11 @@ fn sign_message(input: &mut dyn io::Read, output_path: Option<&str>, State::AfterFirstSigGroup => { // After the first signature group, we push the signer // onto the writer stack. - let signers = signers.take().expect("only happens once"); - sink = Signer::new(sink, signers, None) - .context("Failed to create signer")?; + let mut signer = Signer::new(sink, keypairs.pop().unwrap()); + for s in keypairs.drain(..) { + signer = signer.add_signer(s); + } + sink = signer.build().context("Failed to create signer")?; state = State::Signing { signature_count: 0, }; }, diff --git a/tool/tests/sq-sign.rs b/tool/tests/sq-sign.rs index 6155190e..3a019459 100644 --- a/tool/tests/sq-sign.rs +++ b/tool/tests/sq-sign.rs @@ -213,10 +213,9 @@ fn sq_sign_append_on_compress_then_sign() { Some(SecretKeyMaterial::Unencrypted(ref u)) => u.clone(), _ => unreachable!(), }; - let mut keypair = KeyPair::new(key.clone(), sec).unwrap(); + let keypair = KeyPair::new(key.clone(), sec).unwrap(); let signer = Signer::new(Message::new(File::create(&sig0).unwrap()), - vec![&mut keypair], None) - .unwrap(); + keypair).build().unwrap(); let compressor = Compressor::new(signer, CompressionAlgorithm::Uncompressed, None) .unwrap(); -- cgit v1.2.3