From baa33deeb67bf9ca6771b3be6a56bce018c5702c Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Wed, 25 Mar 2020 15:35:48 +0100 Subject: openpgp: Improve performance of detached signature verification. - Previously, we transformed data and detached signatures into signed messages on the fly, then used the streaming Verifier to verify the message. However, this introduces a nontrivial overhead, even if unnecessary copies are carefully avoided. - Instead, specialize the streaming Decryptor to handle detached signatures. use crypto::hash_buffered_reader to compute the hashes over the data, then attach the computed signatures to the signature packets, and use Decryptor's verification machinery. - While this is arguably less elegant, it is much simpler, and a lot faster. Notably, if we operate on files and can mmap them into memory, we can compute the hash in one call to the compression function. Verification of detached signatures is an important use case, so this speedup outweighs the loss of elegance. - Fixes #457. --- openpgp-ffi/include/sequoia/openpgp.h | 24 +- openpgp-ffi/include/sequoia/openpgp/types.h | 5 + openpgp-ffi/src/parse/stream.rs | 48 ++- openpgp/src/parse/stream.rs | 467 +++++++++------------------- openpgp/src/policy.rs | 13 +- openpgp/src/serialize/stream.rs | 7 +- sqv/src/sqv.rs | 7 +- tool/src/commands/mod.rs | 14 +- 8 files changed, 215 insertions(+), 370 deletions(-) diff --git a/openpgp-ffi/include/sequoia/openpgp.h b/openpgp-ffi/include/sequoia/openpgp.h index 5f612720..20b1f150 100644 --- a/openpgp-ffi/include/sequoia/openpgp.h +++ b/openpgp-ffi/include/sequoia/openpgp.h @@ -1856,21 +1856,27 @@ pgp_reader_t pgp_verifier_new (pgp_error_t *errp, void *cookie, time_t time); /*/ -/// Verifies a detached OpenPGP signature./// -/// A Certificate (see [RFC 4880, section 11.1]) can be used to verify -/// signatures and encrypt data. It can be stored in a keystore and -/// uploaded to keyservers. -/// -/// [RFC 4880, section 11.1]: https://tools.ietf.org/html/rfc4880#section-11.1 - +/// Verifies a detached OpenPGP signature. /*/ -pgp_reader_t pgp_detached_verifier_new (pgp_error_t *errp, +pgp_detached_verifier_t pgp_detached_verifier_new (pgp_error_t *errp, pgp_policy_t policy, - pgp_reader_t signature_input, pgp_reader_t input, + pgp_reader_t signature_input, pgp_decryptor_get_public_keys_cb_t get_public_keys, pgp_decryptor_check_cb_t check, void *cookie, time_t time); +/*/ +/// Frees this object. +/*/ +void pgp_detached_verifier_free (pgp_detached_verifier_t); + +/*/ +/// Verifies `data` using `verifier`. +/*/ +pgp_status_t pgp_detached_verifier_verify (pgp_error_t *errp, + pgp_detached_verifier_t verifier, + pgp_reader_t data); + /*/ /// Returns a new standard policy. /*/ diff --git a/openpgp-ffi/include/sequoia/openpgp/types.h b/openpgp-ffi/include/sequoia/openpgp/types.h index ec883df6..14c4ef08 100644 --- a/openpgp-ffi/include/sequoia/openpgp/types.h +++ b/openpgp-ffi/include/sequoia/openpgp/types.h @@ -533,6 +533,11 @@ typedef pgp_status_t (*pgp_decryptor_check_cb_t) (void *, typedef pgp_status_t (*pgp_decryptor_inspect_cb_t) (void *, pgp_packet_parser_t); +/*/ +/// Verifies a detached signature. +/*/ +typedef struct pgp_detached_verifier *pgp_detached_verifier_t; + /*/ /// An OpenPGP policy. /*/ diff --git a/openpgp-ffi/src/parse/stream.rs b/openpgp-ffi/src/parse/stream.rs index 66e277ff..86a3a307 100644 --- a/openpgp-ffi/src/parse/stream.rs +++ b/openpgp-ffi/src/parse/stream.rs @@ -30,7 +30,6 @@ use self::openpgp::parse::stream::{ Decryptor, VerificationHelper, Verifier, - DetachedVerifier, }; use crate::Maybe; @@ -634,6 +633,14 @@ fn pgp_verifier_new<'a>(errp: Option<&mut *mut crate::error::Error>, .move_into_raw(errp) } +/// Verifies a detached signature. +/// +/// Wraps [`sequoia-openpgp::parse::stream::DetachedVerifier`]. +/// +/// [`sequoia-openpgp::parse::stream::DetachedVerifier`]: ../../../../sequoia_openpgp/parse/stream/struct.DetachedVerifier.html +#[crate::ffi_wrapper_type(prefix = "pgp_")] +pub struct DetachedVerifier(openpgp::parse::stream::DetachedVerifier<'static, VHelper>); + /// Verifies a detached OpenPGP signature. /// /// # Example @@ -697,12 +704,11 @@ fn pgp_verifier_new<'a>(errp: Option<&mut *mut crate::error::Error>, /// int /// main (int argc, char **argv) /// { +/// pgp_status_t rc; /// pgp_cert_t cert; /// pgp_reader_t signature; /// pgp_reader_t source; -/// pgp_reader_t plaintext; -/// uint8_t buf[128]; -/// ssize_t nread; +/// pgp_detached_verifier_t verifier; /// pgp_policy_t policy = pgp_standard_policy (); /// /// cert = pgp_cert_from_file (NULL, @@ -721,17 +727,15 @@ fn pgp_verifier_new<'a>(errp: Option<&mut *mut crate::error::Error>, /// struct verify_cookie cookie = { /// .key = cert, /* Move. */ /// }; -/// plaintext = pgp_detached_verifier_new (NULL, policy, signature, source, +/// verifier = pgp_detached_verifier_new (NULL, policy, signature, /// get_public_keys_cb, check_cb, /// &cookie, 1554542219); -/// assert (source); +/// assert (verifier); /// -/// nread = pgp_reader_read (NULL, plaintext, buf, sizeof buf); -/// assert (nread >= 42); -/// assert ( -/// memcmp (buf, "A Cypherpunk's Manifesto\nby Eric Hughes\n", 40) == 0); +/// rc = pgp_detached_verifier_verify (NULL, verifier, source); +/// assert (rc == PGP_STATUS_SUCCESS); /// -/// pgp_reader_free (plaintext); +/// pgp_detached_verifier_free (verifier); /// pgp_reader_free (source); /// pgp_reader_free (signature); /// pgp_policy_free (policy); @@ -742,23 +746,35 @@ fn pgp_verifier_new<'a>(errp: Option<&mut *mut crate::error::Error>, fn pgp_detached_verifier_new<'a>(errp: Option<&mut *mut crate::error::Error>, policy: *const Policy, signature_input: *mut io::Reader, - input: *mut io::Reader, get_public_keys: GetPublicKeysCallback, check: CheckCallback, cookie: *mut HelperCookie, time: time_t) - -> Maybe + -> Maybe { let policy = policy.ref_raw().as_ref(); let helper = VHelper::new(get_public_keys, check, cookie); - DetachedVerifier::from_reader(policy, signature_input.ref_mut_raw(), - input.ref_mut_raw(), helper, maybe_time(time)) - .map(|r| io::ReaderKind::Generic(Box::new(r))) + openpgp::parse::stream::DetachedVerifier::from_reader( + policy, signature_input.ref_mut_raw(), helper, maybe_time(time)) .move_into_raw(errp) } +/// Verifies `data` using `verifier`. +#[::sequoia_ffi_macros::extern_fn] #[no_mangle] pub extern "C" +fn pgp_detached_verifier_verify(errp: Option<&mut *mut crate::error::Error>, + verifier: *mut DetachedVerifier, + data: *mut io::Reader) + -> Status +{ + ffi_make_fry_from_errp!(errp); + ffi_try_or_status!( + verifier.ref_mut_raw().verify_reader(data.ref_mut_raw())); + Status::Success +} + + struct DHelper { vhelper: VHelper, diff --git a/openpgp/src/parse/stream.rs b/openpgp/src/parse/stream.rs index 3e695420..e57f68d0 100644 --- a/openpgp/src/parse/stream.rs +++ b/openpgp/src/parse/stream.rs @@ -10,7 +10,6 @@ //! [verification example]: struct.Verifier.html#example use std::cmp; -use std::convert::TryFrom; use std::io::{self, Read}; use std::path::Path; use std::time; @@ -22,20 +21,14 @@ use crate::{ types::{ AEADAlgorithm, CompressionAlgorithm, - DataFormat, RevocationStatus, SymmetricAlgorithm, }, packet::{ - header::BodyLength, - header::CTB, key, - Literal, OnePassSig, - one_pass_sig::OnePassSig3, PKESK, SKESK, - Tag, }, KeyID, Packet, @@ -44,7 +37,6 @@ use crate::{ packet::Signature, cert::prelude::*, crypto::SessionKey, - serialize::Marshal, policy::Policy, }; use crate::parse::{ @@ -634,7 +626,8 @@ impl<'a, H: VerificationHelper> Verifier<'a, H> { { Ok(Verifier { decryptor: Decryptor::from_buffered_reader( - policy, bio, NoDecryptionHelper { v: helper, }, time, false)?, + policy, bio, NoDecryptionHelper { v: helper, }, time, + Mode::Verify)?, }) } } @@ -645,218 +638,6 @@ impl<'a, H: VerificationHelper> io::Read for Verifier<'a, H> { } } -/// Transforms a detached signature and content into a signed message -/// on the fly. -struct Transformer<'a> { - state: TransformationState, - sigs: Vec, - reader: Box + 'a>, - - /// We need to buffer some data. This is the buffer, and the - /// offset of unread bytes in the buffer. - buffer: Vec, - cursor: usize, -} - -#[derive(PartialEq, Debug)] -enum TransformationState { - Data, - Sigs, - Done, -} - -impl<'a> Transformer<'a> { - fn new<'b>(signatures: Box + 'b>, - mut data: Box + 'a>) - -> Result> - { - let mut sigs = Vec::new(); - - // Gather signatures. - let mut ppr = PacketParser::from_buffered_reader(signatures)?; - while let PacketParserResult::Some(pp) = ppr { - let (packet, ppr_) = pp.next()?; - ppr = ppr_; - - match packet { - Packet::Signature(sig) => sigs.push(sig), - _ => return Err(Error::InvalidArgument( - format!("Not a signature packet: {:?}", - packet.tag())).into()), - } - } - - let mut buf = Vec::new(); - for (i, sig) in sigs.iter().rev().enumerate() { - let mut ops = OnePassSig3::try_from(sig)?; - if i == sigs.len() - 1 { - ops.set_last(true); - } - - Packet::OnePassSig(ops.into()).serialize(&mut buf)?; - } - - // We need to decide whether to use partial body encoding or - // not. For partial body encoding, the first chunk must be at - // least 512 bytes long. Try to read 512 - HEADER_LEN bytes - // from data. - let state = { - const HEADER_LEN: usize = 6; - let data_prefix = data.data_consume(512 - HEADER_LEN)?; - if data_prefix.len() < 512 - HEADER_LEN { - // Too little data for a partial body encoding, produce a - // Literal Data Packet header of known length. - CTB::new(Tag::Literal).serialize(&mut buf)?; - - let len = BodyLength::Full((data_prefix.len() + HEADER_LEN) as u32); - len.serialize(&mut buf)?; - - let lit = Literal::new(DataFormat::Binary); - lit.serialize_headers(&mut buf, false)?; - - // Copy the data, then proceed directly to the signatures. - buf.extend_from_slice(data_prefix); - TransformationState::Sigs - } else { - // Produce a Literal Data Packet header with partial - // length encoding. - CTB::new(Tag::Literal).serialize(&mut buf)?; - - let len = BodyLength::Partial(512); - len.serialize(&mut buf)?; - - let lit = Literal::new(DataFormat::Binary); - lit.serialize_headers(&mut buf, false)?; - - // Copy the prefix up to the first chunk, then keep in the - // data state. - buf.extend_from_slice(&data_prefix[..512 - HEADER_LEN]); - TransformationState::Data - } - }; - - Ok(Self { - state: state, - sigs: sigs, - reader: data, - buffer: buf, - cursor: 0, - }) - } - - fn read_helper(&mut self, mut buf: &mut [u8]) -> Result { - // Keep track of the bytes written into `buf`. - let mut bytes_read = 0; - - if self.cursor >= self.buffer.len() { - // We have exhausted the buffered data. Reset length and - // offset. - crate::vec_truncate(&mut self.buffer, 0); - self.cursor = 0; - - self.state = match self.state { - TransformationState::Data => { - // Find the largest power of two equal or smaller - // than the size of buf. - let mut s = buf.len().next_power_of_two(); - if ! buf.len().is_power_of_two() { - s >>= 1; - } - - // Cap it. Drop once we avoid the copies below. - const MAX_CHUNK_SIZE: usize = 1 << 22; // 4 megabytes. - if s > MAX_CHUNK_SIZE { - s = MAX_CHUNK_SIZE; - } - - assert!(s <= ::std::u32::MAX as usize); - - // Try to read that amount into the buffer. - let data = self.reader.data_consume(s)?; - let mut data = &data[..cmp::min(s, data.len())]; - - // Short read? The end is nigh. - let short_read = data.len() < s; - let len = if short_read { - BodyLength::Full(data.len() as u32) - } else { - BodyLength::Partial(data.len() as u32) - }; - len.serialize(&mut self.buffer)?; - // Offset into `self.buffer`. - let mut off = self.buffer.len(); - - // Try to copy the length directly into the read - // buffer. - if off < buf.len() { - &mut buf[..off].copy_from_slice(&self.buffer[..off]); - buf = &mut buf[off..]; - bytes_read += off; - off = 0; - - // Try to copy as much as possible of `data` into - // the read buffer. - let n = cmp::min(buf.len(), data.len()); - &mut buf[..n].copy_from_slice(&data[..n]); - data = &data[n..]; - bytes_read += n; - } - - // Copy the rest. - // XXX: Could avoid the copy here. - self.buffer.resize(off + data.len(), 0); - &mut self.buffer[off..].copy_from_slice(data); - - if short_read { - TransformationState::Sigs - } else { - TransformationState::Data - } - }, - - TransformationState::Sigs => { - for sig in self.sigs.drain(..) { - Packet::Signature(sig).serialize(&mut self.buffer)?; - } - - TransformationState::Done - }, - - TransformationState::Done => - TransformationState::Done, - }; - } - - if bytes_read > 0 { - // We (partially?) satisfied the read request. Return - // now, and leave `self.buffer` for the next read - // invocation. - return Ok(bytes_read); - } - - assert!(self.cursor <= self.buffer.len()); - let n = cmp::min(buf.len(), self.buffer.len() - self.cursor); - &mut buf[..n] - .copy_from_slice(&self.buffer[self.cursor..n + self.cursor]); - self.cursor += n; - Ok(n) - } -} - -impl<'a> io::Read for Transformer<'a> { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - match self.read_helper(buf) { - Ok(n) => Ok(n), - Err(e) => match e.downcast::() { - // An io::Error. Pass as-is. - Ok(e) => Err(e), - // A failure. Wrap it. - Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)), - }, - } - } -} - /// Verifies a detached signature. /// @@ -904,26 +685,25 @@ impl<'a> io::Read for Transformer<'a> { /// /// let data = b"Hello World!"; /// let h = Helper {}; -/// let mut v = DetachedVerifier::from_bytes(p, signature, data, h, None)?; -/// -/// let mut content = Vec::new(); -/// v.read_to_end(&mut content)?; -/// assert_eq!(content, b"Hello World!"); +/// let mut v = DetachedVerifier::from_bytes(p, signature, h, None)?; +/// v.verify_bytes(data)?; /// # Ok(()) /// # } -pub struct DetachedVerifier { +pub struct DetachedVerifier<'a, H: VerificationHelper> { + decryptor: Decryptor<'a, NoDecryptionHelper>, } -impl DetachedVerifier { +impl<'a, H: VerificationHelper> DetachedVerifier<'a, H> { /// Creates a `Verifier` from the given readers. /// /// Signature verifications are done relative to time `t`, or the /// current time, if `t` is `None`. - pub fn from_reader<'a, 's, H, R, S, T>(policy: &'a dyn Policy, - signature_reader: S, reader: R, - helper: H, t: T) - -> Result> - where R: io::Read + 'a, S: io::Read + 's, H: VerificationHelper, + pub fn from_reader(policy: &'a dyn Policy, + signature_reader: S, + helper: H, t: T) + -> Result> + where S: io::Read + 'a, + H: VerificationHelper, T: Into> { // Do not eagerly map `t` to the current time. @@ -932,7 +712,6 @@ impl DetachedVerifier { policy, Box::new(buffered_reader::Generic::with_cookie(signature_reader, None, Default::default())), - Box::new(buffered_reader::Generic::new(reader, None)), helper, t) } @@ -940,11 +719,12 @@ impl DetachedVerifier { /// /// Signature verifications are done relative to time `t`, or the /// current time, if `t` is `None`. - pub fn from_file<'a, H, P, S, T>(policy: &'a dyn Policy, - signature_path: S, path: P, - helper: H, t: T) - -> Result> - where P: AsRef, S: AsRef, H: VerificationHelper, + pub fn from_file(policy: &'a dyn Policy, + signature_path: S, + helper: H, t: T) + -> Result> + where S: AsRef, + H: VerificationHelper, T: Into> { // Do not eagerly map `t` to the current time. @@ -952,8 +732,7 @@ impl DetachedVerifier { Self::from_buffered_reader( policy, Box::new(buffered_reader::File::with_cookie(signature_path, - Default::default())?), - Box::new(buffered_reader::File::open(path)?), + Default::default())?), helper, t) } @@ -961,10 +740,10 @@ impl DetachedVerifier { /// /// Signature verifications are done relative to time `t`, or the /// current time, if `t` is `None`. - pub fn from_bytes<'a, 's, H, T>(policy: &'a dyn Policy, - signature_bytes: &'s [u8], bytes: &'a [u8], - helper: H, t: T) - -> Result> + pub fn from_bytes(policy: &'a dyn Policy, + signature_bytes: &'a [u8], + helper: H, t: T) + -> Result> where H: VerificationHelper, T: Into> { // Do not eagerly map `t` to the current time. @@ -973,7 +752,6 @@ impl DetachedVerifier { policy, Box::new(buffered_reader::Memory::with_cookie(signature_bytes, Default::default())), - Box::new(buffered_reader::Memory::new(bytes)), helper, t) } @@ -981,26 +759,75 @@ impl DetachedVerifier { /// /// Signature verifications are done relative to time `t`, or the /// current time, if `t` is `None`. - pub(crate) fn from_buffered_reader<'a, 's, H, T> + pub(crate) fn from_buffered_reader (policy: &'a dyn Policy, - signature_bio: Box + 's>, - reader: Box + 'a>, + signature_bio: Box + 'a>, helper: H, t: T) - -> Result> + -> Result> where H: VerificationHelper, T: Into> { // Do not eagerly map `t` to the current time. let t = t.into(); - Verifier::from_buffered_reader( - policy, - Box::new(buffered_reader::Generic::with_cookie( - Transformer::new(signature_bio, reader)?, - None, Default::default())), - helper, t) + Ok(Self { + decryptor: Decryptor::from_buffered_reader( + policy, + signature_bio, + NoDecryptionHelper { v: helper, }, + t, Mode::VerifyDetached)?, + }) + } + + /// Verifies the given data. + pub fn verify_reader(&mut self, reader: R) -> Result<()> { + self.verify(buffered_reader::Generic::with_cookie( + reader, None, Default::default()).as_boxed()) + } + + /// Verifies the given data. + pub fn verify_file>(&mut self, path: P) -> Result<()> { + self.verify(buffered_reader::File::with_cookie( + path, Default::default())?.as_boxed()) + } + + /// Verifies the given data. + pub fn verify_bytes>(&mut self, buf: B) -> Result<()> { + self.verify(buffered_reader::Memory::with_cookie( + buf.as_ref(), Default::default()).as_boxed()) + } + + /// Verifies the given data. + fn verify(&mut self, reader: R) -> Result<()> + where R: BufferedReader, + { + self.decryptor.verify_detached(reader) + } + + /// Returns a reference to the helper. + pub fn helper_ref(&self) -> &H { + &self.decryptor.helper_ref().v + } + + /// Returns a mutable reference to the helper. + pub fn helper_mut(&mut self) -> &mut H { + &mut self.decryptor.helper_mut().v + } + + /// Recovers the helper. + pub fn into_helper(self) -> H { + self.decryptor.into_helper().v } } + +/// Modes of operation for the Decryptor. +#[derive(Debug, PartialEq, Eq)] +enum Mode { + Decrypt, + Verify, + VerifyDetached, +} + /// Decrypts and verifies an encrypted and optionally signed OpenPGP /// message. /// @@ -1080,9 +907,8 @@ pub struct Decryptor<'a, H: VerificationHelper + DecryptionHelper> { reserve: Option>, cursor: usize, - /// Whether or not we shall decrypt (note: Verifier is implemented - /// using Decryptor). - decrypt: bool, + /// The mode of operation. + mode: Mode, /// Signature verification relative to this time. /// @@ -1165,7 +991,7 @@ impl<'a, H: VerificationHelper + DecryptionHelper> Decryptor<'a, H> { policy, Box::new(buffered_reader::Generic::with_cookie(reader, None, Default::default())), - helper, t, true) + helper, t, Mode::Decrypt) } /// Creates a `Decryptor` from the given file. @@ -1183,7 +1009,7 @@ impl<'a, H: VerificationHelper + DecryptionHelper> Decryptor<'a, H> { policy, Box::new(buffered_reader::File::with_cookie(path, Default::default())?), - helper, t, true) + helper, t, Mode::Decrypt) } /// Creates a `Decryptor` from the given buffer. @@ -1200,7 +1026,7 @@ impl<'a, H: VerificationHelper + DecryptionHelper> Decryptor<'a, H> { policy, Box::new(buffered_reader::Memory::with_cookie(bytes, Default::default())), - helper, t, true) + helper, t, Mode::Decrypt) } /// Returns a reference to the helper. @@ -1227,11 +1053,11 @@ impl<'a, H: VerificationHelper + DecryptionHelper> Decryptor<'a, H> { } /// Creates the `Decryptor`, and buffers the data up to `BUFFER_SIZE`. - pub(crate) fn from_buffered_reader( + fn from_buffered_reader( policy: &'a dyn Policy, bio: Box + 'a>, helper: H, time: T, - decrypt: bool) + mode: Mode) -> Result> where T: Into> { @@ -1256,7 +1082,7 @@ impl<'a, H: VerificationHelper + DecryptionHelper> Decryptor<'a, H> { structure: IMessageStructure::new(), reserve: None, cursor: 0, - decrypt, + mode, time: time, clock_skew_tolerance: tolerance, policy: policy, @@ -1270,9 +1096,15 @@ impl<'a, H: VerificationHelper + DecryptionHelper> Decryptor<'a, H> { while let PacketParserResult::Some(mut pp) = ppr { v.policy.packet(&pp.packet)?; v.helper.inspect(&pp)?; - if let Err(err) = pp.possible_message() { - t!("Malformed message: {}", err); - return Err(err.context("Malformed OpenPGP message").into()); + + // When verifying detached signatures, we parse only the + // signatures here, which on their own are not a valid + // message. + if v.mode != Mode::VerifyDetached { + if let Err(err) = pp.possible_message() { + t!("Malformed message: {}", err); + return Err(err.context("Malformed OpenPGP message").into()); + } } let sym_algo_hint = if let Packet::AED(ref aed) = pp.packet { @@ -1284,7 +1116,7 @@ impl<'a, H: VerificationHelper + DecryptionHelper> Decryptor<'a, H> { match pp.packet { Packet::CompressedData(ref p) => v.structure.new_compression_layer(p.algo()), - Packet::SEIP(_) | Packet::AED(_) if v.decrypt => { + Packet::SEIP(_) | Packet::AED(_) if v.mode == Mode::Decrypt => { saw_content = true; // Get the symmetric algorithm from the decryption @@ -1373,12 +1205,55 @@ impl<'a, H: VerificationHelper + DecryptionHelper> Decryptor<'a, H> { ppr = ppr_tmp; } + if v.mode == Mode::VerifyDetached { + v.certs = v.helper.get_public_keys(&issuers)?; + return Ok(v); + } + // We can only get here if we didn't encounter a literal data // packet. Err(Error::MalformedMessage( "Malformed OpenPGP message".into()).into()) } + /// Verifies the given data in detached verification mode. + fn verify_detached(&mut self, data: D) -> Result<()> + where D: BufferedReader + { + assert_eq!(self.mode, Mode::VerifyDetached); + + let sigs = if let IMessageLayer::SignatureGroup { + sigs, .. } = &mut self.structure.layers[0] { + sigs + } else { + unreachable!("There is exactly one signature group layer") + }; + + // Compute the necessary hashes. + let algos: Vec<_> = sigs.iter().map(|s| s.hash_algo()).collect(); + let hashes = crate::crypto::hash_buffered_reader(data, &algos)?; + + // Attach the digests. + for sig in sigs.iter_mut() { + let algo = sig.hash_algo(); + // Note: |hashes| < 10, most likely 1. + for hash in hashes.iter().filter(|c| c.algo() == algo) { + // Clone the hash context, update it with the + // signature. + use crate::crypto::hash::Hash; + let mut hash = hash.clone(); + sig.hash(&mut hash); + + // Attach digest to the signature. + let mut digest = vec![0; hash.digest_size()]; + hash.digest(&mut digest); + sig.set_computed_digest(Some(digest.into())); + } + } + + self.verify_signatures() + } + /// Stashes the given Signature (if it is one) for later /// verification. fn push_sig(&mut self, p: Packet) -> Result<()> { @@ -1688,6 +1563,7 @@ impl<'a, H: VerificationHelper + DecryptionHelper> io::Read for Decryptor<'a, H> #[cfg(test)] mod test { use super::*; + use std::convert::TryFrom; use crate::parse::Parse; use crate::policy::StandardPolicy as P; @@ -1925,9 +1801,8 @@ mod test { assert!(v.message_processed()); } - // This test is relatively long running in debug mode. Split it - // up. - fn detached_verifier_read_size(l: usize) { + #[test] + fn detached_verifier() { lazy_static! { static ref ZEROS: Vec = vec![0; 100 * 1024 * 1024]; } @@ -1961,29 +1836,6 @@ mod test { .map(|f| Cert::from_bytes(crate::tests::key(f)).unwrap()) .collect::>(); - let read_to_end = |v: &mut Verifier<_>, l, buffer: &mut Vec<_>| { - let mut offset = 0; - loop { - if offset + l > buffer.len() { - if buffer.len() < buffer.capacity() { - // Use the available capacity. - buffer.resize(buffer.capacity(), 0); - } else { - // Double the capacity and size. - buffer.resize(buffer.capacity() * 2, 0); - } - } - match v.read(&mut buffer[offset..offset + l]) { - Ok(0) => break, - Ok(l) => offset += l, - Err(err) => panic!("Error reading data: {:?}", err), - } - } - - offset - }; - - let mut buffer = vec![0; 104 * 1024 * 1024]; for test in tests.iter() { let sig = test.sig; let content = test.content; @@ -1991,34 +1843,13 @@ mod test { let h = VHelper::new(0, 0, 0, 0, keys.clone()); let mut v = DetachedVerifier::from_bytes( - &p, sig, content, h, reference).unwrap(); - - let got = read_to_end(&mut v, l, &mut buffer); - assert!(v.message_processed()); - let got = &buffer[..got]; - assert_eq!(got.len(), content.len()); - assert_eq!(got, &content[..]); + &p, sig, h, reference).unwrap(); + v.verify_bytes(content).unwrap(); let h = v.into_helper(); assert_eq!(h.good, 1); assert_eq!(h.bad, 0); } - crate::vec_truncate(&mut buffer, 0); - } - - #[test] - fn detached_verifier1() { - // Transformer::read_helper rounds up to 4 MB chunks try - // chunk sizes around that size. - detached_verifier_read_size(4 * 1024 * 1024 - 1); - } - #[test] - fn detached_verifier2() { - detached_verifier_read_size(4 * 1024 * 1024); - } - #[test] - fn detached_verifier3() { - detached_verifier_read_size(4 * 1024 * 1024 + 1); } #[test] diff --git a/openpgp/src/policy.rs b/openpgp/src/policy.rs index 1c66ffb8..dc859287 100644 --- a/openpgp/src/policy.rs +++ b/openpgp/src/policy.rs @@ -1700,19 +1700,10 @@ mod test { }; let h = VHelper::new(vec![ cert.clone() ]); - let mut v = - match DetachedVerifier::from_bytes(p, &sig, msg, h, None) { - Ok(v) => v, - Err(e) => panic!("{}", e), - }; - assert!(v.message_processed()); + let mut v = DetachedVerifier::from_bytes(p, &sig, h, None).unwrap(); + v.verify_bytes(msg).unwrap(); assert_eq!(v.helper_ref().good, if good { 1 } else { 0 }); assert_eq!(v.helper_ref().errors, if good { 0 } else { 1 }); - - let mut content = Vec::new(); - v.read_to_end(&mut content).unwrap(); - assert_eq!(msg.len(), content.len()); - assert_eq!(msg, &content[..]); } diff --git a/openpgp/src/serialize/stream.rs b/openpgp/src/serialize/stream.rs index e058e1d7..76e7b4a3 100644 --- a/openpgp/src/serialize/stream.rs +++ b/openpgp/src/serialize/stream.rs @@ -381,12 +381,9 @@ impl<'a> Signer<'a> { /// } /// /// let mut verifier = - /// DetachedVerifier::from_bytes(p, &o, b"Make it so, number one!", - /// Helper(&cert), None)?; + /// DetachedVerifier::from_bytes(p, &o, Helper(&cert), None)?; /// - /// let mut message = String::new(); - /// verifier.read_to_string(&mut message)?; - /// assert_eq!(&message, "Make it so, number one!"); + /// verifier.verify_bytes(b"Make it so, number one!")?; /// # Ok(()) /// # } /// ``` diff --git a/sqv/src/sqv.rs b/sqv/src/sqv.rs index cb458797..74e2d078 100644 --- a/sqv/src/sqv.rs +++ b/sqv/src/sqv.rs @@ -4,7 +4,6 @@ /// the motivation. use std::process::exit; -use std::io; use chrono::{DateTime, offset::Utc}; extern crate clap; @@ -276,10 +275,8 @@ fn main() -> Result<()> { let h = VHelper::new(good_threshold, not_before, not_after, keyrings); - let mut v = DetachedVerifier::from_file( - p, sig_file, file, h, None)?; - - io::copy(&mut v, &mut io::sink())?; + let mut v = DetachedVerifier::from_file(p, sig_file, h, None)?; + v.verify_file(file)?; let h = v.into_helper(); diff --git a/tool/src/commands/mod.rs b/tool/src/commands/mod.rs index 8c2fd516..6e5b029e 100644 --- a/tool/src/commands/mod.rs +++ b/tool/src/commands/mod.rs @@ -407,15 +407,17 @@ pub fn verify(ctx: &Context, policy: &dyn Policy, signatures: usize, certs: Vec) -> Result<()> { let helper = VHelper::new(ctx, mapping, signatures, certs); - let mut verifier = if let Some(dsig) = detached { - DetachedVerifier::from_reader(policy, dsig, input, helper, None)? + let helper = if let Some(dsig) = detached { + let mut v = DetachedVerifier::from_reader(policy, dsig, helper, None)?; + v.verify_reader(input)?; + v.into_helper() } else { - Verifier::from_reader(policy, input, helper, None)? + let mut v = Verifier::from_reader(policy, input, helper, None)?; + io::copy(&mut v, output)?; + v.into_helper() }; - io::copy(&mut verifier, output)?; - - verifier.into_helper().print_status(); + helper.print_status(); Ok(()) } -- cgit v1.2.3