sequoia_openpgp/packet/header/mod.rs
1//! OpenPGP packet headers.
2//!
3//! An OpenPGP packet header contains packet meta-data. Specifically,
4//! it includes the [packet's type] (its so-called *tag*), and the
5//! [packet's length].
6//!
7//! Decades ago, when OpenPGP was conceived, saving even a few bits
8//! was considered important. As such, OpenPGP uses very compact
9//! encodings. The encoding schemes have evolved so that there are
10//! now two families: the so-called old format, and new format
11//! encodings.
12//!
13//! [packet's type]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5
14//! [packet's length]: https://www.rfc-editor.org/rfc/rfc9580.html#section-4.2.2
15
16use crate::{
17 Error,
18 Result,
19};
20use crate::packet::tag::Tag;
21mod ctb;
22pub use self::ctb::{
23 CTB,
24 CTBOld,
25 CTBNew,
26 PacketLengthType,
27};
28
29/// A packet's header.
30///
31/// See [Section 4.2 of RFC 9580] for details.
32///
33/// [Section 4.2 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-4.2
34#[derive(Clone, Debug)]
35pub struct Header {
36 /// The packet's CTB.
37 ctb: CTB,
38 /// The packet's length.
39 length: BodyLength,
40}
41assert_send_and_sync!(Header);
42
43impl Header {
44 /// Creates a new header.
45 pub fn new(ctb: CTB, length: BodyLength) -> Self {
46 Header { ctb, length }
47 }
48
49 /// Returns the header's CTB.
50 pub fn ctb(&self) -> &CTB {
51 &self.ctb
52 }
53
54 /// Returns the header's length.
55 pub fn length(&self) -> &BodyLength {
56 &self.length
57 }
58
59 /// Checks the header for validity.
60 ///
61 /// A header is considered invalid if:
62 ///
63 /// - The tag is [`Tag::Reserved`].
64 /// - The tag is [`Tag::Unknown`] or [`Tag::Private`] and
65 /// `future_compatible` is false.
66 /// - The [length encoding] is invalid for the packet (e.g.,
67 /// partial body encoding may not be used for [`PKESK`] packets)
68 /// - The lengths are unreasonable for a packet (e.g., a
69 /// `PKESK` or [`SKESK`] larger than 10 KB).
70 ///
71 /// [`Tag::Reserved`]: super::Tag::Reserved
72 /// [`Tag::Unknown`]: super::Tag::Unknown
73 /// [`Tag::Private`]: super::Tag::Private
74 /// [length encoding]: https://www.rfc-editor.org/rfc/rfc9580.html#section-4.2.1.4
75 /// [`PKESK`]: super::PKESK
76 /// [`SKESK`]: super::SKESK
77 // Note: To check the packet's content, use
78 // `PacketParser::plausible`.
79 pub fn valid(&self, future_compatible: bool) -> Result<()> {
80 let tag = self.ctb.tag();
81
82 match tag {
83 // Reserved packets are never valid.
84 Tag::Reserved =>
85 return Err(Error::UnsupportedPacketType(tag).into()),
86 // Unknown packets are not valid unless we want future compatibility.
87 Tag::Unknown(_) | Tag::Private(_) if !future_compatible =>
88 return Err(Error::UnsupportedPacketType(tag).into()),
89 _ => (),
90 }
91
92 // An implementation MAY use Partial Body Lengths for data
93 // packets, be they literal, compressed, or encrypted. The
94 // first partial length MUST be at least 512 octets long.
95 // Partial Body Lengths MUST NOT be used for any other packet
96 // types.
97 //
98 // https://www.rfc-editor.org/rfc/rfc9580.html#section-4.2.1.4
99 if tag == Tag::Literal || tag == Tag::CompressedData
100 || tag == Tag::SED || tag == Tag::SEIP
101 || tag == Tag::AED
102 {
103 // Data packet.
104 match self.length {
105 BodyLength::Indeterminate => (),
106 BodyLength::Partial(l) => {
107 if l < 512 {
108 return Err(Error::MalformedPacket(
109 format!("Partial body length must be \
110 at least 512 (got: {})",
111 l)).into());
112 }
113 }
114 BodyLength::Full(l) => {
115 // In the following block cipher length checks, we
116 // conservatively assume a block size of 8 bytes,
117 // because Twofish, TripleDES, IDEA, and CAST-5
118 // have a block size of 64 bits.
119 if tag == Tag::SED && (l < (8 // Random block.
120 + 2 // Quickcheck bytes.
121 + 6)) { // Smallest literal.
122 return Err(Error::MalformedPacket(
123 format!("{} packet's length must be \
124 at least 16 bytes in length (got: {})",
125 tag, l)).into());
126 } else if tag == Tag::SEIP
127 && (l < (1 // Version.
128 + 8 // Random block.
129 + 2 // Quickcheck bytes.
130 + 6 // Smallest literal.
131 + 20)) // MDC packet.
132 {
133 return Err(Error::MalformedPacket(
134 format!("{} packet's length minus 1 must be \
135 at least 37 bytes in length (got: {})",
136 tag, l)).into());
137 } else if tag == Tag::CompressedData && l == 0 {
138 // One byte header.
139 return Err(Error::MalformedPacket(
140 format!("{} packet's length must be \
141 at least 1 byte (got ({})",
142 tag, l)).into());
143 } else if tag == Tag::Literal && l < 6 {
144 // Smallest literal packet consists of 6 octets.
145 return Err(Error::MalformedPacket(
146 format!("{} packet's length must be \
147 at least 6 bytes (got: ({})",
148 tag, l)).into());
149 }
150 }
151 }
152 } else {
153 // Non-data packet.
154 match self.length {
155 BodyLength::Indeterminate =>
156 return Err(Error::MalformedPacket(
157 format!("Indeterminite length encoding \
158 not allowed for {} packets",
159 tag)).into()),
160 BodyLength::Partial(_) =>
161 return Err(Error::MalformedPacket(
162 format!("Partial Body Chunking not allowed \
163 for {} packets",
164 tag)).into()),
165 BodyLength::Full(l) => {
166 let valid = match tag {
167 Tag::Signature =>
168 // A V3 signature is 19 bytes plus the
169 // MPIs. A V4 is 10 bytes plus the hash
170 // areas and the MPIs.
171 (10..(10 // Header, fixed sized fields.
172 + 2 * 64 * 1024 // Hashed & Unhashed areas.
173 + 64 * 1024 // MPIs.
174 )).contains(&l),
175 Tag::SKESK =>
176 // 2 bytes of fixed header. A s2k
177 // specification (at least 1 byte), an
178 // optional encryption session key.
179 (3..10 * 1024).contains(&l),
180 Tag::PKESK =>
181 // For v3, 10 bytes of fixed header, plus
182 // the encrypted session key.
183 (10 < l && l < 10 * 1024)
184 // For v6, 5 bytes of fixed header, plus
185 // the encrypted session key.
186 || (5 <= l && l < 10 * 1024),
187 Tag::OnePassSig if ! future_compatible =>
188 l == 13 // v3
189 || (6 + 32..6 + 32 + 256).contains(&l), // v6
190 Tag::OnePassSig => l < 1024,
191 Tag::PublicKey | Tag::PublicSubkey
192 | Tag::SecretKey | Tag::SecretSubkey =>
193 // A V3 key is 8 bytes of fixed header
194 // plus MPIs. A V4 key is 6 bytes of
195 // fixed headers plus MPIs.
196 6 < l && l < 1024 * 1024,
197 Tag::Trust => true,
198 Tag::UserID =>
199 // Avoid insane user ids.
200 l < 32 * 1024,
201 Tag::UserAttribute =>
202 // The header is at least 2 bytes.
203 2 <= l,
204 Tag::MDC => l == 20,
205
206 Tag::Literal | Tag::CompressedData
207 | Tag::SED | Tag::SEIP | Tag::AED =>
208 unreachable!("handled in the data-packet branch"),
209 Tag::Unknown(_) | Tag::Private(_) => true,
210
211 Tag::Marker => l == 3,
212 Tag::Reserved => true,
213 Tag::Padding => true,
214 };
215
216 if ! valid {
217 return Err(Error::MalformedPacket(
218 format!("Invalid size ({} bytes) for a {} packet",
219 l, tag)).into())
220 }
221 }
222 }
223 }
224
225 Ok(())
226 }
227}
228
229/// A packet's size.
230///
231/// A packet's size can be expressed in three different ways. Either
232/// the size of the packet is fully known (`Full`), the packet is
233/// chunked using OpenPGP's partial body encoding (`Partial`), or the
234/// packet extends to the end of the file (`Indeterminate`). See
235/// [Section 4.2 of RFC 9580] for more details.
236///
237/// [Section 4.2 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-4.2
238#[derive(Debug)]
239// We need PartialEq so that assert_eq! works.
240#[derive(PartialEq)]
241#[derive(Clone, Copy)]
242pub enum BodyLength {
243 /// The packet's size is known.
244 Full(u32),
245 /// The parameter is the number of bytes in the current chunk.
246 ///
247 /// This type is only used with new format packets.
248 Partial(u32),
249 /// The packet extends until an EOF is encountered.
250 ///
251 /// This type is only used with old format packets.
252 Indeterminate,
253}
254assert_send_and_sync!(BodyLength);
255
256#[cfg(test)]
257mod tests {
258 use super::*;
259 use crate::packet::Packet;
260 use crate::parse::{
261 Cookie, Dearmor, PacketParserBuilder, PacketParserResult, Parse,
262 };
263 use crate::serialize::SerializeInto;
264
265 quickcheck! {
266 /// Checks alignment of Header::parse-then-Header::valid, the
267 /// PacketParser, and Arbitrary::arbitrary.
268 fn parser_alignment(p: Packet) -> bool {
269 let verbose = false;
270 let buf = p.to_vec().expect("Failed to serialize packet");
271
272 // First, check Header::parse and Header::valid.
273 let mut reader = buffered_reader::Memory::with_cookie(
274 &buf, Cookie::default());
275 let header = Header::parse(&mut reader).unwrap();
276 if verbose {
277 eprintln!("header parsed: {:?}", header);
278 }
279 header.valid(true).unwrap();
280 header.valid(false).unwrap();
281
282 // Now check the packet parser. Be careful to disable the
283 // armor detection because that in turn relies on
284 // Header::valid, and we want to test the behavior of the
285 // packet parser.
286 let ppr =
287 PacketParserBuilder::from_bytes(&buf).unwrap()
288 .dearmor(Dearmor::Disabled)
289 .buffer_unread_content()
290 .build().unwrap();
291
292 let (p, ppr) = match ppr {
293 PacketParserResult::Some(pp) => {
294 pp.next().unwrap()
295 },
296 PacketParserResult::EOF(eof) =>
297 panic!("no packet found: {:?}", eof),
298 };
299 if verbose {
300 eprintln!("packet parser parsed: {:?}", p);
301 }
302
303 if let PacketParserResult::Some(pp) = ppr {
304 panic!("Excess data after packet: {:?}", pp)
305 }
306
307 true
308 }
309 }
310}