diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2020-02-12 18:18:16 +0100 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2020-02-12 18:18:16 +0100 |
commit | 9f63276b0292a590500517be3d2e88812cb7052a (patch) | |
tree | 30455ecd30f1bbd5f20fab8086176189e863db23 | |
parent | ee21b02c1e94190535145f08f01846d42e6f1db1 (diff) |
openpgp: Express the streaming Verifier using the Decryptor.
-rw-r--r-- | openpgp/src/parse/stream.rs | 581 |
1 files changed, 141 insertions, 440 deletions
diff --git a/openpgp/src/parse/stream.rs b/openpgp/src/parse/stream.rs index eadba97c..28c8d8c9 100644 --- a/openpgp/src/parse/stream.rs +++ b/openpgp/src/parse/stream.rs @@ -64,112 +64,6 @@ const TRACE : bool = false; /// How much data to buffer before giving it to the caller. const BUFFER_SIZE: usize = 25 * 1024 * 1024; -/// Verifies a signed OpenPGP message. -/// -/// Signature verification requires processing the whole message -/// first. Therefore, OpenPGP implementations supporting streaming -/// operations necessarily must output unverified data. This has been -/// a source of problems in the past. To alleviate this, we buffer up -/// to 25 megabytes of net message data first, and verify the -/// signatures if the message fits into our buffer. Nevertheless it -/// is important to treat the data as unverified and untrustworthy -/// until you have seen a positive verification. -/// -/// For a signature to be considered valid: The signature must have a -/// `Signature Creation Time` subpacket. The signature must be alive -/// at the signature verification time (the time passed to -/// `Verifier::from_reader`). The key used to verify the signature -/// must be alive at the signature creation time, not have been soft -/// revoked at the signature creation time, not have ever been hard -/// revoked, and be signing capable at the signature creation time. -/// -/// # Example -/// -/// ``` -/// extern crate sequoia_openpgp as openpgp; -/// extern crate failure; -/// use std::io::Read; -/// use openpgp::{KeyID, Cert, Result}; -/// use openpgp::parse::stream::*; -/// use openpgp::policy::StandardPolicy; -/// -/// # fn main() { f().unwrap(); } -/// # fn f() -> Result<()> { -/// let p = &StandardPolicy::new(); -/// -/// // This fetches keys and computes the validity of the verification. -/// struct Helper {}; -/// impl VerificationHelper for Helper { -/// fn get_public_keys(&mut self, _ids: &[openpgp::KeyHandle]) -> Result<Vec<Cert>> { -/// Ok(Vec::new()) // Feed the Certs to the verifier here... -/// } -/// fn check(&mut self, structure: MessageStructure) -> Result<()> { -/// Ok(()) // Implement your verification policy here. -/// } -/// } -/// -/// let message = -/// b"-----BEGIN PGP MESSAGE----- -/// -/// xA0DAAoWBpwMNI3YLBkByxJiAAAAAABIZWxsbyBXb3JsZCHCdQQAFgoAJwWCW37P -/// 8RahBI6MM/pGJjN5dtl5eAacDDSN2CwZCZAGnAw0jdgsGQAAeZQA/2amPbBXT96Q -/// O7PFms9DRuehsVVrFkaDtjN2WSxI4RGvAQDq/pzNdCMpy/Yo7AZNqZv5qNMtDdhE -/// b2WH5lghfKe/AQ== -/// =DjuO -/// -----END PGP MESSAGE-----"; -/// -/// let h = Helper {}; -/// let mut v = Verifier::from_bytes(p, message, h, None)?; -/// -/// let mut content = Vec::new(); -/// v.read_to_end(&mut content) -/// .map_err(|e| if e.get_ref().is_some() { -/// // Wrapped failure::Error. Recover it. -/// failure::Error::from_boxed_compat(e.into_inner().unwrap()) -/// } else { -/// // Plain io::Error. -/// e.into() -/// })?; -/// -/// assert_eq!(content, b"Hello World!"); -/// # Ok(()) -/// # } -pub struct Verifier<'a, H: VerificationHelper> { - helper: H, - certs: Vec<Cert>, - oppr: Option<PacketParserResult<'a>>, - structure: IMessageStructure, - - // The reserve data. - reserve: Option<Vec<u8>>, - - /// Perform the signature verification relative to this time. - /// - /// This is needed for checking the signature's liveness. - /// - /// We want the same semantics as `Subpacket::signature_alive`. - /// Specifically, when using the current time, we want to tolerate - /// some clock skew, but when using some specific time, we don't. - /// (See `Subpacket::signature_alive` for an explanation.) - /// - /// These semantics can be realized by making `time` an - /// `Option<time::SystemTime>` and passing that as is to - /// `Subpacket::signature_alive`. But that approach has two new - /// problems. First, if we are told to use the current time, then - /// we want to use the time at which the Verifier was - /// instantiated, not the time at which we call - /// `Subpacket::signature_alive`. Second, if we call - /// `Subpacket::signature_alive` multiple times, they should all - /// use the same time. To work around these issues, when a - /// Verifier is instantiated, we evaluate `time` and we record how - /// much we want to tolerate clock skew in the same way as - /// `Subpacket::signature_alive`. - time: time::SystemTime, - clock_skew_tolerance: time::Duration, - - policy: &'a dyn Policy, -} - /// Contains the result of a signature verification. #[derive(Debug)] pub enum VerificationResult<'a> { @@ -538,6 +432,107 @@ pub trait VerificationHelper { fn check(&mut self, structure: MessageStructure) -> Result<()>; } +/// Wraps a VerificationHelper and adds a non-functional +/// DecryptionHelper implementation. +struct NoDecryptionHelper<V: VerificationHelper> { + v: V, +} + +impl<V: VerificationHelper> VerificationHelper for NoDecryptionHelper<V> { + fn get_public_keys(&mut self, ids: &[crate::KeyHandle]) -> Result<Vec<Cert>> + { + self.v.get_public_keys(ids) + } + fn check(&mut self, structure: MessageStructure) -> Result<()> + { + self.v.check(structure) + } +} + +impl<V: VerificationHelper> DecryptionHelper for NoDecryptionHelper<V> { + fn decrypt<D>(&mut self, _: &[PKESK], _: &[SKESK], + _: Option<SymmetricAlgorithm>, + _: D) -> Result<Option<Fingerprint>> + where D: FnMut(SymmetricAlgorithm, &SessionKey) -> Result<()> + { + unreachable!("This is not used for verifications") + } +} + +/// Verifies a signed OpenPGP message. +/// +/// Signature verification requires processing the whole message +/// first. Therefore, OpenPGP implementations supporting streaming +/// operations necessarily must output unverified data. This has been +/// a source of problems in the past. To alleviate this, we buffer up +/// to 25 megabytes of net message data first, and verify the +/// signatures if the message fits into our buffer. Nevertheless it +/// is important to treat the data as unverified and untrustworthy +/// until you have seen a positive verification. +/// +/// For a signature to be considered valid: The signature must have a +/// `Signature Creation Time` subpacket. The signature must be alive +/// at the signature verification time (the time passed to +/// `Verifier::from_reader`). The key used to verify the signature +/// must be alive at the signature creation time, not have been soft +/// revoked at the signature creation time, not have ever been hard +/// revoked, and be signing capable at the signature creation time. +/// +/// # Example +/// +/// ``` +/// extern crate sequoia_openpgp as openpgp; +/// extern crate failure; +/// use std::io::Read; +/// use openpgp::{KeyID, Cert, Result}; +/// use openpgp::parse::stream::*; +/// use openpgp::policy::StandardPolicy; +/// +/// # fn main() { f().unwrap(); } +/// # fn f() -> Result<()> { +/// let p = &StandardPolicy::new(); +/// +/// // This fetches keys and computes the validity of the verification. +/// struct Helper {}; +/// impl VerificationHelper for Helper { +/// fn get_public_keys(&mut self, _ids: &[openpgp::KeyHandle]) -> Result<Vec<Cert>> { +/// Ok(Vec::new()) // Feed the Certs to the verifier here... +/// } +/// fn check(&mut self, structure: MessageStructure) -> Result<()> { +/// Ok(()) // Implement your verification policy here. +/// } +/// } +/// +/// let message = +/// b"-----BEGIN PGP MESSAGE----- +/// +/// xA0DAAoWBpwMNI3YLBkByxJiAAAAAABIZWxsbyBXb3JsZCHCdQQAFgoAJwWCW37P +/// 8RahBI6MM/pGJjN5dtl5eAacDDSN2CwZCZAGnAw0jdgsGQAAeZQA/2amPbBXT96Q +/// O7PFms9DRuehsVVrFkaDtjN2WSxI4RGvAQDq/pzNdCMpy/Yo7AZNqZv5qNMtDdhE +/// b2WH5lghfKe/AQ== +/// =DjuO +/// -----END PGP MESSAGE-----"; +/// +/// let h = Helper {}; +/// let mut v = Verifier::from_bytes(p, message, h, None)?; +/// +/// let mut content = Vec::new(); +/// v.read_to_end(&mut content) +/// .map_err(|e| if e.get_ref().is_some() { +/// // Wrapped failure::Error. Recover it. +/// failure::Error::from_boxed_compat(e.into_inner().unwrap()) +/// } else { +/// // Plain io::Error. +/// e.into() +/// })?; +/// +/// assert_eq!(content, b"Hello World!"); +/// # Ok(()) +/// # } +pub struct Verifier<'a, H: VerificationHelper> { + decryptor: Decryptor<'a, NoDecryptionHelper<H>>, +} + impl<'a, H: VerificationHelper> Verifier<'a, H> { /// Creates a `Verifier` from the given reader. /// @@ -594,17 +589,17 @@ impl<'a, H: VerificationHelper> Verifier<'a, H> { /// Returns a reference to the helper. pub fn helper_ref(&self) -> &H { - &self.helper + &self.decryptor.helper_ref().v } /// Returns a mutable reference to the helper. pub fn helper_mut(&mut self) -> &mut H { - &mut self.helper + &mut self.decryptor.helper_mut().v } /// Recovers the helper. pub fn into_helper(self) -> H { - self.helper + self.decryptor.into_helper().v } /// Returns true if the whole message has been processed and the verification result is ready. @@ -612,7 +607,7 @@ impl<'a, H: VerificationHelper> Verifier<'a, H> { /// **unverified** data must be `read()` from the instance until EOF. pub fn message_processed(&self) -> bool { // oppr is only None after we've processed the packet sequence. - self.oppr.is_none() + self.decryptor.message_processed() } /// Creates the `Verifier`, and buffers the data up to `BUFFER_SIZE`. @@ -625,336 +620,16 @@ impl<'a, H: VerificationHelper> Verifier<'a, H> { -> Result<Verifier<'a, H>> where T: Into<Option<time::SystemTime>> { - let time = time.into(); - let tolerance = time - .map(|_| time::Duration::new(0, 0)) - .unwrap_or( - *crate::packet::signature::subpacket::CLOCK_SKEW_TOLERANCE); - let time = time - .unwrap_or_else(|| time::SystemTime::now()); - - let mut ppr = PacketParser::from_buffered_reader(bio)?; - - let mut v = Verifier { - helper: helper, - certs: Vec::new(), - oppr: None, - structure: IMessageStructure::new(), - reserve: None, - time: time, - clock_skew_tolerance: tolerance, - policy: policy, - }; - - let mut issuers = Vec::new(); - - while let PacketParserResult::Some(pp) = ppr { - if let Err(err) = pp.possible_message() { - return Err(err.context("Malformed OpenPGP message").into()); - } - - match pp.packet { - Packet::CompressedData(ref p) => - v.structure.new_compression_layer(p.algo()), - Packet::OnePassSig(ref ops) => { - v.structure.push_ops(ops); - issuers.push(ops.issuer().clone().into()); - }, - Packet::Literal(_) => { - v.structure.insert_missing_signature_group(); - v.certs = v.helper.get_public_keys(&issuers)?; - v.oppr = Some(PacketParserResult::Some(pp)); - v.finish_maybe()?; - return Ok(v); - }, - _ => (), - } - - let (p, ppr_tmp) = pp.recurse()?; - if let Packet::Signature(sig) = p { - // The following structure is allowed: - // - // SIG LITERAL - // - // In this case, we get the issuer from the - // signature itself. - let sig_issuers = sig.get_issuers(); - if sig_issuers.is_empty() { - issuers.push(KeyID::wildcard().into()); - } else { - for issuer in sig_issuers { - issuers.push(issuer); - } - } - - v.structure.push_bare_signature(sig); - } - - ppr = ppr_tmp; - } - - // We can only get here if we didn't encounter a literal data - // packet. - Err(Error::MalformedMessage( - "Malformed OpenPGP message".into()).into()) - } - - - /// Stashes the given Signature (if it is one) for later - /// verification. - fn push_sig(&mut self, p: Packet) -> Result<()> { - match p { - Packet::Signature(sig) => { - self.structure.push_signature(sig); - }, - _ => (), - } - Ok(()) - } - - // Verify the signatures. This can only be called once the - // message has been fully processed. - fn check_signatures(&mut self) -> Result<()> { - assert!(self.oppr.is_none()); - - // Verify the signatures. - let mut results = MessageStructure::new(); - for layer in ::std::mem::replace(&mut self.structure, - IMessageStructure::new()) - .layers.into_iter() - { - match layer { - IMessageLayer::Compression { algo } => - results.new_compression_layer(algo), - IMessageLayer::Encryption { .. } => - unreachable!("not decrypting messages"), - IMessageLayer::SignatureGroup { sigs, .. } => { - results.new_signature_group(); - 'sigs: for sig in sigs.into_iter() { - let sig_time = if let Some(t) = sig.signature_creation_time() { - t - } else { - // Invalid signature. - results.push_verification_result( - VerificationResult::Error { - sig, - error: Error::MalformedPacket( - "missing a Signature Creation Time \ - subpacket" - .into()).into() - }); - continue; - }; - - if let Err(_err) = sig.signature_alive( - self.time, self.clock_skew_tolerance) - { - // Invalid signature. - results.push_verification_result( - VerificationResult::NotAlive { - sig: sig.clone(), - }); - continue; - } - - let mut err = VerificationResult::MissingKey { - sig: sig.clone(), - }; - - let issuers = sig.get_issuers(); - for ka in self.certs.iter() - .flat_map(|cert| { - cert.keys().with_policy(self.policy, sig_time) - .key_handles(issuers.iter()) - }) - { - err = if let Err(err) = ka.cert_alive() { - VerificationResult::Error { - sig: sig.clone(), - error: err, - } - } else if let Err(err) = ka.alive() { - VerificationResult::Error { - sig: sig.clone(), - error: err, - } - } else if destructures_to!( - RevocationStatus::Revoked(_) = ka.cert_revoked()) - { - VerificationResult::Error { - sig: sig.clone(), - error: Error::InvalidKey( - "certificate is revoked".into()) - .into(), - } - } else if destructures_to!( - RevocationStatus::Revoked(_) = ka.revoked()) - { - VerificationResult::Error { - sig: sig.clone(), - error: Error::InvalidKey( - "signing key is revoked".into()) - .into(), - } - } else if ! ka.for_signing() { - VerificationResult::Error { - sig: sig.clone(), - error: Error::InvalidKey( - "key is not signing capable".into()) - .into(), - } - } else { - match sig.verify(ka.key()) { - Ok(()) => { - if let Err(err) = self.policy.signature(&sig) { - VerificationResult::Error { - sig: sig.clone(), - error: err, - } - } else { - results.push_verification_result( - VerificationResult::GoodChecksum { - sig: sig, - cert: ka.cert(), - ka, - }); - // Continue to the next sig. - continue 'sigs; - } - } - Err(err) => { - VerificationResult::Error { - sig: sig.clone(), - error: err, - } - } - } - } - } - - // Hmm, we didn't consider any keys. Iterate - // over the keys again, but this time, don't - // use a policy. If we find something now, - // the policy must have rejected it. Turn - // that information into a more useful (and - // less misleading) error message than - // `VerificationResult::MissingKey`. - if let VerificationResult::MissingKey { .. } = err { - if let Some(ka) = self.certs.iter() - .flat_map(|cert| { - cert.keys().key_handles(issuers.iter()) - }) - .next() - { - err = VerificationResult::Error { - sig: sig.clone(), - error: Error::InvalidKey( - format!( - "Signing key ({}) not valid \ - when signature was created", - ka.key().fingerprint())) - .into(), - } - } - } - - results.push_verification_result(err); - } - } - } - } - - self.helper.check(results) - } - - // If the amount of remaining data does not exceed the reserve, - // finish processing the OpenPGP packet sequence. - // - // Note: once this call succeeds, you may not call it again. - fn finish_maybe(&mut self) -> Result<()> { - if let Some(PacketParserResult::Some(mut pp)) = self.oppr.take() { - // Check if we hit EOF. - let data_len = pp.data(BUFFER_SIZE + 1)?.len(); - if data_len <= BUFFER_SIZE { - let data_len = pp.data(BUFFER_SIZE + 1)?.len(); - assert!(data_len <= BUFFER_SIZE); - - // Stash the reserve. - self.reserve = Some(pp.steal_eof()?); - - // Process the rest of the packets. - let mut ppr = PacketParserResult::Some(pp); - while let PacketParserResult::Some(pp) = ppr { - if let Err(err) = pp.possible_message() { - return Err(err.context( - "Malformed OpenPGP message").into()); - } - - let (p, ppr_tmp) = pp.recurse()?; - self.push_sig(p)?; - ppr = ppr_tmp; - } - - self.check_signatures() - } else { - self.oppr = Some(PacketParserResult::Some(pp)); - Ok(()) - } - } else { - panic!("No ppr."); - } - } - - /// Like `io::Read::read()`, but returns our `Result`. - fn read_helper(&mut self, buf: &mut [u8]) -> Result<usize> { - if buf.len() == 0 { - return Ok(0); - } - - if let Some(ref mut reserve) = self.reserve { - // The message has been verified. We can now drain the - // reserve. - assert!(self.oppr.is_none()); - - let n = cmp::min(buf.len(), reserve.len()); - &mut buf[..n].copy_from_slice(&reserve[..n]); - crate::vec_drain_prefix(reserve, n); - return Ok(n); - } - - // Read the data from the Literal data packet. - if let Some(PacketParserResult::Some(mut pp)) = self.oppr.take() { - // Be careful to not read from the reserve. - let data_len = pp.data(BUFFER_SIZE + buf.len())?.len(); - if data_len <= BUFFER_SIZE { - self.oppr = Some(PacketParserResult::Some(pp)); - self.finish_maybe()?; - self.read_helper(buf) - } else { - let n = cmp::min(buf.len(), data_len - BUFFER_SIZE); - let buf = &mut buf[..n]; - let result = pp.read(buf); - self.oppr = Some(PacketParserResult::Some(pp)); - Ok(result?) - } - } else { - panic!("No ppr."); - } + Ok(Verifier { + decryptor: Decryptor::from_buffered_reader( + policy, bio, NoDecryptionHelper { v: helper, }, time, false)?, + }) } } impl<'a, H: VerificationHelper> io::Read for Verifier<'a, H> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { - match self.read_helper(buf) { - Ok(n) => Ok(n), - Err(e) => match e.downcast::<io::Error>() { - // An io::Error. Pass as-is. - Ok(e) => Err(e), - // A failure. Create a compat object and wrap it. - Err(e) => Err(io::Error::new(io::ErrorKind::Other, - e.compat())), - }, - } + self.decryptor.read(buf) } } @@ -1395,7 +1070,31 @@ pub struct Decryptor<'a, H: VerificationHelper + DecryptionHelper> { structure: IMessageStructure, reserve: Option<Vec<u8>>, + /// Whether or not we shall decrypt (note: Verifier is implemented + /// using Decryptor). + decrypt: bool, + /// Signature verification relative to this time. + /// + /// This is needed for checking the signature's liveness. + /// + /// We want the same semantics as `Subpacket::signature_alive`. + /// Specifically, when using the current time, we want to tolerate + /// some clock skew, but when using some specific time, we don't. + /// (See `Subpacket::signature_alive` for an explanation.) + /// + /// These semantics can be realized by making `time` an + /// `Option<time::SystemTime>` and passing that as is to + /// `Subpacket::signature_alive`. But that approach has two new + /// problems. First, if we are told to use the current time, then + /// we want to use the time at which the Verifier was + /// instantiated, not the time at which we call + /// `Subpacket::signature_alive`. Second, if we call + /// `Subpacket::signature_alive` multiple times, they should all + /// use the same time. To work around these issues, when a + /// Verifier is instantiated, we evaluate `time` and we record how + /// much we want to tolerate clock skew in the same way as + /// `Subpacket::signature_alive`. time: time::SystemTime, clock_skew_tolerance: time::Duration, @@ -1456,7 +1155,7 @@ impl<'a, H: VerificationHelper + DecryptionHelper> Decryptor<'a, H> { policy, Box::new(buffered_reader::Generic::with_cookie(reader, None, Default::default())), - helper, t) + helper, t, true) } /// Creates a `Decryptor` from the given file. @@ -1474,7 +1173,7 @@ impl<'a, H: VerificationHelper + DecryptionHelper> Decryptor<'a, H> { policy, Box::new(buffered_reader::File::with_cookie(path, Default::default())?), - helper, t) + helper, t, true) } /// Creates a `Decryptor` from the given buffer. @@ -1491,7 +1190,7 @@ impl<'a, H: VerificationHelper + DecryptionHelper> Decryptor<'a, H> { policy, Box::new(buffered_reader::Memory::with_cookie(bytes, Default::default())), - helper, t) + helper, t, true) } /// Returns a reference to the helper. @@ -1521,7 +1220,8 @@ impl<'a, H: VerificationHelper + DecryptionHelper> Decryptor<'a, H> { pub(crate) fn from_buffered_reader<T>( policy: &'a dyn Policy, bio: Box<dyn BufferedReader<Cookie> + 'a>, - helper: H, time: T) + helper: H, time: T, + decrypt: bool) -> Result<Decryptor<'a, H>> where T: Into<Option<time::SystemTime>> { @@ -1545,6 +1245,7 @@ impl<'a, H: VerificationHelper + DecryptionHelper> Decryptor<'a, H> { identity: None, structure: IMessageStructure::new(), reserve: None, + decrypt, time: time, clock_skew_tolerance: tolerance, policy: policy, @@ -1571,7 +1272,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(_) => { + Packet::SEIP(_) | Packet::AED(_) if v.decrypt => { saw_content = true; // Get the symmetric algorithm from the decryption |