1 //! Cipher Type Byte (CTB).
2 //!
3 //! The CTB encodes the packet's type and some length information. It
4 //! has two variants: the so-called old format and the so-called new
5 //! format. See [Section 4.2 of RFC 4880] for more details.
6 //!
7 //! [Section 4.2 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-4.2
8
9 use std::convert::TryFrom;
10
11 use crate::{
12 packet::Tag,
13 Error,
14 Result
15 };
16 use crate::packet::header::BodyLength;
17
18 /// Data common to all CTB formats.
19 ///
20 /// OpenPGP defines two packet formats: an old format and a new
21 /// format. They both include the packet's so-called tag.
22 ///
23 /// See [Section 4.2 of RFC 4880] for more details.
24 ///
25 /// [Section 4.2 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-4.2
26 #[derive(Clone, Debug)]
27 struct CTBCommon {
28 /// RFC4880 Packet tag
29 tag: Tag,
30 }
31
32 /// A CTB using the new format encoding.
33 ///
34 /// See [Section 4.2 of RFC 4880] for more details.
35 ///
36 /// [Section 4.2 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-4.2
37 #[derive(Clone, Debug)]
38 pub struct CTBNew {
39 /// Packet CTB fields
40 common: CTBCommon,
41 }
42
43 impl CTBNew {
44 /// Constructs a new-style CTB.
new(tag: Tag) -> Self45 pub fn new(tag: Tag) -> Self {
46 CTBNew {
47 common: CTBCommon {
48 tag,
49 },
50 }
51 }
52
53 /// Returns the packet's tag.
tag(&self) -> Tag54 pub fn tag(&self) -> Tag {
55 self.common.tag
56 }
57 }
58
59 /// The length encoded for an old style CTB.
60 ///
61 /// The `PacketLengthType` is only part of the [old CTB], and is
62 /// partially used to determine the packet's size.
63 ///
64 /// See [Section 4.2.1 of RFC 4880] for more details.
65 ///
66 /// [old CTB]: struct.CTBOld.html
67 /// [Section 4.2.1 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-4.2.1
68 #[derive(Debug)]
69 #[derive(Clone, Copy, PartialEq)]
70 pub enum PacketLengthType {
71 /// A one-octet Body Length header encodes a length of 0 to 191 octets.
72 ///
73 /// The header is 2 octets long. It contains the one byte CTB
74 /// followed by the one octet length.
75 OneOctet,
76 /// A two-octet Body Length header encodes a length of 192 to 8383 octets.
77 ///
78 /// The header is 3 octets long. It contains the one byte CTB
79 /// followed by the two octet length.
80 TwoOctets,
81 /// A four-octet Body Length.
82 ///
83 /// The header is 5 octets long. It contains the one byte CTB
84 /// followed by the four octet length.
85 FourOctets,
86 /// The packet is of indeterminate length.
87 ///
88 /// Neither the packet header nor the packet itself contain any
89 /// information about the length. The end of the packet is clear
90 /// from the context, e.g., EOF.
91 Indeterminate,
92 }
93
94 impl TryFrom<u8> for PacketLengthType {
95 type Error = anyhow::Error;
96
try_from(u: u8) -> Result<Self>97 fn try_from(u: u8) -> Result<Self> {
98 match u {
99 0 => Ok(PacketLengthType::OneOctet),
100 1 => Ok(PacketLengthType::TwoOctets),
101 2 => Ok(PacketLengthType::FourOctets),
102 3 => Ok(PacketLengthType::Indeterminate),
103 _ => Err(Error::InvalidArgument(
104 format!("Invalid packet length: {}", u)).into()),
105 }
106 }
107 }
108
109 impl From<PacketLengthType> for u8 {
from(l: PacketLengthType) -> Self110 fn from(l: PacketLengthType) -> Self {
111 match l {
112 PacketLengthType::OneOctet => 0,
113 PacketLengthType::TwoOctets => 1,
114 PacketLengthType::FourOctets => 2,
115 PacketLengthType::Indeterminate => 3,
116 }
117 }
118 }
119
120 /// A CTB using the old format encoding.
121 ///
122 /// See [Section 4.2 of RFC 4880] for more details.
123 ///
124 /// [Section 4.2 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-4.2
125 #[derive(Clone, Debug)]
126 pub struct CTBOld {
127 /// Common CTB fields.
128 common: CTBCommon,
129 /// Type of length specifier.
130 length_type: PacketLengthType,
131 }
132
133 impl CTBOld {
134 /// Constructs an old-style CTB.
135 ///
136 /// # Errors
137 ///
138 /// Returns [`Error::InvalidArgument`] if the tag or the body
139 /// length cannot be expressed using an old-style CTB.
140 ///
141 /// [`Error::InvalidArgument`]: ../../enum.Error.html#variant.InvalidArgument
new(tag: Tag, length: BodyLength) -> Result<Self>142 pub fn new(tag: Tag, length: BodyLength) -> Result<Self> {
143 let n: u8 = tag.into();
144
145 // Only tags 0-15 are supported.
146 if n > 15 {
147 return Err(Error::InvalidArgument(
148 format!("Only tags 0-15 are supported, got: {:?} ({})",
149 tag, n)).into());
150 }
151
152 let length_type = match length {
153 // Assume an optimal encoding.
154 BodyLength::Full(l) => {
155 match l {
156 // One octet length.
157 0 ..= 0xFF => PacketLengthType::OneOctet,
158 // Two octet length.
159 0x1_00 ..= 0xFF_FF => PacketLengthType::TwoOctets,
160 // Four octet length,
161 _ => PacketLengthType::FourOctets,
162 }
163 },
164 BodyLength::Partial(_) =>
165 return Err(Error::InvalidArgument(
166 "Partial body lengths are not support for old format packets".
167 into()).into()),
168 BodyLength::Indeterminate =>
169 PacketLengthType::Indeterminate,
170 };
171
172 Ok(CTBOld {
173 common: CTBCommon {
174 tag,
175 },
176 length_type,
177 })
178 }
179
180 /// Returns the packet's tag.
tag(&self) -> Tag181 pub fn tag(&self) -> Tag {
182 self.common.tag
183 }
184
185 /// Returns the packet's length type.
length_type(&self) -> PacketLengthType186 pub fn length_type(&self) -> PacketLengthType {
187 self.length_type
188 }
189 }
190
191 /// The CTB variants.
192 ///
193 /// There are two CTB variants: the [old CTB format] and the [new CTB
194 /// format].
195 ///
196 /// [old CTB format]: struct.CTBOld.html
197 /// [new CTB format]: struct.CTBNew.html
198 ///
199 /// Note: CTB stands for Cipher Type Byte.
200 #[derive(Clone, Debug)]
201 pub enum CTB {
202 /// New (current) packet header format.
203 New(CTBNew),
204 /// Old PGP 2.6 header format.
205 Old(CTBOld),
206 }
207
208 impl CTB {
209 /// Constructs a new-style CTB.
new(tag: Tag) -> Self210 pub fn new(tag: Tag) -> Self {
211 CTB::New(CTBNew::new(tag))
212 }
213
214 /// Returns the packet's tag.
tag(&self) -> Tag215 pub fn tag(&self) -> Tag {
216 match self {
217 CTB::New(c) => c.tag(),
218 CTB::Old(c) => c.tag(),
219 }
220 }
221 }
222
223 impl TryFrom<u8> for CTB {
224 type Error = anyhow::Error;
225
226 /// Parses a CTB as described in [Section 4.2 of RFC 4880]. This
227 /// function parses both new and old format CTBs.
228 ///
229 /// [Section 4.2 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-4.2
try_from(ptag: u8) -> Result<CTB>230 fn try_from(ptag: u8) -> Result<CTB> {
231 // The top bit of the ptag must be set.
232 if ptag & 0b1000_0000 == 0 {
233 return Err(
234 Error::MalformedPacket(
235 format!("Malformed CTB: MSB of ptag ({:#010b}) not set{}.",
236 ptag,
237 if ptag == '-' as u8 {
238 " (ptag is a dash, perhaps this is an \
239 ASCII-armor encoded message)"
240 } else {
241 ""
242 })).into());
243 }
244
245 let new_format = ptag & 0b0100_0000 != 0;
246 let ctb = if new_format {
247 let tag = ptag & 0b0011_1111;
248 CTB::New(CTBNew {
249 common: CTBCommon {
250 tag: tag.into()
251 }})
252 } else {
253 let tag = (ptag & 0b0011_1100) >> 2;
254 let length_type = ptag & 0b0000_0011;
255
256 CTB::Old(CTBOld {
257 common: CTBCommon {
258 tag: tag.into(),
259 },
260 length_type: PacketLengthType::try_from(length_type)?,
261 })
262 };
263
264 Ok(ctb)
265 }
266 }
267
268 #[test]
ctb()269 fn ctb() {
270 // 0x99 = public key packet
271 if let CTB::Old(ctb) = CTB::try_from(0x99).unwrap() {
272 assert_eq!(ctb.tag(), Tag::PublicKey);
273 assert_eq!(ctb.length_type, PacketLengthType::TwoOctets);
274 } else {
275 panic!("Expected an old format packet.");
276 }
277
278 // 0xa3 = old compressed packet
279 if let CTB::Old(ctb) = CTB::try_from(0xa3).unwrap() {
280 assert_eq!(ctb.tag(), Tag::CompressedData);
281 assert_eq!(ctb.length_type, PacketLengthType::Indeterminate);
282 } else {
283 panic!("Expected an old format packet.");
284 }
285
286 // 0xcb: new literal
287 if let CTB::New(ctb) = CTB::try_from(0xcb).unwrap() {
288 assert_eq!(ctb.tag(), Tag::Literal);
289 } else {
290 panic!("Expected a new format packet.");
291 }
292 }
293