summaryrefslogtreecommitdiffstats
path: root/openpgp/src/packet/header
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2019-09-11 12:23:26 +0200
committerJustus Winter <justus@sequoia-pgp.org>2019-09-11 12:23:26 +0200
commit5ca99787dce34100e43b8e1a2bf51454c9a6601a (patch)
tree4c333b52383b94b7dc219342ce9fdfe42021620d /openpgp/src/packet/header
parent36d27de9bbae47c76a717f2808517f3ff63fc312 (diff)
openpgp: Move the ctb module to the header module.
Diffstat (limited to 'openpgp/src/packet/header')
-rw-r--r--openpgp/src/packet/header/ctb.rs266
-rw-r--r--openpgp/src/packet/header/mod.rs3
2 files changed, 268 insertions, 1 deletions
diff --git a/openpgp/src/packet/header/ctb.rs b/openpgp/src/packet/header/ctb.rs
new file mode 100644
index 00000000..50705c6f
--- /dev/null
+++ b/openpgp/src/packet/header/ctb.rs
@@ -0,0 +1,266 @@
+//! Cipher Type Byte.
+//!
+//! See [Section 4.2 of RFC 4880] for more details.
+//!
+//! [Section 4.2 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-4.2
+
+use std::convert::TryFrom;
+
+use crate::{
+ packet::Tag,
+ Error,
+ Result
+};
+use crate::packet::BodyLength;
+
+/// OpenPGP defines two packet formats: the old and the new format.
+/// They both include the packet's so-called tag.
+///
+/// See [Section 4.2 of RFC 4880] for more details.
+///
+/// [Section 4.2 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-4.2
+#[derive(Clone, Debug)]
+struct CTBCommon {
+ /// RFC4880 Packet tag
+ tag: Tag,
+}
+
+/// The new CTB format.
+///
+/// See [Section 4.2 of RFC 4880] for more details.
+///
+/// [Section 4.2 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-4.2
+#[derive(Clone, Debug)]
+pub struct CTBNew {
+ /// Packet CTB fields
+ common: CTBCommon,
+}
+
+impl CTBNew {
+ /// Constructs a new-style CTB.
+ pub fn new(tag: Tag) -> Self {
+ CTBNew {
+ common: CTBCommon {
+ tag: tag,
+ },
+ }
+ }
+
+ /// Returns the packet's tag.
+ pub fn tag(&self) -> Tag {
+ self.common.tag
+ }
+}
+
+/// The PacketLengthType is used as part of the [old CTB], and is
+/// partially used to determine the packet's size.
+///
+/// See [Section 4.2.1 of RFC 4880] for more details.
+///
+/// [Section 4.2.1 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-4.2.1
+/// [old CTB]: ./CTBOld.t.html
+#[derive(Debug)]
+#[derive(Clone, Copy, PartialEq)]
+pub enum PacketLengthType {
+ /// A one-octet Body Length header encodes a length of 0 to 191 octets.
+ OneOctet,
+ /// A two-octet Body Length header encodes a length of 192 to 8383 octets.
+ TwoOctets,
+ /// A five-octet Body Length header consists of a single octet holding
+ /// the value 255, followed by a four-octet scalar.
+ FourOctets,
+ /// A Partial Body Length header is one octet long and encodes the length
+ /// of only part of the data packet.
+ Indeterminate,
+}
+
+impl TryFrom<u8> for PacketLengthType {
+ type Error = failure::Error;
+
+ fn try_from(u: u8) -> Result<Self> {
+ match u {
+ 0 => Ok(PacketLengthType::OneOctet),
+ 1 => Ok(PacketLengthType::TwoOctets),
+ 2 => Ok(PacketLengthType::FourOctets),
+ 3 => Ok(PacketLengthType::Indeterminate),
+ _ => Err(Error::InvalidArgument(
+ format!("Invalid packet length: {}", u)).into()),
+ }
+ }
+}
+
+impl From<PacketLengthType> for u8 {
+ fn from(l: PacketLengthType) -> Self {
+ match l {
+ PacketLengthType::OneOctet => 0,
+ PacketLengthType::TwoOctets => 1,
+ PacketLengthType::FourOctets => 2,
+ PacketLengthType::Indeterminate => 3,
+ }
+ }
+}
+
+/// The old CTB format.
+///
+/// See [Section 4.2 of RFC 4880] for more details.
+///
+/// [Section 4.2 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-4.2
+#[derive(Clone, Debug)]
+pub struct CTBOld {
+ /// Common CTB fields.
+ common: CTBCommon,
+ /// Type of length sepcifier.
+ pub length_type: PacketLengthType,
+}
+
+impl CTBOld {
+ /// Constructs a old-style CTB.
+ ///
+ /// # Errors
+ ///
+ /// Returns [`Error::InvalidArgument`] if the tag or body length
+ /// cannot be expressed using an old-style CTB.
+ ///
+ /// [`Error::InvalidArgument`]: ../../enum.Error.html#variant.InvalidArgument
+ pub fn new(tag: Tag, length: BodyLength) -> Result<Self> {
+ let n: u8 = tag.into();
+
+ // Only tags 0-15 are supported.
+ if n > 15 {
+ return Err(Error::InvalidArgument(
+ format!("Only tags 0-15 are supported, got: {:?} ({})",
+ tag, n)).into());
+ }
+
+ let length_type = match length {
+ // Assume an optimal encoding.
+ BodyLength::Full(l) => {
+ match l {
+ // One octet length.
+ 0 ... 0xFF => PacketLengthType::OneOctet,
+ // Two octet length.
+ 0x1_00 ... 0xFF_FF => PacketLengthType::TwoOctets,
+ // Four octet length,
+ _ => PacketLengthType::FourOctets,
+ }
+ },
+ BodyLength::Partial(_) =>
+ return Err(Error::InvalidArgument(
+ "Partial body lengths are not support for old format packets".
+ into()).into()),
+ BodyLength::Indeterminate =>
+ PacketLengthType::Indeterminate,
+ };
+ Ok(CTBOld {
+ common: CTBCommon {
+ tag: tag,
+ },
+ length_type: length_type,
+ })
+ }
+
+ /// Returns the packet's tag.
+ pub fn tag(&self) -> Tag {
+ self.common.tag
+ }
+}
+
+/// A sum type for the different CTB variants.
+///
+/// There are two CTB variants: the [old CTB format] and the [new CTB
+/// format].
+///
+/// [old CTB format]: ./CTBOld.t.html
+/// [new CTB format]: ./CTBNew.t.html
+///
+/// Note: CTB stands for Cipher Type Byte.
+#[derive(Clone, Debug)]
+pub enum CTB {
+ /// New (current) packet header format.
+ New(CTBNew),
+ /// Old PGP 2.6 header format.
+ Old(CTBOld),
+}
+
+impl CTB {
+ /// Constructs a new-style CTB.
+ pub fn new(tag: Tag) -> Self {
+ CTB::New(CTBNew::new(tag))
+ }
+
+ /// Parses a CTB as described in [Section 4.2 of RFC 4880]. This
+ /// function parses both new and old format CTBs.
+ ///
+ /// [Section 4.2 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-4.2
+ pub fn from_ptag(ptag: u8) -> Result<CTB> {
+ // The top bit of the ptag must be set.
+ if ptag & 0b1000_0000 == 0 {
+ // XXX: Use a proper error.
+ return Err(
+ Error::MalformedPacket(
+ format!("Malformed CTB: MSB of ptag ({:#010b}) not set{}.",
+ ptag,
+ if ptag == '-' as u8 {
+ " (ptag is a dash, perhaps this is an \
+ ASCII-armor encoded message)"
+ } else {
+ ""
+ })).into());
+ }
+
+ let new_format = ptag & 0b0100_0000 != 0;
+ let ctb = if new_format {
+ let tag = ptag & 0b0011_1111;
+ CTB::New(CTBNew {
+ common: CTBCommon {
+ tag: tag.into()
+ }})
+ } else {
+ let tag = (ptag & 0b0011_1100) >> 2;
+ let length_type = ptag & 0b0000_0011;
+
+ CTB::Old(CTBOld {
+ common: CTBCommon {
+ tag: tag.into(),
+ },
+ length_type: PacketLengthType::try_from(length_type)?,
+ })
+ };
+
+ Ok(ctb)
+ }
+
+ /// Returns the packet's tag.
+ pub fn tag(&self) -> Tag {
+ match self {
+ CTB::New(c) => c.tag(),
+ CTB::Old(c) => c.tag(),
+ }
+ }
+}
+
+#[test]
+fn ctb() {
+ // 0x99 = public key packet
+ if let CTB::Old(ctb) = CTB::from_ptag(0x99).unwrap() {
+ assert_eq!(ctb.tag(), Tag::PublicKey);
+ assert_eq!(ctb.length_type, PacketLengthType::TwoOctets);
+ } else {
+ panic!("Expected an old format packet.");
+ }
+
+ // 0xa3 = old compressed packet
+ if let CTB::Old(ctb) = CTB::from_ptag(0xa3).unwrap() {
+ assert_eq!(ctb.tag(), Tag::CompressedData);
+ assert_eq!(ctb.length_type, PacketLengthType::Indeterminate);
+ } else {
+ panic!("Expected an old format packet.");
+ }
+
+ // 0xcb: new literal
+ if let CTB::New(ctb) = CTB::from_ptag(0xcb).unwrap() {
+ assert_eq!(ctb.tag(), Tag::Literal);
+ } else {
+ panic!("Expected a new format packet.");
+ }
+}
diff --git a/openpgp/src/packet/header/mod.rs b/openpgp/src/packet/header/mod.rs
index 776d83eb..680d34e5 100644
--- a/openpgp/src/packet/header/mod.rs
+++ b/openpgp/src/packet/header/mod.rs
@@ -6,7 +6,8 @@ use crate::{
};
use crate::packet::tag::Tag;
pub use crate::packet::BodyLength;
-pub use crate::packet::ctb::{self, CTB};
+pub mod ctb;
+pub use self::ctb::CTB;
/// An OpenPGP packet's header.
#[derive(Clone, Debug)]