diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2019-05-09 15:19:24 +0200 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2019-05-09 15:19:24 +0200 |
commit | 75d4e6dda12e8b7ae8573227e61e718ede3f2cfc (patch) | |
tree | c6b9e3f177d8c65d134acfd889c236203b2ac13f /openpgp | |
parent | 8e0f817f312f469871a5fbed6bb961f6117ba742 (diff) |
openpgp: Communicate message structure from the decryptor.
- Fixes #100.
Diffstat (limited to 'openpgp')
-rw-r--r-- | openpgp/examples/decrypt-with.rs | 44 | ||||
-rw-r--r-- | openpgp/examples/generate-encrypt-decrypt.rs | 2 | ||||
-rw-r--r-- | openpgp/examples/generate-sign-verify.rs | 51 | ||||
-rw-r--r-- | openpgp/src/parse/stream.rs | 574 | ||||
-rw-r--r-- | openpgp/src/serialize/stream.rs | 30 |
5 files changed, 492 insertions, 209 deletions
diff --git a/openpgp/examples/decrypt-with.rs b/openpgp/examples/decrypt-with.rs index 908b8650..63582299 100644 --- a/openpgp/examples/decrypt-with.rs +++ b/openpgp/examples/decrypt-with.rs @@ -17,6 +17,8 @@ use openpgp::parse::{ Decryptor, VerificationHelper, VerificationResult, + MessageStructure, + MessageLayer, }, }; @@ -95,7 +97,7 @@ impl DecryptionHelper for Helper { } // XXX: In production code, return the Fingerprint of the // recipient's TPK here - Ok(None) + Ok(None) } } @@ -104,8 +106,46 @@ impl VerificationHelper for Helper { -> failure::Fallible<Vec<openpgp::TPK>> { Ok(Vec::new()) // Feed the TPKs to the verifier here. } - fn check(&mut self, _sigs: Vec<Vec<VerificationResult>>) + fn check(&mut self, structure: &MessageStructure) -> failure::Fallible<()> { + use self::VerificationResult::*; + for layer in structure.iter() { + match layer { + MessageLayer::Compression { algo } => + eprintln!("Compressed using {}", algo), + MessageLayer::Encryption { sym_algo, aead_algo } => + if let Some(aead_algo) = aead_algo { + eprintln!("Encrypted and protected using {}/{}", + sym_algo, aead_algo); + } else { + eprintln!("Encrypted using {}", sym_algo); + }, + MessageLayer::SignatureGroup { ref results } => + for result in results { + match result { + GoodChecksum(ref sig, ..) => { + let issuer = sig.issuer() + .expect("good checksum has an issuer"); + eprintln!("Good signature from {}", issuer); + }, + MissingKey(ref sig) => { + let issuer = sig.issuer() + .expect("missing key checksum has an \ + issuer"); + eprintln!("No key to check signature from {}", + issuer); + }, + BadChecksum(ref sig) => + if let Some(issuer) = sig.issuer() { + eprintln!("Bad signature from {}", issuer); + } else { + eprintln!("Bad signature without issuer \ + information"); + }, + } + } + } + } Ok(()) // Implement your verification policy here. } } diff --git a/openpgp/examples/generate-encrypt-decrypt.rs b/openpgp/examples/generate-encrypt-decrypt.rs index c338f154..4645cb0c 100644 --- a/openpgp/examples/generate-encrypt-decrypt.rs +++ b/openpgp/examples/generate-encrypt-decrypt.rs @@ -93,7 +93,7 @@ impl<'a> VerificationHelper for Helper<'a> { Ok(Vec::new()) } - fn check(&mut self, _sigs: Vec<Vec<VerificationResult>>) + fn check(&mut self, _structure: &MessageStructure) -> openpgp::Result<()> { // Implement your signature verification policy here. Ok(()) diff --git a/openpgp/examples/generate-sign-verify.rs b/openpgp/examples/generate-sign-verify.rs index 0ebb3d1e..70899c81 100644 --- a/openpgp/examples/generate-sign-verify.rs +++ b/openpgp/examples/generate-sign-verify.rs @@ -92,30 +92,41 @@ impl<'a> VerificationHelper for Helper<'a> { Ok(vec![self.tpk.clone()]) } - fn check(&mut self, sigs: Vec<Vec<VerificationResult>>) + fn check(&mut self, structure: &MessageStructure) -> openpgp::Result<()> { // In this function, we implement our signature verification // policy. - // First, we are interested in signatures over the data, - // i.e. level 0 signatures. - let sigs_over_data = sigs.get(0) - .ok_or_else(|| failure::err_msg("No level 0 signatures found"))?; - - // Now, let's see if there is a signature on that level. - let sig_result = sigs_over_data.get(0) - .ok_or_else(|| failure::err_msg("No signature found"))?; - - // Finally, given a VerificationResult, which only says - // whether the signature checks out mathematically, we apply - // our policy. - match sig_result { - VerificationResult::GoodChecksum(..) => - Ok(()), // Good signature - VerificationResult::MissingKey(_) => - Err(failure::err_msg("Missing key to verify signature")), - VerificationResult::BadChecksum(_) => - Err(failure::err_msg("Bad signature")), + let mut good = false; + for (i, layer) in structure.iter().enumerate() { + match (i, layer) { + // First, we are interested in signatures over the + // data, i.e. level 0 signatures. + (0, MessageLayer::SignatureGroup { ref results }) => { + // Finally, given a VerificationResult, which only says + // whether the signature checks out mathematically, we apply + // our policy. + match results.get(0) { + Some(VerificationResult::GoodChecksum(..)) => + good = true, + Some(VerificationResult::MissingKey(_)) => + return Err(failure::err_msg( + "Missing key to verify signature")), + Some(VerificationResult::BadChecksum(_)) => + return Err(failure::err_msg("Bad signature")), + None => + return Err(failure::err_msg("No signature")), + } + }, + _ => return Err(failure::err_msg( + "Unexpected message structure")), + } + } + + if good { + Ok(()) // Good signature. + } else { + Err(failure::err_msg("Signature verification failed")) } } } diff --git a/openpgp/src/parse/stream.rs b/openpgp/src/parse/stream.rs index f6b20a0f..d9791479 100644 --- a/openpgp/src/parse/stream.rs +++ b/openpgp/src/parse/stream.rs @@ -19,6 +19,8 @@ use { Error, Fingerprint, constants::{ + AEADAlgorithm, + CompressionAlgorithm, DataFormat, SymmetricAlgorithm, }, @@ -27,6 +29,7 @@ use { ctb::CTB, Key, Literal, + OnePassSig, one_pass_sig::OnePassSig3, PKESK, SKESK, @@ -83,7 +86,7 @@ const BUFFER_SIZE: usize = 25 * 1024 * 1024; /// fn get_public_keys(&mut self, _ids: &[KeyID]) -> Result<Vec<TPK>> { /// Ok(Vec::new()) // Feed the TPKs to the verifier here... /// } -/// fn check(&mut self, sigs: Vec<Vec<VerificationResult>>) -> Result<()> { +/// fn check(&mut self, structure: &MessageStructure) -> Result<()> { /// Ok(()) // Implement your verification policy here. /// } /// } @@ -120,7 +123,7 @@ pub struct Verifier<'a, H: VerificationHelper> { /// Maps KeyID to tpks[i].keys_all().nth(j). keys: HashMap<KeyID, (usize, usize)>, oppr: Option<PacketParserResult<'a>>, - sigs: Vec<Vec<Signature>>, + structure: IMessageStructure, // The reserve data. reserve: Option<Vec<u8>>, @@ -162,29 +165,229 @@ impl<'a> VerificationResult<'a> { } } +/// Communicates the message structure to the VerificationHelper. +#[derive(Debug)] +pub struct MessageStructure<'a>(Vec<MessageLayer<'a>>); + +impl<'a> MessageStructure<'a> { + fn new() -> Self { + MessageStructure(Vec::new()) + } + + fn new_compression_layer(&mut self, algo: CompressionAlgorithm) { + self.0.push(MessageLayer::Compression { + algo: algo, + }) + } + + fn new_encryption_layer(&mut self, sym_algo: SymmetricAlgorithm, + aead_algo: Option<AEADAlgorithm>) { + self.0.push(MessageLayer::Encryption { + sym_algo: sym_algo, + aead_algo: aead_algo, + }) + } + + fn new_signature_group(&mut self) { + self.0.push(MessageLayer::SignatureGroup { + results: Vec::new(), + }) + } + + fn push_verification_result(&mut self, sig: VerificationResult<'a>) { + if let Some(MessageLayer::SignatureGroup { ref mut results }) = + self.0.iter_mut().last() + { + results.push(sig); + } else { + panic!("cannot push to encryption or compression layer"); + } + } + + /// Iterates over the message structure. + pub fn iter(&self) -> MessageStructureIter { + MessageStructureIter(self.0.iter()) + } +} + +/// Iterates over the message structure. +pub struct MessageStructureIter<'a>(::std::slice::Iter<'a, MessageLayer<'a>>); + +impl<'a> Iterator for MessageStructureIter<'a> { + type Item = &'a MessageLayer<'a>; + fn next(&mut self) -> Option<Self::Item> { + self.0.next() + } +} + +/// Represents a layer of the message structure. +#[derive(Debug)] +pub enum MessageLayer<'a> { + /// Represents an compression container. + Compression { + /// Compression algorithm used. + algo: CompressionAlgorithm, + }, + /// Represents an encryption container. + Encryption { + /// Symmetric algorithm used. + sym_algo: SymmetricAlgorithm, + /// AEAD algorithm used, if any. + aead_algo: Option<AEADAlgorithm>, + }, + /// Represents a signature group. + SignatureGroup { + /// The results of the signature verifications. + results: Vec<VerificationResult<'a>>, + } +} + +/// Internal version of the message structure. +/// +/// In contrast to MessageStructure, this owns unverified +/// signature packets. +#[derive(Debug)] +struct IMessageStructure { + layers: Vec<IMessageLayer>, + + // We insert a SignatureGroup layer every time we see a OnePassSig + // packet with the last flag. + // + // However, we need to make sure that we insert a SignatureGroup + // layer even if the OnePassSig packet has the last flag set to + // false. To do that, we keep track of the fact that we saw such + // a OPS packet. + sig_group_counter: usize, +} + +impl IMessageStructure { + fn new() -> Self { + IMessageStructure { + layers: Vec::new(), + sig_group_counter: 0, + } + } + + fn new_compression_layer(&mut self, algo: CompressionAlgorithm) { + self.insert_missing_signature_group(); + self.layers.push(IMessageLayer::Compression { + algo: algo, + }); + } + + fn new_encryption_layer(&mut self, sym_algo: SymmetricAlgorithm, + aead_algo: Option<AEADAlgorithm>) { + self.insert_missing_signature_group(); + self.layers.push(IMessageLayer::Encryption { + sym_algo: sym_algo, + aead_algo: aead_algo, + }); + } + + /// Makes sure that we insert a signature group even if the + /// previous OPS packet had the last flag set to false. + fn insert_missing_signature_group(&mut self) { + if self.sig_group_counter > 0 { + self.layers.push(IMessageLayer::SignatureGroup { + sigs: Vec::new(), + count: self.sig_group_counter, + }); + } + self.sig_group_counter = 0; + } + + fn push_ops(&mut self, ops: &OnePassSig) { + self.sig_group_counter += 1; + if ops.last() { + self.layers.push(IMessageLayer::SignatureGroup { + sigs: Vec::new(), + count: self.sig_group_counter, + }); + self.sig_group_counter = 0; + } + } + + fn push_signature(&mut self, sig: Signature) { + for layer in self.layers.iter_mut().rev() { + match layer { + IMessageLayer::SignatureGroup { + ref mut sigs, ref mut count, + } if *count > 0 => { + sigs.push(sig); + *count -= 1; + return; + }, + _ => (), + } + } + panic!("signature unaccounted for"); + } + + fn push_bare_signature(&mut self, sig: Signature) { + if let Some(IMessageLayer::SignatureGroup { .. }) = self.layers.iter().last() { + // The last layer is a SignatureGroup. We will append the + // signature there without accounting for it. + } else { + // The last layer is not a SignatureGroup, or there is no + // layer at all. Create one. + self.layers.push(IMessageLayer::SignatureGroup { + sigs: Vec::new(), + count: 0, + }); + } + + if let IMessageLayer::SignatureGroup { ref mut sigs, .. } = + self.layers.iter_mut().last().expect("just checked or created") + { + sigs.push(sig); + } else { + unreachable!() + } + } + +} + +/// Internal version of a layer of the message structure. +/// +/// In contrast to MessageLayer, this owns unverified signature packets. +#[derive(Debug)] +enum IMessageLayer { + Compression { + algo: CompressionAlgorithm, + }, + Encryption { + sym_algo: SymmetricAlgorithm, + aead_algo: Option<AEADAlgorithm>, + }, + SignatureGroup { + sigs: Vec<Signature>, + count: usize, + } +} + /// Helper for signature verification. pub trait VerificationHelper { /// Retrieves the TPKs containing the specified keys. fn get_public_keys(&mut self, &[KeyID]) -> Result<Vec<TPK>>; - /// Conveys the result of a signature verification. + /// Conveys the message structure. + /// + /// The message structure contains the results of signature + /// verifications. See [`MessageStructure`] for more information. + /// + /// [`MessageStructure`]: struct.MessageStructure.html /// /// This is called after the last signature has been verified. /// This is the place to implement your verification policy. /// Check that the required number of signatures or notarizations /// were confirmed as valid. /// - /// The argument is a vector, with `sigs[sigs.len()-1]` being the - /// vector of signatures over the data, `sigs[sigs.len()-2]` being - /// notarizations over signatures of level 0, and the data, and so - /// on. - /// /// This callback is only called before all data is returned. /// That is, once `io::Read` returns EOF, this callback will not /// be called again. As such, any error returned by this function /// will abort reading, and the error will be propagated via the /// `io::Read` operation. - fn check(&mut self, sigs: Vec<Vec<VerificationResult>>) -> Result<()>; + fn check(&mut self, structure: &MessageStructure) -> Result<()>; } impl<'a, H: VerificationHelper> Verifier<'a, H> { @@ -271,18 +474,12 @@ impl<'a, H: VerificationHelper> Verifier<'a, H> { tpks: Vec::new(), keys: HashMap::new(), oppr: None, - sigs: Vec::new(), + structure: IMessageStructure::new(), reserve: None, time: t, }; let mut issuers = Vec::new(); - // The following structure is allowed: - // - // SIG LITERAL - // - // In this case, we queue the signature packets. - let mut sigs = Vec::new(); while let PacketParserResult::Some(pp) = ppr { if ! pp.possible_message() { @@ -291,9 +488,14 @@ impl<'a, H: VerificationHelper> Verifier<'a, H> { } match pp.packet { - Packet::OnePassSig(ref ops) => - issuers.push(ops.issuer().clone()), + Packet::CompressedData(ref p) => + v.structure.new_compression_layer(p.algorithm()), + Packet::OnePassSig(ref ops) => { + v.structure.push_ops(ops); + issuers.push(ops.issuer().clone()); + }, Packet::Literal(_) => { + v.structure.insert_missing_signature_group(); // Query keys. v.tpks = v.helper.get_public_keys(&issuers)?; @@ -325,12 +527,6 @@ impl<'a, H: VerificationHelper> Verifier<'a, H> { v.oppr = Some(PacketParserResult::Some(pp)); v.finish_maybe()?; - - // Stash signatures. - for sig in sigs.into_iter() { - v.push_sig(Packet::Signature(sig))?; - } - return Ok(v); }, _ => (), @@ -338,12 +534,19 @@ impl<'a, H: VerificationHelper> Verifier<'a, H> { 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. if let Some(issuer) = sig.get_issuer() { issuers.push(issuer); } else { issuers.push(KeyID::wildcard()); } - sigs.push(sig); + + v.structure.push_bare_signature(sig); } ppr = ppr_tmp; @@ -361,21 +564,7 @@ impl<'a, H: VerificationHelper> Verifier<'a, H> { fn push_sig(&mut self, p: Packet) -> Result<()> { match p { Packet::Signature(sig) => { - if self.sigs.is_empty() { - self.sigs.push(Vec::new()); - } - - if let Some(current_level) = self.sigs.iter().last() - .expect("sigs is never empty") - .get(0).map(|r| r.level()) - { - if current_level != sig.level() { - self.sigs.push(Vec::new()); - } - } - - self.sigs.iter_mut().last() - .expect("sigs is never empty").push(sig); + self.structure.push_signature(sig); }, _ => (), } @@ -408,38 +597,52 @@ impl<'a, H: VerificationHelper> Verifier<'a, H> { } // Verify the signatures. - let mut results = Vec::new(); - for sigs in ::std::mem::replace(&mut self.sigs, Vec::new()) - .into_iter().rev() + let mut results = MessageStructure::new(); + for layer in ::std::mem::replace(&mut self.structure, + IMessageStructure::new()) + .layers.into_iter() { - results.push(Vec::new()); - for sig in sigs.into_iter() { - results.iter_mut().last().expect("never empty").push( - if let Some(issuer) = sig.get_issuer() { - if let Some((i, j)) = self.keys.get(&issuer) { - let tpk = &self.tpks[*i]; - let (binding, revocation, key) - = tpk.keys_all().nth(*j).unwrap(); - if sig.verify(key).unwrap_or(false) && - sig.signature_alive_at(self.time) - { - VerificationResult::GoodChecksum - (sig, tpk, key, binding, revocation) + match layer { + IMessageLayer::Compression { algo } => + results.new_compression_layer(algo), + IMessageLayer::Encryption { .. } => + unreachable!("not decrypting messages"), + IMessageLayer::SignatureGroup { sigs, .. } => { + results.new_signature_group(); + for sig in sigs.into_iter() { + results.push_verification_result( + if let Some(issuer) = sig.get_issuer() { + if let Some((i, j)) = + self.keys.get(&issuer) + { + let tpk = &self.tpks[*i]; + let (binding, revocation, key) + = tpk.keys_all().nth(*j) + .unwrap(); + if sig.verify(key).unwrap_or(false) + && sig.signature_alive_at(self.time) + { + VerificationResult::GoodChecksum + (sig, tpk, key, binding, + revocation) + } else { + VerificationResult::BadChecksum + (sig) + } + } else { + VerificationResult::MissingKey(sig) + } } else { + // No issuer. VerificationResult::BadChecksum(sig) } - } else { - VerificationResult::MissingKey(sig) - } - } else { - // No issuer. - VerificationResult::BadChecksum(sig) + ) } - ) + }, } } - self.helper.check(results) + self.helper.check(&results) } else { self.oppr = Some(PacketParserResult::Some(pp)); Ok(()) @@ -706,7 +909,7 @@ impl<'a> io::Read for Transformer<'a> { /// fn get_public_keys(&mut self, _ids: &[KeyID]) -> Result<Vec<TPK>> { /// Ok(Vec::new()) // Feed the TPKs to the verifier here... /// } -/// fn check(&mut self, sigs: Vec<Vec<VerificationResult>>) -> Result<()> { +/// fn check(&mut self, structure: &MessageStructure) -> Result<()> { /// Ok(()) // Implement your verification policy here. /// } /// } @@ -844,7 +1047,7 @@ impl DetachedVerifier { /// fn get_public_keys(&mut self, _ids: &[KeyID]) -> Result<Vec<TPK>> { /// Ok(Vec::new()) // Feed the TPKs to the verifier here... /// } -/// fn check(&mut self, sigs: Vec<Vec<VerificationResult>>) -> Result<()> { +/// fn check(&mut self, structure: &MessageStructure) -> Result<()> { /// Ok(()) // Implement your verification policy here. /// } /// } @@ -891,7 +1094,7 @@ pub struct Decryptor<'a, H: VerificationHelper + DecryptionHelper> { keys: HashMap<KeyID, (usize, usize)>, oppr: Option<PacketParserResult<'a>>, identity: Option<Fingerprint>, - sigs: Vec<Vec<Signature>>, + structure: IMessageStructure, reserve: Option<Vec<u8>>, /// Signature verification relative to this time. @@ -1017,18 +1220,12 @@ impl<'a, H: VerificationHelper + DecryptionHelper> Decryptor<'a, H> { keys: HashMap::new(), oppr: None, identity: None, - sigs: Vec::new(), + structure: IMessageStructure::new(), reserve: None, time: t, }; let mut issuers = Vec::new(); - // The following structure is allowed: - // - // SIG LITERAL - // - // In this case, we queue the signature packets. - let mut sigs = Vec::new(); let mut pkesks: Vec<packet::PKESK> = Vec::new(); let mut skesks: Vec<packet::SKESK> = Vec::new(); let mut saw_content = false; @@ -1042,23 +1239,49 @@ impl<'a, H: VerificationHelper + DecryptionHelper> Decryptor<'a, H> { } match pp.packet { + Packet::CompressedData(ref p) => + v.structure.new_compression_layer(p.algorithm()), Packet::SEIP(_) | Packet::AED(_) => { saw_content = true; - v.identity = - v.helper.decrypt(&pkesks[..], &skesks[..], - |algo, secret| pp.decrypt(algo, secret) - )?; + // Get the symmetric algorithm from the decryption + // proxy function. This is necessary because we + // cannot get the algorithm from the SEIP packet. + let mut sym_algo = None; + { + let decryption_proxy = |algo, secret: &SessionKey| { + let result = pp.decrypt(algo, secret); + if let Ok(_) = result { + sym_algo = Some(algo); + } + result + }; + + v.identity = + v.helper.decrypt(&pkesks[..], &skesks[..], + decryption_proxy)?; + } if ! pp.decrypted() { // XXX: That is not quite the right error to return. return Err( Error::InvalidSessionKey("No session key".into()) .into()); } + |