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}