diff options
-rw-r--r-- | examples/guide-exploring-openpgp.rs | 6 | ||||
-rw-r--r-- | ffi/src/openpgp.rs | 83 | ||||
-rw-r--r-- | openpgp/src/armor.rs | 4 | ||||
-rw-r--r-- | openpgp/src/lib.rs | 16 | ||||
-rw-r--r-- | openpgp/src/message.rs | 490 | ||||
-rw-r--r-- | openpgp/src/packet.rs | 29 | ||||
-rw-r--r-- | openpgp/src/parse/key.rs | 6 | ||||
-rw-r--r-- | openpgp/src/parse/message_parser.rs | 317 | ||||
-rw-r--r-- | openpgp/src/parse/mod.rs | 28 | ||||
-rw-r--r-- | openpgp/src/parse/parse.rs | 4 | ||||
-rw-r--r-- | openpgp/src/parse/subpacket.rs | 36 | ||||
-rw-r--r-- | openpgp/src/serialize/mod.rs | 44 | ||||
-rw-r--r-- | openpgp/src/serialize/stream.rs | 24 | ||||
-rw-r--r-- | openpgp/src/tpk.rs | 16 | ||||
-rw-r--r-- | tool/src/sqv.rs | 6 |
15 files changed, 153 insertions, 956 deletions
diff --git a/examples/guide-exploring-openpgp.rs b/examples/guide-exploring-openpgp.rs index bc05a7e1..a2bd1517 100644 --- a/examples/guide-exploring-openpgp.rs +++ b/examples/guide-exploring-openpgp.rs @@ -38,10 +38,10 @@ fn main() { ); // Parse message. - let message = openpgp::Message::from_reader(&mut reader).unwrap(); + let pile = openpgp::PacketPile::from_reader(&mut reader).unwrap(); // Iterate over children. - for (i, p) in message.children().enumerate() { + for (i, p) in pile.children().enumerate() { println!("{}: {:?}", i, p); } @@ -49,7 +49,7 @@ fn main() { println!(); // Parse into TPK. - let tpk = openpgp::TPK::from_message(message).unwrap(); + let tpk = openpgp::TPK::from_packet_pile(pile).unwrap(); println!("Fingerprint: {}", tpk.fingerprint()); // List userids. diff --git a/ffi/src/openpgp.rs b/ffi/src/openpgp.rs index 8bddf426..fb213cdc 100644 --- a/ffi/src/openpgp.rs +++ b/ffi/src/openpgp.rs @@ -10,7 +10,7 @@ use libc::{uint8_t, uint64_t, c_char, c_int, size_t}; extern crate openpgp; -use self::openpgp::{armor, Fingerprint, KeyID, Message, TPK, Packet}; +use self::openpgp::{armor, Fingerprint, KeyID, PacketPile, TPK, Packet}; use self::openpgp::parse::{PacketParser}; use self::openpgp::serialize::Serialize; @@ -229,88 +229,88 @@ pub extern "system" fn sq_armor_writer_new(inner: Option<&'static mut Box<Write> } -/* openpgp::Message. */ +/* openpgp::PacketPile. */ /// Deserializes the OpenPGP message stored in a `std::io::Read` /// object. /// /// Although this method is easier to use to parse an OpenPGP -/// message than a `PacketParser` or a `MessageParser`, this +/// message than a `PacketParser` or a `PacketPileParser`, this /// interface buffers the whole message in memory. Thus, the /// caller must be certain that the *deserialized* message is not /// too large. /// /// Note: this interface *does* buffer the contents of packets. #[no_mangle] -pub extern "system" fn sq_message_from_reader(ctx: Option<&mut Context>, - reader: Option<&mut Box<Read>>) - -> *mut Message { +pub extern "system" fn sq_packet_pile_from_reader(ctx: Option<&mut Context>, + reader: Option<&mut Box<Read>>) + -> *mut PacketPile { let ctx = ctx.expect("Context is NULL"); let reader = reader.expect("Reader is NULL"); - fry_box!(ctx, Message::from_reader(reader)) + fry_box!(ctx, PacketPile::from_reader(reader)) } /// Deserializes the OpenPGP message stored in the file named by /// `filename`. /// -/// See `sq_message_from_reader` for more details and caveats. +/// See `sq_packet_pile_from_reader` for more details and caveats. #[no_mangle] -pub extern "system" fn sq_message_from_file(ctx: Option<&mut Context>, - filename: *const c_char) - -> *mut Message { +pub extern "system" fn sq_packet_pile_from_file(ctx: Option<&mut Context>, + filename: *const c_char) + -> *mut PacketPile { let ctx = ctx.expect("Context is NULL"); assert!(! filename.is_null()); let filename = unsafe { CStr::from_ptr(filename).to_string_lossy().into_owned() }; - fry_box!(ctx, Message::from_file(&filename)) + fry_box!(ctx, PacketPile::from_file(&filename)) } /// Deserializes the OpenPGP message stored in the provided buffer. /// -/// See `sq_message_from_reader` for more details and caveats. +/// See `sq_packet_pile_from_reader` for more details and caveats. #[no_mangle] -pub extern "system" fn sq_message_from_bytes(ctx: Option<&mut Context>, - b: *const uint8_t, len: size_t) - -> *mut Message { +pub extern "system" fn sq_packet_pile_from_bytes(ctx: Option<&mut Context>, + b: *const uint8_t, len: size_t) + -> *mut PacketPile { let ctx = ctx.expect("Context is NULL"); assert!(!b.is_null()); let buf = unsafe { slice::from_raw_parts(b, len as usize) }; - fry_box!(ctx, Message::from_bytes(buf)) + fry_box!(ctx, PacketPile::from_bytes(buf)) } -/// Frees the message. +/// Frees the packet_pile. #[no_mangle] -pub extern "system" fn sq_message_free(message: *mut Message) { - if message.is_null() { +pub extern "system" fn sq_packet_pile_free(packet_pile: *mut PacketPile) { + if packet_pile.is_null() { return } unsafe { - drop(Box::from_raw(message)); + drop(Box::from_raw(packet_pile)); } } -/// Clones the Message. +/// Clones the PacketPile. #[no_mangle] -pub extern "system" fn sq_message_clone(message: Option<&Message>) - -> *mut Message { - let message = message.expect("Message is NULL"); - box_raw!(message.clone()) +pub extern "system" fn sq_packet_pile_clone(packet_pile: Option<&PacketPile>) + -> *mut PacketPile { + let packet_pile = packet_pile.expect("PacketPile is NULL"); + box_raw!(packet_pile.clone()) } -/// Serializes the message. +/// Serializes the packet pile. #[no_mangle] -pub extern "system" fn sq_message_serialize(ctx: Option<&mut Context>, - message: Option<&Message>, - writer: Option<&mut Box<Write>>) - -> Status { +pub extern "system" fn sq_packet_pile_serialize(ctx: Option<&mut Context>, + packet_pile: Option<&PacketPile>, + writer: Option<&mut Box<Write>>) + -> Status { let ctx = ctx.expect("Context is NULL"); - let message = message.expect("Message is NULL"); + let packet_pile = packet_pile.expect("PacketPile is NULL"); let writer = writer.expect("Writer is NULL"); - fry_status!(ctx, message.serialize(writer)) + fry_status!(ctx, packet_pile.serialize(writer)) } @@ -343,18 +343,18 @@ pub extern "system" fn sq_tpk_from_file(ctx: Option<&mut Context>, /// /// Consumes `m`. #[no_mangle] -pub extern "system" fn sq_tpk_from_message(ctx: Option<&mut Context>, - m: *mut Message) - -> *mut TPK { +pub extern "system" fn sq_tpk_from_packet_pile(ctx: Option<&mut Context>, + m: *mut PacketPile) + -> *mut TPK { let ctx = ctx.expect("Context is NULL"); assert!(! m.is_null()); let m = unsafe { Box::from_raw(m) }; - fry_box!(ctx, TPK::from_message(*m)) + fry_box!(ctx, TPK::from_packet_pile(*m)) } /// Returns the first TPK found in `buf`. /// -/// `buf` must be an OpenPGP encoded message. +/// `buf` must be an OpenPGP-encoded TPK. #[no_mangle] pub extern "system" fn sq_tpk_from_bytes(ctx: Option<&mut Context>, b: *const uint8_t, len: size_t) @@ -516,7 +516,8 @@ pub extern "system" fn sq_skesk_decrypt(ctx: Option<&mut Context>, /* openpgp::parse. */ -/// Starts parsing an OpenPGP message stored in a `sq_reader_t` object. +/// Starts parsing OpenPGP packets stored in a `sq_reader_t` +/// object. /// /// This function returns a `PacketParser` for the first packet in /// the stream. @@ -531,7 +532,7 @@ pub extern "system" fn sq_packet_parser_from_reader<'a> .unwrap_or(ptr::null_mut()) } -/// Starts parsing an OpenPGP message stored in a file named `path`. +/// Starts parsing OpenPGP packets stored in a file named `path`. /// /// This function returns a `PacketParser` for the first packet in /// the stream. @@ -549,7 +550,7 @@ pub extern "system" fn sq_packet_parser_from_file .unwrap_or(ptr::null_mut()) } -/// Starts parsing an OpenPGP message stored in a buffer. +/// Starts parsing OpenPGP packets stored in a buffer. /// /// This function returns a `PacketParser` for the first packet in /// the stream. diff --git a/openpgp/src/armor.rs b/openpgp/src/armor.rs index d85c90eb..eb1e6a53 100644 --- a/openpgp/src/armor.rs +++ b/openpgp/src/armor.rs @@ -50,7 +50,9 @@ const LINE_ENDING: &str = "\n"; /// [RFC 4880, section 6.2]: https://tools.ietf.org/html/rfc4880#section-6.2 #[derive(Copy, Clone, Debug, PartialEq)] pub enum Kind { - /// A generic OpenPGP message. + /// A generic OpenPGP message. (Since its structure hasn't been + /// validated, in this crate's terminology, this is just a + /// `PacketPile`.) Message, /// A transferable public key. PublicKey, diff --git a/openpgp/src/lib.rs b/openpgp/src/lib.rs index 55efb498..09ff0112 100644 --- a/openpgp/src/lib.rs +++ b/openpgp/src/lib.rs @@ -95,7 +95,7 @@ pub use pkesk::PKESK; mod reader; pub use reader::Reader; -mod message; +mod packet_pile; pub mod constants; use constants::{ PublicKeyAlgorithm, @@ -340,9 +340,9 @@ pub struct UserAttribute { /// /// A literal packet contains unstructured data. Since the size can /// be very large, it is advised to process messages containing such -/// packets using a `PacketParser` or a `MessageParser` and process +/// packets using a `PacketParser` or a `PacketPileParser` and process /// the data in a streaming manner rather than the using the -/// `Message::from_file` and related interfaces. +/// `PacketPile::from_file` and related interfaces. /// /// See [Section 5.9 of RFC 4880] for details. /// @@ -474,17 +474,17 @@ impl Packet { } } -/// A `Message` holds a deserialized OpenPGP message. +/// A `PacketPile` holds a deserialized sequence of OpenPGP messages. /// /// To deserialize an OpenPGP usage, use either [`PacketParser`], -/// [`MessageParser`], or [`Message::from_file`] (or related +/// [`PacketPileParser`], or [`PacketPile::from_file`] (or related /// routines). /// /// [`PacketParser`]: parse/struct.PacketParser.html -/// [`MessageParser`]: parse/struct.MessageParser.html -/// [`Message::from_file`]: struct.Message.html#method.from_file +/// [`PacketPileParser`]: parse/struct.PacketPileParser.html +/// [`PacketPile::from_file`]: struct.PacketPile.html#method.from_file #[derive(PartialEq, Clone)] -pub struct Message { +pub struct PacketPile { // At the top level, we have a sequence of packets, which may be // containers. top_level: Container, diff --git a/openpgp/src/message.rs b/openpgp/src/message.rs deleted file mode 100644 index f07e52ae..00000000 --- a/openpgp/src/message.rs +++ /dev/null @@ -1,490 +0,0 @@ -use std::fmt; -use std::slice; -use std::vec; -use std::io; -use std::path::Path; -use std::fs::File; - -use buffered_reader::BufferedReader; -use buffered_reader::BufferedReaderGeneric; -use buffered_reader::BufferedReaderMemory; - -use Result; -use Packet; -use packet::{Container, PacketIter}; -use Message; -use parse::PacketParser; -use parse::PacketParserBuilder; -use parse::Cookie; - -#[cfg(test)] -macro_rules! bytes { - ( $x:expr ) => { include_bytes!(concat!("../tests/data/messages/", $x)) }; -} - -#[cfg(test)] -use std::path::PathBuf; - -#[cfg(test)] -fn path_to(artifact: &str) -> PathBuf { - [env!("CARGO_MANIFEST_DIR"), "tests", "data", "messages", artifact] - .iter().collect() -} - -impl fmt::Debug for Message { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Message") - .field("packets", &self.top_level.packets) - .finish() - } -} - -impl Message { - /// Turns a vector of [`Packet`s] into a `Message`. - /// - /// This is a simple wrapper function; it does not process the - /// packets in any way. - /// - /// [`Packet`s]: enum.Packet.html - pub fn from_packets(p: Vec<Packet>) -> Self { - Message { top_level: Container { packets: p } } - } - - /// Turns a [`Packet`] into a `Message`. - /// - /// This is a simple wrapper function; it does not process the - /// packets in any way. - /// - /// [`Packet`]: enum.Packet.html - pub fn from_packet(p: Packet) -> Self { - let mut top_level = Vec::with_capacity(1); - top_level.push(p); - Self::from_packets(top_level) - } - - /// Pretty prints the message to stderr. - /// - /// This function is primarily intended for debugging purposes. - pub fn pretty_print(&self) { - self.top_level.pretty_print(0); - } - - /// Returns the packet at the location described by `pathspec`. - /// - /// `pathspec` is a slice of the form `[ 0, 1, 2 ]`. Each element - /// is the index of packet in a container. Thus, the previous - /// path specification means: return the third child of the second - /// child of the first top-level packet. In other words, the - /// starred packet in the following tree: - /// - /// ```text - /// Message - /// / | \ - /// 0 1 2 ... - /// / \ - /// / \ - /// 0 1 ... - /// / | \ ... - /// 0 1 2 - /// * - /// ``` - /// - /// And, `[ 10 ]` means return the 11th top-level packet. - /// - /// Note: there is no packet at the root. Thus, the path `[]` - /// returns None. - pub fn path_ref(&self, pathspec: &[usize]) -> Option<&Packet> { - let mut packet : Option<&Packet> = None; - - let mut cont = Some(&self.top_level); - for i in pathspec { - if let Some(ref c) = cont.take() { - if *i < c.packets.len() { - let p = &c.packets[*i]; - packet = Some(p); - cont = p.children.as_ref(); - continue; - } - } - - return None; - } - return packet; - } - - /// Returns an iterator over all of the packet's descendants, in - /// depth-first order. - pub fn descendants(&self) -> PacketIter { - self.top_level.descendants() - } - - /// Returns an iterator over the top-level packets. - pub fn children<'a>(&'a self) -> slice::Iter<'a, Packet> { - self.top_level.children() - } - - /// Returns an `IntoIter` over the top-level packets. - pub fn into_children(self) -> vec::IntoIter<Packet> { - self.top_level.into_children() - } - - - pub(crate) fn from_buffered_reader<'a>(bio: Box<'a + BufferedReader<Cookie>>) - -> Result<Message> { - PacketParserBuilder::from_buffered_reader(bio)? - .buffer_unread_content() - .to_message() - } - - /// Deserializes the OpenPGP message stored in a `std::io::Read` - /// object. - /// - /// Although this method is easier to use to parse an OpenPGP - /// message than a [`PacketParser`] or a [`MessageParser`], this - /// interface buffers the whole message in memory. Thus, the - /// caller must be certain that the *deserialized* message is not - /// too large. - /// - /// Note: this interface *does* buffer the contents of packets. - /// - /// [`PacketParser`]: parse/struct.PacketParser.html - /// [`MessageParser`]: parse/struct.MessageParser.html - pub fn from_reader<'a, R: 'a + io::Read>(reader: R) -> Result<Message> { - let bio = BufferedReaderGeneric::with_cookie( - reader, None, Cookie::default()); - Message::from_buffered_reader(Box::new(bio)) - } - - /// Deserializes the OpenPGP message stored in the file named by - /// `path`. - /// - /// See `from_reader` for more details and caveats. - pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Message> { - Message::from_reader(File::open(path)?) - } - - /// Deserializes the OpenPGP message stored in the provided buffer. - /// - /// See `from_reader` for more details and caveats. - pub fn from_bytes(data: &[u8]) -> Result<Message> { - let bio = BufferedReaderMemory::with_cookie( - data, Cookie::default()); - Message::from_buffered_reader(Box::new(bio)) - } - - /// Reads all of the packets from a `PacketParser`, and turns them - /// into a message. - /// - /// Note: this assumes that `ppo` points to a top-level packet. - pub fn from_packet_parser<'a>(ppo: Option<PacketParser<'a>>) - -> Result<Message> - { - // Things are not going to work out if we don't start with a - // top-level packet. We should only pop until - // ppo.recursion_depth and leave the rest of the message, but - // it is hard to imagine that that is what the caller wants. - // Instead of hiding that error, fail fast. - if let Some(ref pp) = ppo { - assert_eq!(pp.recursion_depth, 0); - } - - // Create a top-level container. - let mut top_level = Container::new(); - - let mut last_position = 0; - - if ppo.is_none() { - // Empty message. - return Ok(Message::from_packets(Vec::new())); - } - let mut pp = ppo.unwrap(); - - 'outer: loop { - let (mut packet, mut position, mut ppo, _) = pp.recurse()?; - - let mut relative_position : isize = position - last_position; - assert!(relative_position <= 1); - - // Find the right container for `packet`. - let mut container = &mut top_level; - // If we recurse, don't create the new container here. - for _ in 0..(position - if relative_position > 0 { 1 } else { 0 }) { - // Do a little dance to prevent container from - // being reborrowed and preventing us from - // assigning to it. - let tmp = container; - let packets_len = tmp.packets.len(); - let p = &mut tmp.packets[packets_len - 1]; - - container = p.children.as_mut().unwrap(); - } - - if relative_position < 0 { - relative_position = 0; - } - - // If next packet will be inserted in the same container - // or the current container's child, we don't need to walk - // the tree from the root. - loop { - if relative_position == 1 { - // Create a new container. - let tmp = container; - let i = tmp.packets.len() - 1; - assert!(tmp.packets[i].children.is_none()); - tmp.packets[i].children = Some(Container::new()); - container = tmp.packets[i].children.as_mut().unwrap(); - } - - container.packets.push(packet); - - if ppo.is_none() { - break 'outer; - } - - pp = ppo.unwrap(); - - last_position = position; - position = pp.recursion_depth as isize; - relative_position = position - last_position; - if position < last_position { - // There was a pop, we need to restart from the - // root. - break; - } - - let result = pp.recurse()?; - packet = result.0; - assert_eq!(position, result.1); - ppo = result.2; - } - } - - return Ok(Message { top_level: top_level }); - } -} - -impl<'a> PacketParserBuilder<'a> { - /// Finishes configuring the `PacketParser` and returns a fully - /// parsed message. - /// - /// Note: calling this function does not change the default - /// settings `PacketParserSettings`. Thus, by default, the - /// content of packets will *not* be buffered. - /// - /// Note: to avoid denial of service attacks, the `PacketParser` - /// interface should be preferred unless the size of the message - /// is known to fit in memory. - /// - /// # Examples - /// - /// ```rust - /// # use openpgp::Result; - /// # use openpgp::Message; - /// # use openpgp::parse::{PacketParser,PacketParserBuilder}; - /// # f(include_bytes!("../tests/data/messages/public-key.gpg")); - /// # - /// # fn f(message_data: &[u8]) -> Result<Message> { - /// let message = PacketParserBuilder::from_bytes(message_data)? - /// .buffer_unread_content() - /// .to_message()?; - /// # return Ok(message); - /// # } - /// ``` - pub fn to_message(self) -> Result<Message> { - Message::from_packet_parser(self.finalize()?) - } -} - -#[cfg(test)] -mod message_test { - use super::*; - - use std::io::Read; - - #[test] - fn deserialize_test_1 () { - // XXX: This test should be more thorough. Right now, we mostly - // just rely on the fact that an assertion is not thrown. - - // A flat message. - let message = Message::from_bytes(bytes!("public-key.gpg")).unwrap(); - eprintln!("Message has {} top-level packets.", - message.children().len()); - eprintln!("Message: {:?}", message); - - let mut count = 0; - for (i, p) in message.descendants().enumerate() { - eprintln!("{}: {:?}", i, p); - count += 1; - } - - assert_eq!(count, 61); - } - - #[test] - fn deserialize_test_2 () { - // A message containing a compressed packet that contains a - // literal packet. - let path = path_to("compressed-data-algo-1.gpg"); - let message = Message::from_file(&path).unwrap(); - eprintln!("Message has {} top-level packets.", - message.children().len()); - eprintln!("Message: {:?}", message); - - let mut count = 0; - for (i, p) in message.descendants().enumerate() { - eprintln!("{}: {:?}", i, p); - count += 1; - } - assert_eq!(count, 2); - } - - #[test] - fn deserialize_test_3 () { - let path = path_to("signed.gpg"); - let message = Message::from_file(&path).unwrap(); - eprintln!("Message has {} top-level packets.", - message.children().len()); - eprintln!("Message: {:?}", message); - - let mut count = 0; - for (i, p) in message.descendants().enumerate() { - count += 1; - eprintln!("{}: {:?}", i, p); - } - // We expect 6 packets. - assert_eq!(count, 6); - } - - // dkg's key contains packets from different OpenPGP - // implementations. And, it even includes some v3 signatures. - // - // lutz's key is a v3 key. - #[test] - fn torture() { - let data = bytes!("../keys/dkg.gpg"); - let mut mp = PacketParserBuilder::from_bytes(data).unwrap() - //.trace() - .buffer_unread_content() - .to_message_parser().unwrap(); - - while mp.recurse() { - //let pp = mp.ppo.as_mut().unwrap(); - //eprintln!("{:?}", pp); - } - let message = mp.finish(); - //message.pretty_print(); - assert_eq!(message.children().len(), 1450); - - let data = bytes!("../keys/lutz.gpg"); - let mut mp = PacketParserBuilder::from_bytes(data).unwrap() - //.trace() - .buffer_unread_content() - .to_message_parser().unwrap(); - - while mp.recurse() { - let pp = mp.ppo.as_mut().unwrap(); - eprintln!("{:?}", pp); - } - let message = mp.finish(); - message.pretty_print(); - assert_eq!(message.children().len(), 77); - } - - #[test] - fn compression_quine_test_1 () { - // Use the Message::from_file interface to parse an OpenPGP - // quine. - let path = path_to("compression-quine.gpg"); - let max_recursion_depth = 128; - let message = PacketParserBuilder::from_file(path).unwrap() - .max_recursion_depth(max_recursion_depth) - .to_message().unwrap(); - - let mut count = 0; - for (i, p) in message.descendants().enumerate() { - count += 1; - if false { - eprintln!("{}: p: {:?}", i, p); - } - } - - assert_eq!(count, 1 + max_recursion_depth); - } - - #[test] - fn compression_quine_test_2 () { - // Use the iterator interface to parse an OpenPGP quine. - let path = path_to("compression-quine.gpg"); - let max_recursion_depth = 255; - let mut ppo : Option<PacketParser> - = PacketParserBuilder::from_file(path).unwrap() - .max_recursion_depth(max_recursion_depth) - .finalize().unwrap(); - - let mut count : usize = 0; - loop { - if let Some(pp2) = ppo { - count += 1; - - let (_packet, packet_depth, pp2, pp_depth) - = pp2.recurse().unwrap(); - eprintln!("{}, {}", packet_depth, pp_depth); - assert_eq!(packet_depth as usize, count - 1); - if pp2.is_some() { - assert_eq!(pp_depth as usize, count); - } - ppo = pp2; |