diff options
Diffstat (limited to 'openpgp/src/message')
-rw-r--r-- | openpgp/src/message/grammar.lalrpop | 67 | ||||
-rw-r--r-- | openpgp/src/message/lexer.rs | 125 | ||||
-rw-r--r-- | openpgp/src/message/mod.rs | 568 |
3 files changed, 760 insertions, 0 deletions
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<Tok, Loc, Error> + = ::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<Iterator<Item=(usize, &'input Token)> + 'input>), + Owned(Box<Iterator<Item=(usize, Token)> + 'input>), +} + +impl<'input> Iterator for Lexer<'input> { + type Item = LexerItem<Token, usize, LexicalError>; + + fn next(&mut self) -> Option<Self::Item> { + 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<Self> { + 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<Self> { + 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<Packet>) -> Result<Self> { + 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<P: AsRef<Path>>(path: P) -> Result<Self> { + 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<Packet> = 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<Packet> = 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<Packet> = 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<Packet> = 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<Packet> = 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<Packet> = 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<Packet> = 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<Packet> = 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<Packet> = 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<Packet> = 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<Packet> = 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::<Vec<Tag>>() + == [ 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::<Vec<Tag>>() + == [ 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::<Vec<Tag>>() + == [ 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::<Vec<Tag>>() + == [ 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::<Vec<Tag>>() + == [ 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::<Vec<Tag>>() + == [ 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::<Vec<Tag>>() + == [ 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::<Vec<Tag>>() + == [ Tag::SKESK, Tag::PKESK, Tag::SKESK, Tag::SEIP ]); + + let message = Message::from_packets(packets.clone()); + assert!(message.is_ok(), "{:#?}", message); + } +} |