From a3abfd6bfd399bd4f366f47b527d9cd68957c018 Mon Sep 17 00:00:00 2001 From: "Neal H. Walfield" Date: Wed, 11 Jul 2018 12:02:02 +0200 Subject: openpgp: Instead of using an ad-hoc parser, use a parser generator. - Instead of using an ad-hoc parser for recognizing OpenPGP messages, use lalrpop, an lr(1) parser generator, for recognizing OpenPGP messages. --- openpgp/src/lib.rs | 2 + openpgp/src/message.rs | 633 ------------------------------------ openpgp/src/message/grammar.lalrpop | 67 ++++ openpgp/src/message/lexer.rs | 125 +++++++ openpgp/src/message/mod.rs | 568 ++++++++++++++++++++++++++++++++ 5 files changed, 762 insertions(+), 633 deletions(-) delete mode 100644 openpgp/src/message.rs create mode 100644 openpgp/src/message/grammar.lalrpop create mode 100644 openpgp/src/message/lexer.rs create mode 100644 openpgp/src/message/mod.rs (limited to 'openpgp/src') diff --git a/openpgp/src/lib.rs b/openpgp/src/lib.rs index fb727b2e..371cb3a0 100644 --- a/openpgp/src/lib.rs +++ b/openpgp/src/lib.rs @@ -40,6 +40,8 @@ #![warn(missing_docs)] +extern crate lalrpop_util; + #[macro_use] extern crate failure; diff --git a/openpgp/src/message.rs b/openpgp/src/message.rs deleted file mode 100644 index dc2148a4..00000000 --- a/openpgp/src/message.rs +++ /dev/null @@ -1,633 +0,0 @@ -use std::fmt; -use std::path::Path; -use std::iter; - -use Result; -use Error; -use Tag; -use Packet; -use PacketPile; -use Message; - -impl fmt::Debug for Message { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Message") - .field("pile", &self.pile) - .finish() - } -} - -// This is a helper function to signal that an `PacketPile` is not a -// `Message`. -macro_rules! bad { - ($msg:expr) => ({ - return Err(Error::MalformedMessage( - format!("Invalid OpenPGP message: {}", $msg.to_string()) - .into()).into()) - }); -} - -// The grammar for an encrypt part is: -// -// ESK :- Public-Key Encrypted Session Key Packet | -// Symmetric-Key Encrypted Session Key Packet. -// -// ESK Sequence :- ESK | ESK Sequence, ESK. -// -// Encrypted Data :- Symmetrically Encrypted Data Packet | -// Symmetrically Encrypted Integrity Protected Data Packet -// -// Encrypted Message :- Encrypted Data | ESK Sequence, Encrypted Data. -// -// See https://tools.ietf.org/html/rfc4880#section-11.3 -// -// In other words: zero or more ESKs followed by exactly one SEIP or -// SED packet. -fn is_encrypted_part<'a, I>(mut po: Option<&'a Packet>, mut iter: I, - depth: usize) - -> Result<()> - where I: Iterator -{ - if po.is_none() { - po = iter.next(); - } - - while let Some(p) = po { - // We match by tag so that we correctly handle Unknown - // packets. - match p.tag() { - Tag::PKESK | Tag::SKESK => (), - - tag @ Tag::SEIP | tag @ Tag::SED => { - // This has to be the last packet. - let tail : Vec<&Packet> = iter.collect(); - if tail.len() > 0 { - bad!(format!( - "{} should be the last packet in an encrypted part, \ - but followed by {} packets ({:?}).", - tag, tail.len(), - tail.iter().map(|p| p.tag()).collect::>())); - } - - // XXX: We assume that if a SEIP or SED packet has a - // body, then the body is encrypted. - if p.body.is_some() { - return Ok(()); - } else if let Some(ref children) = p.children { - return is_message(None, children.children(), depth + 1); - } else { - bad!("an encrypted part cannot be empty."); - } - }, - - tag @ _ => - bad!(format!("while parsing an encrypted part: \ - unexpected packet ({})", - tag)), - } - - po = iter.next(); - } - - bad!("encrypted part missing a SEIP or SED packet."); -} - -fn is_one_pass_signed_part<'a, I>(mut po: Option<&'a Packet>, mut iter: I, - depth: usize) - -> Result<()> - where I: Iterator -{ - if po.is_none() { - po = iter.next(); - } - - let mut ops = 0; - let mut saw_message = false; - - while let Some(p) = po { - // We match by tag so that we correctly handle Unknown - // packets. - match p.tag() { - Tag::OnePassSig => { - if saw_message { - bad!("One Pass Signature packet should not follow \ - a message."); - } - ops += 1; - }, - Tag::Signature => { - if !saw_message { - bad!("Signature packet encountered \ - before a signed message."); - } - if ops == 0 { - bad!("Unbalanced signature: more Signature than \ - One Pass Signature packets."); - } - ops -= 1; - } - _ => { - if saw_message { - bad!("A signature is only allowed over a single message."); - } - saw_message = true; - is_message(Some(p), iter::empty(), depth + 1)? - }, - } - - po = iter.next(); - } - - if !(ops == 0 && saw_message) { - bad!(format!("Unbalanced signature: missing {} signature packets", - ops)); - } - - Ok(()) -} - -fn is_message<'a, I>(mut po: Option<&'a Packet>, mut iter: I, depth: usize) - -> Result<()> - where I: Iterator -{ - if po.is_none() { - po = iter.next(); - } - - let tag = po.and_then(|p| Some(p.tag())); - - match tag { - None => - bad!("an empty message is not a valid OpenPGP message."), - - Some(Tag::PublicKey) => - bad!("it appears to be a TPK."), - - Some(Tag::SecretKey) => - bad!("it appears to be a TSK."), - - Some(Tag::PKESK) | Some(Tag::SKESK) - | Some(Tag::SEIP) | Some(Tag::SED) => - is_encrypted_part(po, iter, depth + 1), - - Some(Tag::OnePassSig) => - is_one_pass_signed_part(po, iter, depth + 1), - - Some(Tag::Signature) => { - // Signature Packet, OpenPGP Message - is_message(None, iter, depth + 1) - }, - - Some(Tag::CompressedData) => { - if iter.next().is_some() { - bad!("a compressed packet may not be \ - followed by another packet."); - } - - let p = po.unwrap(); - if p.body.is_some() { - // XXX: The body is still compressed. Assume it is - // okay. - Ok(()) - } else if let Some(ref children) = p.children { - is_message(None, children.children(), depth + 1) - } else { - bad!("empty compressed data packet."); - } - }, - - Some(Tag::Literal) => { - if iter.next().is_some() { - bad!("a literal packet may not be \ - followed by another packet."); - } - - Ok(()) - }, - - _ => { - bad!(format!("{:?} is invalid.", tag)); - }, - } -} - -impl Message { - /// Converts the `PacketPile` to a `Message`. - /// - /// Converting a `PacketPile` to a `Message` doesn't change the - /// packets; it asserts that the packet sequence is an optionally - /// encrypted, optionally signed, optionally compressed literal - /// data packet. The exact grammar is defined in [Section 11.3 of - /// RFC 4880]. - /// - /// Caveats: this function assumes that any still encrypted parts - /// or still compressed parts are valid messages. - /// - /// [Section 11.3 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-11.3 - pub fn from_packet_pile(pile: PacketPile) -> Result { - is_message(None, pile.children(), 0) - .and_then(|_| Ok(Message { pile: pile } )) - } - - /// Converts the vector of `Packets` to a `Message`. - /// - /// See [`Message::from_packets`] for more details. - /// - /// [`Message::from_packets`]: #method.from_packet_pile - pub fn from_packets(packets: Vec) -> Result { - Self::from_packet_pile(PacketPile::from_packets(packets)) - } - - /// Reads a `Message` from the specified file. - /// - /// See [`Message::from_packets`] for more details. - /// - /// [`Message::from_packets`]: #method.from_packet_pile - pub fn from_file>(path: P) -> Result { - Self::from_packet_pile(PacketPile::from_file(path)?) - } - - /// Converts the `Message` to a `PacketPile`. - pub fn to_packet_pile(self) -> PacketPile { - self.pile - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use constants::DataFormat::Text; - use HashAlgorithm; - use CompressionAlgorithm; - use SymmetricAlgorithm; - use PublicKeyAlgorithm; - use SignatureType; - use s2k::S2K; - use mpis::MPIs; - use CompressedData; - use Literal; - use OnePassSig; - use Signature; - use SKESK; - use PKESK; - use SEIP; - use KeyID; - use Container; - - #[test] - fn basic() { - // Empty. - // => bad. - let message = Message::from_packets(vec![]); - assert!(message.is_err(), "{:?}", message); - - // 0: Literal - // => good. - let mut packets = Vec::new(); - packets.push(Literal::new(Text).body(b"data".to_vec()).to_packet()); - - let message = Message::from_packets(packets); - assert!(message.is_ok(), "{:?}", message); - } - - #[test] - fn compressed_part() { - // 0: CompressedData - // 0: Literal - // => good. - let mut packets = Vec::new(); - packets.push( - CompressedData::new(CompressionAlgorithm::Uncompressed) - .push(Literal::new(Text).body(b"inner".to_vec()).to_packet()) - .to_packet()); - - let message = Message::from_packets(packets); - assert!(message.is_ok(), "{:?}", message); - - // 0: CompressedData - // 0: Literal - // 1: Literal - // => bad. - let mut packets = Vec::new(); - packets.push( - CompressedData::new(CompressionAlgorithm::Uncompressed) - .push(Literal::new(Text).body(b"inner one".to_vec()).to_packet()) - .push(Literal::new(Text).body(b"inner two".to_vec()).to_packet()) - .to_packet()); - - let message = Message::from_packets(packets); - assert!(message.is_err(), "{:?}", message); - - // 0: CompressedData - // 0: Literal - // 1: Literal - // => bad. - let mut packets = Vec::new(); - packets.push( - CompressedData::new(CompressionAlgorithm::Uncompressed) - .push(Literal::new(Text).body(b"inner".to_vec()).to_packet()) - .to_packet()); - packets.push(Literal::new(Text).body(b"outer".to_vec()).to_packet()); - - let message = Message::from_packets(packets); - assert!(message.is_err(), "{:?}", message); - - // 0: CompressedData - // 0: CompressedData - // 0: Literal - // => good. - let mut packets = Vec::new(); - packets.push( - CompressedData::new(CompressionAlgorithm::Uncompressed) - .push(CompressedData::new(CompressionAlgorithm::Uncompressed) - .push(Literal::new(Text).body(b"inner".to_vec()) - .to_packet()) - .to_packet()) - .to_packet()); - - let message = Message::from_packets(packets); - assert!(message.is_ok(), "{:?}", message); - } - - #[test] - fn one_pass_sig_part() { - // 0: OnePassSig - // => bad. - let mut packets : Vec = Vec::new(); - packets.push(OnePassSig::new(SignatureType::Binary).to_packet()); - - let message = Message::from_packets(packets); - assert!(message.is_err(), "{:?}", message); - - // 0: OnePassSig - // 1: Literal - // => bad. - let mut packets : Vec = Vec::new(); - packets.push(OnePassSig::new(SignatureType::Binary).to_packet()); - packets.push(Literal::new(Text).body(b"inner".to_vec()).to_packet()); - - let message = Message::from_packets(packets); - assert!(message.is_err(), "{:?}", message); - - // 0: OnePassSig - // 1: Literal - // 2: Signature - // => good. - let mut packets : Vec = Vec::new(); - packets.push(OnePassSig::new(SignatureType::Binary).to_packet()); - packets.push(Literal::new(Text).body(b"inner".to_vec()).to_packet()); - packets.push(Signature::new(SignatureType::Binary).to_packet()); - - let message = Message::from_packets(packets); - assert!(message.is_ok(), "{:?}", message); - - // 0: OnePassSig - // 1: Literal - // 2: Signature - // 3: Signature - // => bad. - let mut packets : Vec = Vec::new(); - packets.push(OnePassSig::new(SignatureType::Binary).to_packet()); - packets.push(Literal::new(Text).body(b"inner".to_vec()).to_packet()); - packets.push(Signature::new(SignatureType::Binary).to_packet()); - packets.push(Signature::new(SignatureType::Binary).to_packet()); - - let message = Message::from_packets(packets); - assert!(message.is_err(), "{:?}", message); - - // 0: OnePassSig - // 1: OnePassSig - // 2: Literal - // 3: Signature - // 4: Signature - // => good. - let mut packets : Vec = Vec::new(); - packets.push(OnePassSig::new(SignatureType::Binary).to_packet()); - packets.push(OnePassSig::new(SignatureType::Binary).to_packet()); - packets.push(Literal::new(Text).body(b"inner".to_vec()).to_packet()); - packets.push(Signature::new(SignatureType::Binary).to_packet()); - packets.push(Signature::new(SignatureType::Binary).to_packet()); - - let message = Message::from_packets(packets); - assert!(message.is_ok(), "{:?}", message); - - // 0: OnePassSig - // 1: OnePassSig - // 2: Literal - // 3: Literal - // 4: Signature - // 5: Signature - // => bad. - let mut packets : Vec = Vec::new(); - packets.push(OnePassSig::new(SignatureType::Binary).to_packet()); - packets.push(OnePassSig::new(SignatureType::Binary).to_packet()); - packets.push(Literal::new(Text).body(b"inner".to_vec()).to_packet()); - packets.push(Literal::new(Text).body(b"inner".to_vec()).to_packet()); - packets.push(Signature::new(SignatureType::Binary).to_packet()); - packets.push(Signature::new(SignatureType::Binary).to_packet()); - - let message = Message::from_packets(packets); - assert!(message.is_err(), "{:?}", message); - - // 0: OnePassSig - // 1: OnePassSig - // 2: CompressedData - // 0: Literal - // 3: Signature - // 4: Signature - // => good. - let mut packets : Vec = Vec::new(); - packets.push(OnePassSig::new(SignatureType::Binary).to_packet()); - packets.push(OnePassSig::new(SignatureType::Binary).to_packet()); - packets.push( - CompressedData::new(CompressionAlgorithm::Uncompressed) - .push(Literal::new(Text).body(b"inner".to_vec()).to_packet()) - .to_packet()); - packets.push(Signature::new(SignatureType::Binary).to_packet()); - packets.push(Signature::new(SignatureType::Binary).to_packet()); - - let message = Message::from_packets(packets); - assert!(message.is_ok(), "{:?}", message); - } - - #[test] - fn signature_part() { - // 0: Signature - // => bad. - let mut packets : Vec = Vec::new(); - packets.push(Signature::new(SignatureType::Binary).to_packet()); - - let message = Message::from_packets(packets); - assert!(message.is_err(), "{:?}", message); - - // 0: Signature - // 1: Literal - // => good. - let mut packets : Vec = Vec::new(); - packets.push(Signature::new(SignatureType::Binary).to_packet()); - packets.push(Literal::new(Text).body(b"inner".to_vec()).to_packet()); - - let message = Message::from_packets(packets); - assert!(message.is_ok(), "{:?}", message); - - // 0: Signature - // 1: Signature - // 2: Literal - // => good. - let mut packets : Vec = Vec::new(); - packets.push(Signature::new(SignatureType::Binary).to_packet()); - packets.push(Signature::new(SignatureType::Binary).to_packet()); - packets.push(Literal::new(Text).body(b"inner".to_vec()).to_packet()); - - let message = Message::from_packets(packets); - assert!(message.is_ok(), "{:?}", message); - } - - #[test] - fn encrypted_part() { - // There are no simple constructors for SEIP packets: they are - // interleaved with SK-ESK and PK-ESK packets. And, the - // session key needs to be managed. Instead, we use some - // internal iterfaces to progressively build up more - // complicated messages. - - // 0: SK-ESK - // => bad. - let mut packets : Vec = Vec::new(); - packets.push(SKESK::new(SymmetricAlgorithm::AES256, - S2K::Simple { hash: HashAlgorithm::SHA256 }, - &b"12345678"[..], - &b"12345678"[..]).unwrap().to_packet()); - let message = Message::from_packets(packets.clone()); - assert!(message.is_err(), "{:?}", message); - - // 0: SK-ESK - // 1: Literal - // => bad. - packets.push(Literal::new(Text).body(b"inner".to_vec()).to_packet()); - - assert!(packets.iter().map(|p| p.tag()).collect::>() - == [ Tag::SKESK, Tag::Literal ]); - - let message = Message::from_packets(packets.clone()); - assert!(message.is_err(), "{:?}", message); - - // 0: SK-ESK - // 1: SEIP - // 0: Literal - // => good. - let mut seip = SEIP { - common: Default::default(), - version: 0 - }; - seip.common.children = Some(Container::new()); - seip.common.children.as_mut().unwrap().push( - Literal::new(Text).body(b"inner".to_vec()).to_packet()); - packets[1] = Packet::SEIP(seip); - - assert!(packets.iter().map(|p| p.tag()).collect::>() - == [ Tag::SKESK, Tag::SEIP ]); - - let message = Message::from_packets(packets.clone()); - assert!(message.is_ok(), "{:#?}", message); - - // 0: SK-ESK - // 1: SEIP - // 0: Literal - // 2: SK-ESK - // => bad. - let skesk = packets[0].clone(); - packets.push(skesk); - - assert!(packets.iter().map(|p| p.tag()).collect::>() - == [ Tag::SKESK, Tag::SEIP, Tag::SKESK ]); - - let message = Message::from_packets(packets.clone()); - assert!(message.is_err(), "{:#?}", message); - - // 0: SK-ESK - // 1: SK-ESK - // 2: SEIP - // 0: Literal - // => good. - packets.swap(1, 2); - - assert!(packets.iter().map(|p| p.tag()).collect::>() - == [ Tag::SKESK, Tag::SKESK, Tag::SEIP ]); - - let message = Message::from_packets(packets.clone()); - assert!(message.is_ok(), "{:#?}", message); - - // 0: SK-ESK - // 1: SK-ESK - // 2: SEIP - // 0: Literal - // 3: SEIP - // 0: Literal - // => bad. - let seip = packets[2].clone(); - packets.push(seip); - - assert!(packets.iter().map(|p| p.tag()).collect::>() - == [ Tag::SKESK, Tag::SKESK, Tag::SEIP, Tag::SEIP ]); - - let message = Message::from_packets(packets.clone()); - assert!(message.is_err(), "{:#?}", message); - - // 0: SK-ESK - // 1: SK-ESK - // 2: SEIP - // 0: Literal - // 3: Literal - // => bad. - packets[3] - = packets[3].children.as_mut().unwrap().packets.pop().unwrap(); - - assert!(packets.iter().map(|p| p.tag()).collect::>() - == [ Tag::SKESK, Tag::SKESK, Tag::SEIP, Tag::Literal ]); - - let message = Message::from_packets(packets.clone()); - assert!(message.is_err(), "{:#?}", message); - - // 0: SK-ESK - // 1: SK-ESK - // 2: SEIP - // 0: Literal - // 1: Literal - // => bad. - packets.remove(3); - packets[2].children.as_mut().unwrap().push( - Literal::new(Text).body(b"inner two".to_vec()).to_packet()); - - assert!(packets.iter().map(|p| p.tag()).collect::>() - == [ Tag::SKESK, Tag::SKESK, Tag::SEIP ]); - - let message = Message::from_packets(packets.clone()); - assert!(message.is_err(), "{:#?}", message); - - // 0: SK-ESK - // 2: PK-ESK - // 1: SK-ESK - // 2: SEIP - // 0: Literal - // => good. - packets[2].children.as_mut().unwrap().packets.pop().unwrap(); - - packets.insert( - 1, - Packet::PKESK(PKESK { - common: Default::default(), - version: 0, - recipient: KeyID::from_hex("0000111122223333").unwrap(), - pk_algo: PublicKeyAlgorithm::RSAEncrypt, - esk: MPIs::empty() - })); - - assert!(packets.iter().map(|p| p.tag()).collect::>() - == [ Tag::SKESK, Tag::PKESK, Tag::SKESK, Tag::SEIP ]); - - let message = Message::from_packets(packets.clone()); - assert!(message.is_ok(), "{:#?}", message); - } -} diff --git a/openpgp/src/message/grammar.lalrpop b/openpgp/src/message/grammar.lalrpop new file mode 100644 index 00000000..4eebd18b --- /dev/null +++ b/openpgp/src/message/grammar.lalrpop @@ -0,0 +1,67 @@ +// -*- mode: Rust; -*- + +use message::lexer; + +grammar; + +pub Message: () = { + LITERAL, + CompressedData, + EncryptedPart, + SignedPart, + OPAQUE_CONTENT, +}; + +CompressedData: () = { + COMPRESSED_DATA Message POP +}; + +SeipPart: () = { + SEIP Message POP, +} + +// An encrypted part is 0 or more ESKs followed by a SEIP packet. +EncryptedPart: () = { + SeipPart, + ESKS SeipPart, +}; + +ESKS: () = { + ESK, + ESKS ESK, +}; + +ESK: () = { + PKESK, + SKESK, +}; + +// Signatures bracket a message like so: +// +// OPS OPS Message SIG SIG +// +// or, there are 1 or more signatures preceding a Message (this is an +// artifact of old PGP versions): +// +// SIG SIG Message +SignedPart: () = { + SIG Message, + OPS Message SIG, +} + +extern { + type Location = usize; + type Error = lexer::LexicalError; + + enum lexer::Token { + LITERAL => lexer::Token::Literal, + COMPRESSED_DATA => lexer::Token::CompressedData, + SKESK => lexer::Token::SKESK, + PKESK => lexer::Token::PKESK, + SEIP => lexer::Token::SEIP, + OPS => lexer::Token::OPS, + SIG => lexer::Token::SIG, + POP => lexer::Token::Pop, + OPAQUE_CONTENT => lexer::Token::OpaqueContent, + } +} diff --git a/openpgp/src/message/lexer.rs b/openpgp/src/message/lexer.rs new file mode 100644 index 00000000..442399d4 --- /dev/null +++ b/openpgp/src/message/lexer.rs @@ -0,0 +1,125 @@ +use Error; +use Result; + +use Packet; +use PacketPile; + +// The type of the parser's input. +// +// The parser iterators over tuples consisting of the token's starting +// position, the token itself, and the token's ending position. +pub(crate) type LexerItem + = ::std::result::Result<(Loc, Tok, Loc), Error>; + +#[derive(Debug, Clone)] +pub enum Token { + Literal, + CompressedData, + + SKESK, + PKESK, + SEIP, + + OPS, + SIG, + + Pop, + + // This represents the content of a container that is not parsed. + OpaqueContent, +} + +#[derive(Debug)] +pub enum LexicalError { + // There are no lexing errors. +} + +pub(crate) enum Lexer<'input> { + Refed(Box + 'input>), + Owned(Box + 'input>), +} + +impl<'input> Iterator for Lexer<'input> { + type Item = LexerItem; + + fn next(&mut self) -> Option { + let n = match self { + Lexer::Refed(ref mut i) => + i.next().map(|(pos, tok)| (pos, tok.clone())), + Lexer::Owned(ref mut i) => i.next(), + }; + + if let Some((pos, tok)) = n { + Some(Ok((pos, tok, pos))) + } else { + None + } + } +} + +impl<'input> Lexer<'input> { + /// Uses a raw sequence of tokens as input to the parser. + // This is only used in the test code. It would be better to use + // cfg(test), but then we have to do the same for the Lexer enum + // above and then we also have to specialize Lexer::next(). This + // is significantly less ugly. + #[allow(unused)] + pub(crate) fn from_tokens(raw: &'input [Token]) -> Self { + let iter = raw.iter().enumerate(); + Lexer::Refed(Box::new(iter)) + } + + /// Uses a `PacketPile` as input to the parser. + pub(crate) fn from_packet_pile(pp: &'input PacketPile) -> Result { + let mut t = vec![]; + let mut last_path = vec![0]; + + for (path, p) in pp.descendants().paths() { + if last_path.len() > path.len() { + // We popped one or more containers. + for _ in 1..last_path.len() - path.len() + 1 { + t.push(Token::Pop); + } + } + last_path = path; + + match p { + Packet::Literal(_) => t.push(Token::Literal), + Packet::CompressedData(_) => t.push(Token::CompressedData), + Packet::SKESK(_) => t.push(Token::SKESK), + Packet::PKESK(_) => t.push(Token::PKESK), + Packet::SEIP(_) => t.push(Token::SEIP), + Packet::OnePassSig(_) => t.push(Token::OPS), + Packet::Signature(_) => t.push(Token::SIG), + + p => + return Err(Error::MalformedMessage( + format!("Invalid OpenPGP message: \ + unexpected packet: {:?}", + p.tag()).into()).into()), + } + + match p { + Packet::CompressedData(_) | Packet::SEIP(_) => { + // If a container's content is not unpacked, then + // we treat the content as an opaque message. + + if p.children.is_none() && p.body.is_some() { + t.push(Token::OpaqueContent); + t.push(Token::Pop); + } + } + _ => {} + } + } + + if last_path.len() > 1 { + // We popped one or more containers. + for _ in 1..last_path.len() { + t.push(Token::Pop); + } + } + + Ok(Lexer::Owned(Box::new(t.into_iter().enumerate()))) + } +} diff --git a/openpgp/src/message/mod.rs b/openpgp/src/message/mod.rs new file mode 100644 index 00000000..0651390a --- /dev/null +++ b/openpgp/src/message/mod.rs @@ -0,0 +1,568 @@ +use std::fmt; +use std::path::Path; + +use Result; +use Error; +use Packet; +use PacketPile; +use Message; + +mod lexer; +mod grammar; + +use self::lexer::Lexer; +use self::grammar::MessageParser; + +impl fmt::Debug for Message { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Message") + .field("pile", &self.pile) + .finish() + } +} + +impl Message { + /// Converts the `PacketPile` to a `Message`. + /// + /// Converting a `PacketPile` to a `Message` doesn't change the + /// packets; it asserts that the packet sequence is an optionally + /// encrypted, optionally signed, optionally compressed literal + /// data packet. The exact grammar is defined in [Section 11.3 of + /// RFC 4880]. + /// + /// Caveats: this function assumes that any still encrypted parts + /// or still compressed parts are valid messages. + /// + /// [Section 11.3 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-11.3 + pub fn from_packet_pile(pile: PacketPile) -> Result { + let r = MessageParser::new().parse(Lexer::from_packet_pile(&pile)?); + match r { + Ok(_) => Ok(Message { pile: pile }), + /// We really want to squash the lexer's error: it is an + /// internal detail that may change, and meaningless even + /// to an immediate user of this crate. + Err(err) => Err(Error::MalformedMessage( + format!("Invalid OpenPGP message: {:?}", err).into()).into()) + } + } + + /// Converts the vector of `Packets` to a `Message`. + /// + /// See [`Message::from_packets`] for more details. + /// + /// [`Message::from_packets`]: #method.from_packet_pile + pub fn from_packets(packets: Vec) -> Result { + Self::from_packet_pile(PacketPile::from_packets(packets)) + } + + /// Reads a `Message` from the specified file. + /// + /// See [`Message::from_packets`] for more details. + /// + /// [`Message::from_packets`]: #method.from_packet_pile + pub fn from_file>(path: P) -> Result { + Self::from_packet_pile(PacketPile::from_file(path)?) + } + + /// Converts the `Message` to a `PacketPile`. + pub fn to_packet_pile(self) -> PacketPile { + self.pile + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use constants::DataFormat::Text; + use HashAlgorithm; + use CompressionAlgorithm; + use SymmetricAlgorithm; + use PublicKeyAlgorithm; + use SignatureType; + use s2k::S2K; + use mpis::MPIs; + use Tag; + use CompressedData; + use Literal; + use OnePassSig; + use Signature; + use SKESK; + use PKESK; + use SEIP; + use KeyID; + use Container; + + #[test] + fn tokens() { + use self::lexer::{Token, Lexer}; + use self::lexer::Token::*; + use self::grammar::MessageParser; + + struct TestVector<'a> { + s: &'a [Token], + result: bool, + } + + let test_vectors = [ + TestVector { + s: &[Literal][..], + result: true, + }, + TestVector { + s: &[CompressedData, Literal, Pop], + result: true, + }, + TestVector { + s: &[CompressedData, CompressedData, Literal, + Pop, Pop], + result: true, + }, + TestVector { + s: &[SEIP, Literal, Pop], + result: true, + }, + TestVector { + s: &[CompressedData, SEIP, Literal, Pop, Pop], + result: true, + }, + TestVector { + s: &[CompressedData, SEIP, CompressedData, Literal, + Pop, Pop, Pop], + result: true, + }, + TestVector { + s: &[SEIP, Pop], + result: false, + }, + TestVector { + s: &[SKESK, SEIP, Literal, Pop], + result: true, + }, + TestVector { + s: &[PKESK, SEIP, Literal, Pop], + result: true, + }, + TestVector { + s: &[SKESK, SKESK, SEIP, Literal, Pop], + result: true, + }, + + TestVector { + s: &[OPS, Literal, SIG], + result: true, + }, + TestVector { + s: &[OPS, OPS, Literal, SIG, SIG], + result: true, + }, + TestVector { + s: &[OPS, OPS, Literal, SIG], + result: false, + }, + TestVector { + s: &[OPS, OPS, SEIP, OPS, SEIP, Literal, Pop, + SIG, Pop, SIG, SIG], + result: true, + }, + + TestVector { + s: &[CompressedData, OpaqueContent], + result: false, + }, + TestVector { + s: &[CompressedData, OpaqueContent, Pop], + result: true, + }, + TestVector { + s: &[CompressedData, CompressedData, OpaqueContent, Pop, Pop], + result: true, + }, + TestVector { + s: &[SEIP, CompressedData, OpaqueContent, Pop, Pop], + result: true, + }, + TestVector { + s: &[SEIP, OpaqueContent, Pop], + result: true, + }, + ]; + + for v in test_vectors.into_iter() { + eprintln!("Parsing: {:?}", v.s); + match MessageParser::new().parse(Lexer::from_tokens(v.s)) + { + Ok(r) => { + println!("Parsed as {:?} {}", + r, + if v.result { "(expected)" } + else { "UNEXPECTED!" }); + assert!(v.result); + }, + Err(e) => { + println!("Parse error: {:?} {}", + e, + if v.result { "UNEXPECTED!" } + else { "(expected)" }); + assert!(! v.result); + } + } + } + } + + #[test] + fn basic() { + // Empty. + // => bad. + let message = Message::from_packets(vec![]); + assert!(message.is_err(), "{:?}", message); + + // 0: Literal + // => good. + let mut packets = Vec::new(); + packets.push(Literal::new(Text).body(b"data".to_vec()).to_packet()); + + let message = Message::from_packets(packets); + assert!(message.is_ok(), "{:?}", message); + } + + #[test] + fn compressed_part() { + // 0: CompressedData + // 0: Literal + // => good. + let mut packets = Vec::new(); + packets.push( + CompressedData::new(CompressionAlgorithm::Uncompressed) + .push(Literal::new(Text).body(b"inner".to_vec()).to_packet()) + .to_packet()); + + let message = Message::from_packets(packets); + assert!(message.is_ok(), "{:?}", message); + + // 0: CompressedData + // 0: Literal + // 1: Literal + // => bad. + let mut packets = Vec::new(); + packets.push( + CompressedData::new(CompressionAlgorithm::Uncompressed) + .push(Literal::new(Text).body(b"inner one".to_vec()).to_packet()) + .push(Literal::new(Text).body(b"inner two".to_vec()).to_packet()) + .to_packet()); + + let message = Message::from_packets(packets); + assert!(message.is_err(), "{:?}", message); + + // 0: CompressedData + // 0: Literal + // 1: Literal + // => bad. + let mut packets = Vec::new(); + packets.push( + CompressedData::new(CompressionAlgorithm::Uncompressed) + .push(Literal::new(Text).body(b"inner".to_vec()).to_packet()) + .to_packet()); + packets.push(Literal::new(Text).body(b"outer".to_vec()).to_packet()); + + let message = Message::from_packets(packets); + assert!(message.is_err(), "{:?}", message); + + // 0: CompressedData + // 0: CompressedData + // 0: Literal + // => good. + let mut packets = Vec::new(); + packets.push( + CompressedData::new(CompressionAlgorithm::Uncompressed) + .push(CompressedData::new(CompressionAlgorithm::Uncompressed) + .push(Literal::new(Text).body(b"inner".to_vec()) + .to_packet()) + .to_packet()) + .to_packet()); + + let message = Message::from_packets(packets); + assert!(message.is_ok(), "{:?}", message); + } + + #[test] + fn one_pass_sig_part() { + // 0: OnePassSig + // => bad. + let mut packets : Vec = Vec::new(); + packets.push(OnePassSig::new(SignatureType::Binary).to_packet()); + + let message = Message::from_packets(packets); + assert!(message.is_err(), "{:?}", message); + + // 0: OnePassSig + // 1: Literal + // => bad. + let mut packets : Vec = Vec::new(); + packets.push(OnePassSig::new(SignatureType::Binary).to_packet()); + packets.push(Literal::new(Text).body(b"inner".to_vec()).to_packet()); + + let message = Message::from_packets(packets); + assert!(message.is_err(), "{:?}", message); + + // 0: OnePassSig + // 1: Literal + // 2: Signature + // => good. + let mut packets : Vec = Vec::new(); + packets.push(OnePassSig::new(SignatureType::Binary).to_packet()); + packets.push(Literal::new(Text).body(b"inner".to_vec()).to_packet()); + packets.push(Signature::new(SignatureType::Binary).to_packet()); + + let message = Message::from_packets(packets); + assert!(message.is_ok(), "{:?}", message); + + // 0: OnePassSig + // 1: Literal + // 2: Signature + // 3: Signature + // => bad. + let mut packets : Vec = Vec::new(); + packets.push(OnePassSig::new(SignatureType::Binary).to_packet()); + packets.push(Literal::new(Text).body(b"inner".to_vec()).to_packet()); + packets.push(Signature::new(SignatureType::Binary).to_packet()); + packets.push(Signature::new(SignatureType::Binary).to_packet()); + + let message = Message::from_packets(packets); + assert!(message.is_err(), "{:?}", message); + + // 0: OnePassSig + // 1: OnePassSig + // 2: Literal + // 3: Signature + // 4: Signature + // => good. + let mut packets : Vec = Vec::new(); + packets.push(OnePassSig::new(SignatureType::Binary).to_packet()); + packets.push(OnePassSig::new(SignatureType::Binary).to_packet()); + packets.push(Literal::new(Text).body(b"inner".to_vec()).to_packet()); + packets.push(Signature::new(SignatureType::Binary).to_packet()); + packets.push(Signature::new(SignatureType::Binary).to_packet()); + + let message = Message::from_packets(packets); + assert!(message.is_ok(), "{:?}", message); + + // 0: OnePassSig + // 1: OnePassSig + // 2: Literal + // 3: Literal + // 4: Signature + // 5: Signature + // => bad. + let mut packets : Vec = Vec::new(); + packets.push(OnePassSig::new(SignatureType::Binary).to_packet()); + packets.push(OnePassSig::new(SignatureType::Binary).to_packet()); + packets.push(Literal::new(Text).body(b"inner".to_vec()).to_packet()); + packets.push(Literal::new(Text).body(b"inner".to_vec()).to_packet()); + packets.push(Signature::new(SignatureType::Binary).to_packet()); + packets.push(Signature::new(SignatureType::Binary).to_packet()); + + let message = Message::from_packets(packets); + assert!(message.is_err(), "{:?}", message); + + // 0: OnePassSig + // 1: OnePassSig + // 2: CompressedData + // 0: Literal + // 3: Signature + // 4: Signature + // => good. + let mut packets : Vec = Vec::new(); + packets.push(OnePassSig::new(SignatureType::Binary).to_packet()); + packets.push(OnePassSig::new(SignatureType::Binary).to_packet()); + packets.push( + CompressedData::new(CompressionAlgorithm::Uncompressed) + .push(Literal::new(Text).body(b"inner".to_vec()).to_packet()) + .to_packet()); + packets.push(Signature::new(SignatureType::Binary).to_packet()); + packets.push(Signature::new(SignatureType::Binary).to_packet()); + + let message = Message::from_packets(packets); + assert!(message.is_ok(), "{:?}", message); + } + + #[test] + fn signature_part() { + // 0: Signature + // => bad. + let mut packets : Vec = Vec::new(); + packets.push(Signature::new(SignatureType::Binary).to_packet()); + + let message = Message::from_packets(packets); + assert!(message.is_err(), "{:?}", message); + + // 0: Signature + // 1: Literal + // => good. + let mut packets : Vec = Vec::new(); + packets.push(Signature::new(SignatureType::Binary).to_packet()); + packets.push(Literal::new(Text).body(b"inner".to_vec()).to_packet()); + + let message = Message::from_packets(packets); + assert!(message.is_ok(), "{:?}", message); + + // 0: Signature + // 1: Signature + // 2: Literal + // => good. + let mut packets : Vec = Vec::new(); + packets.push(Signature::new(SignatureType::Binary).to_packet()); + packets.push(Signature::new(SignatureType::Binary).to_packet()); + packets.push(Literal::new(Text).body(b"inner".to_vec()).to_packet()); + + let message = Message::from_packets(packets); + assert!(message.is_ok(), "{:?}", message); + } + + #[test] + fn encrypted_part() { + // There are no simple constructors for SEIP packets: they are + // interleaved with SK-ESK and PK-ESK packets. And, the + // session key needs to be managed. Instead, we use some + // internal iterfaces to progressively build up more + // complicated messages. + + // 0: SK-ESK + // => bad. + let mut packets : Vec = Vec::new(); + packets.push(SKESK::new(SymmetricAlgorithm::AES256, + S2K::Simple { hash: HashAlgorithm::SHA256 }, + &b"12345678"[..], + &b"12345678"[..]).unwrap().to_packet()); + let message = Message::from_packets(packets.clone()); + assert!(message.is_err(), "{:?}", message); + + // 0: SK-ESK + // 1: Literal + // => bad. + packets.push(Literal::new(Text).body(b"inner".to_vec()).to_packet()); + + assert!(packets.iter().map(|p| p.tag()).collect::>() + == [ Tag::SKESK, Tag::Literal ]); + + let message = Message::from_packets(packets.clone()); + assert!(message.is_err(), "{:?}", message); + + // 0: SK-ESK + // 1: SEIP + // 0: Literal + // => good. + let mut seip = SEIP { + common: Default::default(), + version: 0 + }; + seip.common.children = Some(Container::new()); + seip.common.children.as_mut().unwrap().push( + Literal::new(Text).body(b"inner".to_vec()).to_packet()); + packets[1] = Packet::SEIP(seip); + + assert!(packets.iter().map(|p| p.tag()).collect::>() + == [ Tag::SKESK, Tag::SEIP ]); + + let message = Message::from_packets(packets.clone()); + assert!(message.is_ok(), "{:#?}", message); + + // 0: SK-ESK + // 1: SEIP + // 0: Literal + // 2: SK-ESK + // => bad. + let skesk = packets[0].clone(); + packets.push(skesk); + + assert!(packets.iter().map(|p| p.tag()).collect::>() + == [ Tag::SKESK, Tag::SEIP, Tag::SKESK ]); + + let message = Message::from_packets(packets.clone()); + assert!(message.is_err(), "{:#?}", message); + + // 0: SK-ESK + // 1: SK-ESK + // 2: SEIP + // 0: Literal + // => good. + packets.swap(1, 2); + + assert!(packets.iter().map(|p| p.tag()).collect::>() + == [ Tag::SKESK, Tag::SKESK, Tag::SEIP ]); + + let message = Message::from_packets(packets.clone()); + assert!(message.is_ok(), "{:#?}", message); + + // 0: SK-ESK + // 1: SK-ESK + // 2: SEIP + // 0: Literal + // 3: SEIP + // 0: Literal + // => bad. + let seip = packets[2].clone(); + packets.push(seip); + + assert!(packets.iter().map(|p| p.tag()).collect::>() + == [ Tag::SKESK, Tag::SKESK, Tag::SEIP, Tag::SEIP ]); + + let message = Message::from_packets(packets.clone()); + assert!(message.is_err(), "{:#?}", message); + + // 0: SK-ESK + // 1: SK-ESK + // 2: SEIP + // 0: Literal + // 3: Literal + // => bad. + packets[3] + = packets[3].children.as_mut().unwrap().packets.pop().unwrap(); + + assert!(packets.iter().map(|p| p.tag()).collect::>() + == [ Tag::SKESK, Tag::SKESK, Tag::SEIP, Tag::Literal ]); + + let message = Message::from_packets(packets.clone()); + assert!(message.is_err(), "{:#?}", message); + + // 0: SK-ESK + // 1: SK-ESK + // 2: SEIP + // 0: Literal + // 1: Literal + // => bad. + packets.remove(3); + packets[2].children.as_mut().unwrap().push( + Literal::new(Text).body(b"inner two".to_vec()).to_packet()); + + assert!(packets.iter().map(|p| p.tag()).collect::>() + == [ Tag::SKESK, Tag::SKESK, Tag::SEIP ]); + + let message = Message::from_packets(packets.clone()); + assert!(message.is_err(), "{:#?}", message); + + // 0: SK-ESK + // 2: PK-ESK + // 1: SK-ESK + // 2: SEIP + // 0: Literal + // => good. + packets[2].children.as_mut().unwrap().packets.pop().unwrap(); + + packets.insert( + 1, + Packet::PKESK(PKESK { + common: Default::default(), + version: 0, + recipient: KeyID::from_hex("0000111122223333").unwrap(), + pk_algo: PublicKeyAlgorithm::RSAEncrypt, + esk: MPIs::empty() + })); + + assert!(packets.iter().map(|p| p.tag()).collect::>() + == [ Tag::SKESK, Tag::PKESK, Tag::SKESK, Tag::SEIP ]); + + let message = Message::from_packets(packets.clone()); + assert!(message.is_ok(), "{:#?}", message); + } +} -- cgit v1.2.3