diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2020-03-26 17:21:32 +0100 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2020-03-26 17:21:32 +0100 |
commit | 89337646884b59c894329432eea960be4b3e335e (patch) | |
tree | 349136c28ee05b1ba71773c3ff8b1a0d7e2865a2 | |
parent | e26c4d5a8fb582ec2a3c4c373536913aa2d0a468 (diff) |
openpgp: Change packet bodies to be tristate.
- Packet bodies can now be either unprocessed (e.g. compressed,
encrypted), processed (e.g. uncompressed, decrypted), or
structured (e.g. parsed into packets).
- Make the container types deref to Container, and container deref
to packet bodies.
- This cleanly avoids the confusion when serializing containers: We
can serialize compressed data packets with either body, but we can
only serialize encryption containers with unprocessed bodies.
- Fixes #187.
-rw-r--r-- | openpgp/src/message/mod.rs | 13 | ||||
-rw-r--r-- | openpgp/src/packet/aed.rs | 15 | ||||
-rw-r--r-- | openpgp/src/packet/compressed_data.rs | 24 | ||||
-rw-r--r-- | openpgp/src/packet/container.rs | 402 | ||||
-rw-r--r-- | openpgp/src/packet/literal.rs | 2 | ||||
-rw-r--r-- | openpgp/src/packet/mod.rs | 14 | ||||
-rw-r--r-- | openpgp/src/packet/prelude.rs | 2 | ||||
-rw-r--r-- | openpgp/src/packet/seip.rs | 15 | ||||
-rw-r--r-- | openpgp/src/packet/unknown.rs | 2 | ||||
-rw-r--r-- | openpgp/src/packet_pile.rs | 73 | ||||
-rw-r--r-- | openpgp/src/parse.rs | 54 | ||||
-rw-r--r-- | openpgp/src/parse/packet_pile_parser.rs | 10 | ||||
-rw-r--r-- | openpgp/src/serialize/mod.rs | 139 |
13 files changed, 466 insertions, 299 deletions
diff --git a/openpgp/src/message/mod.rs b/openpgp/src/message/mod.rs index 1e3f3e69..dce7f11a 100644 --- a/openpgp/src/message/mod.rs +++ b/openpgp/src/message/mod.rs @@ -386,9 +386,7 @@ impl Message { // we treat the content as an opaque message. path.push(0); - if packet.children().next().is_none() - && packet.body().is_some() - { + if packet.children().is_none() { v.push_token(Token::OpaqueContent, &path); } } @@ -1003,9 +1001,9 @@ mod tests { // 1: MDC // => good. let mut seip = SEIP1::new(); - seip.children_mut().push( + seip.children_mut().unwrap().push( lit.clone().into()); - seip.children_mut().push( + seip.children_mut().unwrap().push( MDC::from([0u8; 20]).into()); packets[1] = seip.into(); @@ -1086,7 +1084,7 @@ mod tests { // => bad. packets.remove(3); packets[2].container_mut().unwrap() - .children_mut().push(lit.clone().into()); + .children_mut().unwrap().push(lit.clone().into()); assert!(packets.iter().map(|p| p.tag()).collect::<Vec<Tag>>() == [ Tag::SKESK, Tag::SKESK, Tag::SEIP ]); @@ -1100,7 +1098,8 @@ mod tests { // 2: SEIP // 0: Literal // => good. - packets[2].container_mut().unwrap().children_mut().pop().unwrap(); + packets[2].container_mut().unwrap() + .children_mut().unwrap().pop().unwrap(); #[allow(deprecated)] packets.insert( diff --git a/openpgp/src/packet/aed.rs b/openpgp/src/packet/aed.rs index 60388036..1fd68274 100644 --- a/openpgp/src/packet/aed.rs +++ b/openpgp/src/packet/aed.rs @@ -34,6 +34,19 @@ pub struct AED1 { container: packet::Container, } +impl std::ops::Deref for AED1 { + type Target = packet::Container; + fn deref(&self) -> &Self::Target { + &self.container + } +} + +impl std::ops::DerefMut for AED1 { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.container + } +} + impl PartialEq for AED1 { fn eq(&self, other: &AED1) -> bool { self.sym_algo == other.sym_algo @@ -144,8 +157,6 @@ impl AED1 { } } -impl_container_forwards!(AED1); - impl From<AED1> for Packet { fn from(p: AED1) -> Self { super::AED::from(p).into() diff --git a/openpgp/src/packet/compressed_data.rs b/openpgp/src/packet/compressed_data.rs index bf89956f..9cecf5a6 100644 --- a/openpgp/src/packet/compressed_data.rs +++ b/openpgp/src/packet/compressed_data.rs @@ -25,6 +25,19 @@ pub struct CompressedData { container: packet::Container, } +impl std::ops::Deref for CompressedData { + type Target = packet::Container; + fn deref(&self) -> &Self::Target { + &self.container + } +} + +impl std::ops::DerefMut for CompressedData { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.container + } +} + impl PartialEq for CompressedData { fn eq(&self, other: &CompressedData) -> bool { self.algo == other.algo @@ -45,10 +58,7 @@ impl fmt::Debug for CompressedData { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("CompressedData") .field("algo", &self.algo) - .field("children", &self.container.children_ref()) - .field("body (bytes)", - &self.container.body().len()) - .field("body_digest", &self.container.body_digest()) + .field("container", &self.container) .finish() } } @@ -76,7 +86,7 @@ impl CompressedData { /// Adds a new packet to the container. #[cfg(test)] pub fn push(mut self, packet: Packet) -> Self { - self.container.children_mut().push(packet); + self.container.children_mut().unwrap().push(packet); self } @@ -86,13 +96,11 @@ impl CompressedData { /// packet, etc. #[cfg(test)] pub fn insert(mut self, i: usize, packet: Packet) -> Self { - self.container.children_mut().insert(i, packet); + self.container.children_mut().unwrap().insert(i, packet); self } } -impl_container_forwards!(CompressedData); - impl From<CompressedData> for Packet { fn from(s: CompressedData) -> Self { Packet::CompressedData(s) diff --git a/openpgp/src/packet/container.rs b/openpgp/src/packet/container.rs index 185659bb..2cc6c655 100644 --- a/openpgp/src/packet/container.rs +++ b/openpgp/src/packet/container.rs @@ -15,17 +15,75 @@ use crate::{ types::HashAlgorithm, }; -/// Holds zero or more OpenPGP packets. +/// A packet's body holds either unprocessed bytes, processed bytes, +/// or packets. /// -/// This is used by OpenPGP container packets, like the compressed -/// data packet, to store the containing packets. +/// We conceptually divide packets into two parts: the header and the +/// body. Whereas the header is read eagerly when the packet is +/// deserialized, the body is only read on demand. +/// +/// A packet's body is stored here either when configured via +/// [`PacketParserBuilder::buffer_unread_content`], when one of the +/// [`PacketPile`] deserialization routines is used, or on demand for +/// a particular packet using the +/// [`PacketParser::buffer_unread_content`] method. +/// +/// [`PacketParserBuilder::buffer_unread_content`]: ../parse/struct.PacketParserBuilder.html#method.buffer_unread_content +/// [`PacketPile`]: ../struct.PacketPile.html +/// [`PacketParser::buffer_unread_content`]: ../parse/struct.PacketParser.html#method.buffer_unread_content +/// +/// There are three different types of packets: +/// +/// - Packets like the [`UserID`] and [`Signature`] packets, don't +/// actually have a body. +/// +/// [`UserID`]: ../packet/struct.UserID.html +/// [`Signature`]: ../packet/signature/struct.Signature.html +/// +/// - One packet, the literal data packet, includes unstructured +/// data. That data is stored in [`Literal`]. +/// +/// [`Literal`]: ../packet/struct.Literal.html +/// +/// - Some packets are containers. If the parser does not parse the +/// packet's child, either because the caller used +/// [`PacketParser::next`] to get the next packet, or the maximum +/// recursion depth was reached, then the packets can be stored here +/// as a byte stream. (If the caller so chooses, the content can be +/// parsed later using the regular deserialization routines, since +/// the content is just an OpenPGP message.) +/// +/// [`PacketParser::next`]: ../parse/struct.PacketParser.html#method.next #[derive(Clone)] -pub(crate) struct Container { +pub enum Body { + /// Unprocessed packet body. + /// + /// The body has not been processed, i.e. it is still encrypted. + /// + /// Note: if some of a packet's data is streamed, and the + /// `PacketParser` is configured to buffer unread content, then + /// this is not the packet's entire content; it is just the unread + /// content. + Unprocessed(Vec<u8>), + + /// Processed packed body. + /// + /// The body has been processed, i.e. decompressed or decrypted, + /// but not parsed into packets. + /// + /// Note: if some of a packet's data is streamed, and the + /// `PacketParser` is configured to buffer unread content, then + /// this is not the packet's entire content; it is just the unread + /// content. + Processed(Vec<u8>), + + /// Parsed packet body. + /// /// Used by container packets (such as the encryption and /// compression packets) to reference their immediate children. /// This results in a tree structure. /// - /// This is automatically populated when using the `PacketPile` + /// This is automatically populated when using the [`PacketPile`] /// deserialization routines, e.g., [`PacketPile::from_file`]. By /// default, it is *not* automatically filled in by the /// [`PacketParser`] deserialization routines; this needs to be @@ -34,59 +92,29 @@ pub(crate) struct Container { /// [`PacketPile`]: ../struct.PacketPile.html /// [`PacketPile::from_file`]: ../struct.PacketPile.html#method.from_file /// [`PacketParser`]: ../parse/struct.PacketParser.html - pub(crate) packets: Vec<Packet>, + Structured(Vec<Packet>), +} +/// Holds packet bodies. +/// +/// This is used by OpenPGP container packets, like the compressed +/// data packet, to store the containing packets. +#[derive(Clone)] +pub struct Container { /// Holds a packet's body. - /// - /// We conceptually divide packets into two parts: the header and - /// the body. Whereas the header is read eagerly when the packet - /// is deserialized, the body is only read on demand. - /// - /// A packet's body is stored here either when configured via - /// [`PacketParserBuilder::buffer_unread_content`], when one of - /// the [`PacketPile`] deserialization routines is used, or on demand - /// for a particular packet using the - /// [`PacketParser::buffer_unread_content`] method. - /// - /// [`PacketParserBuilder::buffer_unread_content`]: ../parse/struct.PacketParserBuilder.html#method.buffer_unread_content - /// [`PacketPile`]: ../struct.PacketPile.html - /// [`PacketParser::buffer_unread_content`]: ../parse/struct.PacketParser.html#method.buffer_unread_content - /// - /// There are three different types of packets: - /// - /// - Packets like the [`UserID`] and [`Signature`] packets, - /// don't actually have a body. These packets don't use this - /// field. - /// - /// [`UserID`]: ../packet/struct.UserID.html - /// [`Signature`]: ../packet/signature/struct.Signature.html - /// - /// - One packet, the literal data packet, includes unstructured - /// data. That data is stored in [`Literal`]. - /// - /// [`Literal`]: ../packet/struct.Literal.html - /// - /// - Some packets are containers. If the parser does not parse - /// the packet's child, either because the caller used - /// [`PacketParser::next`] to get the next packet, or the - /// maximum recursion depth was reached, then the packets can - /// be stored here as a byte stream. (If the caller so - /// chooses, the content can be parsed later using the regular - /// deserialization routines, since the content is just an - /// OpenPGP message.) - /// - /// [`PacketParser::next`]: ../parse/struct.PacketParser.html#method.next - /// - /// Note: if some of a packet's data is processed, and the - /// `PacketParser` is configured to buffer unread content, then - /// this is not the packet's entire content; it is just the unread - /// content. - body: Vec<u8>, + body: Body, /// We compute a digest over the body to implement comparison. body_digest: Vec<u8>, } +impl std::ops::Deref for Container { + type Target = Body; + fn deref(&self) -> &Self::Target { + &self.body + } +} + // Pick the fastest hash function from the SHA2 family for the // architectures word size. On 64-bit architectures, SHA512 is almost // twice as fast, but on 32-bit ones, SHA256 is faster. @@ -97,8 +125,16 @@ const CONTAINER_BODY_HASH: HashAlgorithm = HashAlgorithm::SHA256; impl PartialEq for Container { fn eq(&self, other: &Container) -> bool { - self.packets == other.packets - && self.body_digest == other.body_digest + use Body::*; + match (&self.body, &other.body) { + (Unprocessed(_), Unprocessed(_)) => + self.body_digest == other.body_digest, + (Processed(_), Processed(_)) => + self.body_digest == other.body_digest, + (Structured(a), Structured(b)) => + a == b, + _ => false, + } } } @@ -106,17 +142,19 @@ impl Eq for Container {} impl Hash for Container { fn hash<H: Hasher>(&self, state: &mut H) { - self.packets.hash(state); - self.body_digest.hash(state); + if let Body::Structured(packets) = &self.body { + packets.hash(state); + } else { + self.body_digest.hash(state); + } } } impl Default for Container { fn default() -> Self { Self { - packets: Vec::with_capacity(0), - body: Vec::with_capacity(0), - body_digest: Self::empty_body_digest(), + body: Body::Structured(Vec::with_capacity(0)), + body_digest: Vec::with_capacity(0), } } } @@ -124,83 +162,122 @@ impl Default for Container { impl From<Vec<Packet>> for Container { fn from(packets: Vec<Packet>) -> Self { Self { - packets, - body: Vec::with_capacity(0), - body_digest: Self::empty_body_digest(), + body: Body::Structured(packets), + body_digest: Vec::with_capacity(0), } } } impl fmt::Debug for Container { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let threshold = 16; - let prefix = &self.body[..std::cmp::min(threshold, self.body.len())]; - let mut prefix_fmt = crate::fmt::hex::encode(prefix); - if self.body.len() > threshold { - prefix_fmt.push_str("..."); + fn fmt_bytes(f: &mut fmt::Formatter, tag: &str, bytes: &[u8], + digest: String) + -> fmt::Result + { + let threshold = 16; + let prefix = &bytes[..std::cmp::min(threshold, bytes.len())]; + let mut prefix_fmt = crate::fmt::hex::encode(prefix); + if bytes.len() > threshold { + prefix_fmt.push_str("..."); + } + prefix_fmt.push_str(&format!(" ({} bytes)", bytes.len())[..]); + + f.debug_struct("Container") + .field(tag, &prefix_fmt) + .field("digest", &digest) + .finish() } - prefix_fmt.push_str(&format!(" ({} bytes)", self.body.len())[..]); - f.debug_struct("Container") - .field("packets", &self.packets) - .field("body", &prefix_fmt) - .field("body_digest", &self.body_digest()) - .finish() + use Body::*; + match &self.body { + Unprocessed(bytes) => + fmt_bytes(f, "unprocessed", bytes, self.body_digest()), + Processed(bytes) => + fmt_bytes(f, "processed", bytes, self.body_digest()), + Structured(packets) => + f.debug_struct("Container").field("packets", packets).finish(), + } } } impl Container { + pub(crate) fn default_unprocessed() -> Self { + Self { + body: Body::Unprocessed(Vec::with_capacity(0)), + body_digest: Self::empty_body_digest(), + } + } + /// Returns a reference to this Packet's children. - pub fn children_ref(&self) -> &[Packet] { - &self.packets + /// + /// Returns `None` if the body is not structured. + pub fn children_ref(&self) -> Option<&[Packet]> { + if let Body::Structured(packets) = &self.body { + Some(&packets[..]) + } else { + None + } } /// Returns a mutable reference to this Packet's children. - pub fn children_mut(&mut self) -> &mut Vec<Packet> { - &mut self.packets + /// + /// Returns `None` if the body is not structured. + pub fn children_mut(&mut self) -> Option<&mut Vec<Packet>> { + if let Body::Structured(packets) = &mut self.body { + Some(packets) + } else { + None + } } /// Returns an iterator over the packet's descendants. The /// descendants are visited in depth-first order. - pub fn descendants(&self) -> Iter { - return Iter { + /// + /// Returns `None` if the body is not structured. + pub fn descendants(&self) -> Option<Iter> { + Some(Iter { // Iterate over each packet in the message. - children: self.children(), + children: self.children()?, child: None, grandchildren: None, depth: 0, - }; + }) } /// Returns an iterator over the packet's immediate children. - pub fn children<'a>(&'a self) -> slice::Iter<'a, Packet> { - self.packets.iter() + /// + /// Returns `None` if the body is not structured. + pub fn children<'a>(&'a self) -> Option<slice::Iter<'a, Packet>> { + Some(self.children_ref()?.iter()) } /// Returns an `IntoIter` over the packet's immediate children. - pub fn into_children(self) -> vec::IntoIter<Packet> { - self.packets.into_iter() + /// + /// Returns `None` if the body is not structured. + pub fn into_children(self) -> Option<vec::IntoIter<Packet>> { + if let Body::Structured(packets) = self.body { + Some(packets.into_iter()) + } else { + None + } } - /// Retrieves the packet's body. - /// - /// Packets can store a sequence of bytes as body, e.g. if the - /// maximum recursion level is reached while parsing a sequence of - /// packets, the container's body is stored as is. - pub fn body(&self) -> &[u8] { + /// Gets the packet's body. + pub fn body(&self) -> &Body { &self.body } /// Sets the packet's body. - /// - /// Setting the body clears the old body, or any of the packet's - /// descendants. - pub fn set_body(&mut self, data: Vec<u8>) -> Vec<u8> { - self.packets.clear(); + pub fn set_body(&mut self, body: Body) -> Body { + use Body::*; let mut h = Self::make_body_hash(); - h.update(&data); + match &body { + Unprocessed(bytes) => h.update(bytes), + Processed(bytes) => h.update(bytes), + Structured(_) => (), + } self.set_body_hash(h); - std::mem::replace(&mut self.body, data) + std::mem::replace(&mut self.body, body) } /// Returns the hash for the empty body. @@ -250,40 +327,18 @@ impl Container { // // `indent` is the number of spaces to indent the output. pub(crate) fn pretty_print(&self, indent: usize) { - for (i, p) in self.packets.iter().enumerate() { + for (i, p) in self.children_ref().iter().enumerate() { eprintln!("{}{}: {:?}", Self::indent(indent), i + 1, p); - if let Some(ref children) = self.packets[i].container_ref() { + if let Some(ref children) = self.children_ref() + .and_then(|c| c.get(i)).and_then(|p| p.container_ref()) + { children.pretty_print(indent + 1); } } } } -macro_rules! the_common_container_forwards { - () => { - /// Returns a reference to the container. - pub(crate) fn container_ref(&self) -> &packet::Container { - &self.container - } - - /// Returns a mutable reference to the container. - pub(crate) fn container_mut(&mut self) -> &mut packet::Container { - &mut self.container - } - - /// Gets a reference to the this packet's body. - pub fn body(&self) -> &[u8] { - self.container.body() - } - - /// Sets the this packet's body. - pub fn set_body(&mut self, data: Vec<u8>) -> Vec<u8> { - self.container.set_body(data) - } - }; -} - macro_rules! impl_body_forwards { ($typ:ident) => { /// This packet implements the unprocessed container @@ -292,39 +347,38 @@ macro_rules! impl_body_forwards { /// Container packets like this one can contain unprocessed /// data. impl $typ { - the_common_container_forwards!(); - } - }; -} - -macro_rules! impl_container_forwards { - ($typ:ident) => { - /// This packet implements the container interface. - /// - /// Container packets can contain other packets, unprocessed - /// data, or both. - impl $typ { - the_common_container_forwards!(); - - /// Returns a reference to this Packet's children. - pub fn children_ref(&self) -> &[Packet] { - self.container.children_ref() + /// Returns a reference to the container. + pub(crate) fn container_ref(&self) -> &packet::Container { + &self.container } - /// Returns a mutable reference to this Packet's children. - pub fn children_mut(&mut self) -> &mut Vec<Packet> { - self.container.children_mut() + /// Returns a mutable reference to the container. + pub(crate) fn container_mut(&mut self) -> &mut packet::Container { + &mut self.container } - /// Returns an iterator over the packet's immediate children. - pub fn children<'a>(&'a self) -> impl Iterator<Item = &'a Packet> { - self.container.children() + /// Gets a reference to the this packet's body. + pub fn body(&self) -> &[u8] { + use crate::packet::Body::*; + match self.container.body() { + Unprocessed(bytes) => bytes, + Processed(_) => unreachable!( + "Unprocessed container has processed body"), + Structured(_) => unreachable!( + "Unprocessed container has structured body"), + } } - /// Returns an iterator over all of the packet's descendants, in - /// depth-first order. - pub fn descendants(&self) -> super::Iter { - self.container.descendants() + /// Sets the this packet's body. + pub fn set_body(&mut self, data: Vec<u8>) -> Vec<u8> { + use crate::packet::{Body, Body::*}; + match self.container.set_body(Body::Unprocessed(data)) { + Unprocessed(bytes) => bytes, + Processed(_) => unreachable!( + "Unprocessed container has processed body"), + Structured(_) => unreachable!( + "Unprocessed container has structured body"), + } } } }; @@ -333,22 +387,24 @@ macro_rules! impl_container_forwards { impl Packet { pub(crate) // for packet_pile.rs fn container_ref(&self) -> Option<&Container> { + use std::ops::Deref; match self { - Packet::CompressedData(p) => Some(p.container_ref()), - Packet::SEIP(p) => Some(p.container_ref()), - Packet::AED(p) => Some(p.container_ref()), + Packet::CompressedData(p) => Some(p.deref()), + Packet::SEIP(p) => Some(p.deref()), + Packet::AED(p) => Some(p.deref()), Packet::Literal(p) => Some(p.container_ref()), Packet::Unknown(p) => Some(p.container_ref()), _ => None, } } - pub(crate) // for packet_pile.rs + pub(crate) // for packet_pile.rs, packet_pile_parser.rs, parse.rs fn container_mut(&mut self) -> Option<&mut Container> { + use std::ops::DerefMut; match self { - Packet::CompressedData(p) => Some(p.container_mut()), - Packet::SEIP(p) => Some(p.container_mut()), - Packet::AED(p) => Some(p.container_mut()), + Packet::CompressedData(p) => Some(p.deref_mut()), + Packet::SEIP(p) => Some(p.deref_mut()), + Packet::AED(p) => Some(p.deref_mut()), Packet::Literal(p) => Some(p.container_mut()), Packet::Unknown(p) => Some(p.container_mut()), _ => None, @@ -356,22 +412,32 @@ impl Packet { } /// Returns an iterator over the packet's immediate children. - pub(crate) fn children<'a>(&'a self) -> impl Iterator<Item = &'a Packet> { - self.container_ref().map(|c| c.children()).unwrap_or_else(|| [].iter()) + pub(crate) fn children<'a>(&'a self) + -> Option<impl Iterator<Item = &'a Packet>> { + self.container_ref().and_then(|c| c.children()) } /// Returns an iterator over all of the packet's descendants, in /// depth-first order. - pub(crate) fn descendants(&self) -> Iter { - self.container_ref().map(|c| c.descendants()).unwrap_or_default() + pub(crate) fn descendants(&self) -> Option<Iter> { |