sequoia_openpgp/parse/
packet_parser_builder.rs

1use buffered_reader::BufferedReader;
2
3use crate::Result;
4use crate::parse::PacketParserResult;
5use crate::parse::PacketParser;
6use crate::parse::PacketParserEOF;
7use crate::parse::PacketParserState;
8use crate::parse::PacketParserSettings;
9use crate::parse::ParserResult;
10use crate::parse::Parse;
11use crate::parse::Cookie;
12use crate::armor;
13use crate::packet;
14
15/// Controls transparent stripping of ASCII armor when parsing.
16///
17/// When parsing OpenPGP data streams, the [`PacketParser`] will by
18/// default automatically detect and remove any ASCII armor encoding
19/// (see [Section 6 of RFC 9580]).  This automatism can be disabled
20/// and fine-tuned using [`PacketParserBuilder::dearmor`].
21///
22///   [Section 6 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-6
23///   [`PacketParserBuilder::dearmor`]: PacketParserBuilder::dearmor()
24#[derive(PartialEq)]
25#[non_exhaustive]
26pub enum Dearmor {
27    /// Unconditionally treat the input as if it were an OpenPGP
28    /// message encoded using ASCII armor.
29    ///
30    /// Parsing a binary encoded OpenPGP message using this mode will
31    /// fail.  The [`ReaderMode`] allow further customization of the
32    /// ASCII armor parser.
33    ///
34    ///   [`ReaderMode`]: crate::armor::ReaderMode
35    Enabled(armor::ReaderMode),
36    /// Unconditionally treat the input as if it were a binary OpenPGP
37    /// message.
38    ///
39    /// Parsing an ASCII armor encoded OpenPGP message using this mode will
40    /// fail.
41    Disabled,
42    /// If input does not appear to be a binary encoded OpenPGP
43    /// message, treat it as if it were encoded using ASCII armor.
44    ///
45    /// This is the default.  The [`ReaderMode`] allow further
46    /// customization of the ASCII armor parser.
47    ///
48    ///   [`ReaderMode`]: crate::armor::ReaderMode
49    Auto(armor::ReaderMode),
50}
51assert_send_and_sync!(Dearmor);
52
53impl Default for Dearmor {
54    fn default() -> Self {
55        Dearmor::Auto(Default::default())
56    }
57}
58
59/// This is the level at which we insert the dearmoring filter into
60/// the buffered reader stack.
61pub(super) const ARMOR_READER_LEVEL: isize = -2;
62
63/// A builder for configuring a `PacketParser`.
64///
65/// Since the default settings are usually appropriate, this mechanism
66/// will only be needed in exceptional circumstances.  Instead use,
67/// for instance, `PacketParser::from_file` or
68/// `PacketParser::from_reader` to start parsing an OpenPGP message.
69///
70/// # Examples
71///
72/// ```rust
73/// # fn main() -> sequoia_openpgp::Result<()> {
74/// use sequoia_openpgp as openpgp;
75/// use openpgp::parse::{Parse, PacketParserResult, PacketParserBuilder};
76///
77/// // Parse a message.
78/// let message_data: &[u8] = // ...
79/// #    include_bytes!("../../tests/data/messages/compressed-data-algo-0.pgp");
80/// let mut ppr = PacketParserBuilder::from_bytes(message_data)?
81///     // Customize the `PacketParserBuilder` here.
82///     .build()?;
83/// while let PacketParserResult::Some(mut pp) = ppr {
84///     // ...
85///
86///     // Start parsing the next packet, recursing.
87///     ppr = pp.recurse()?.1;
88/// }
89/// # Ok(()) }
90/// ```
91pub struct PacketParserBuilder<'a> {
92    bio: Box<dyn BufferedReader<Cookie> + 'a>,
93    dearmor: Dearmor,
94    settings: PacketParserSettings,
95    csf_transformation: bool,
96}
97assert_send_and_sync!(PacketParserBuilder<'_>);
98
99impl<'a> Parse<'a, PacketParserBuilder<'a>> for PacketParserBuilder<'a> {
100    /// Starts parsing an OpenPGP object stored in a `BufferedReader` object.
101    ///
102    /// This function returns a `PacketParser` for the first packet in
103    /// the stream.
104    fn from_buffered_reader<R>(reader: R) -> Result<PacketParserBuilder<'a>>
105    where
106        R: BufferedReader<Cookie> + 'a,
107    {
108        PacketParserBuilder::from_cookie_reader(reader.into_boxed())
109    }
110}
111
112impl<'a> crate::seal::Sealed for PacketParserBuilder<'a> {}
113
114impl<'a> PacketParserBuilder<'a> {
115    // Creates a `PacketParserBuilder` for an OpenPGP message stored
116    // in a `BufferedReader` object.
117    //
118    // Note: this clears the `level` field of the
119    // `Cookie` cookie.
120    pub(crate) fn from_cookie_reader(mut bio: Box<dyn BufferedReader<Cookie> + 'a>)
121            -> Result<Self> {
122        bio.cookie_mut().level = None;
123        Ok(PacketParserBuilder {
124            bio,
125            dearmor: Default::default(),
126            settings: PacketParserSettings::default(),
127            csf_transformation: false,
128        })
129    }
130
131    /// Sets the maximum recursion depth.
132    ///
133    /// Setting this to 0 means that the `PacketParser` will never
134    /// recurse; it will only parse the top-level packets.
135    ///
136    /// This is a u8, because recursing more than 255 times makes no
137    /// sense.  The default is [`DEFAULT_MAX_RECURSION_DEPTH`].
138    /// (GnuPG defaults to a maximum recursion depth of 32.)
139    ///
140    /// [`DEFAULT_MAX_RECURSION_DEPTH`]: crate::parse::DEFAULT_MAX_RECURSION_DEPTH
141    ///
142    /// # Examples
143    ///
144    /// ```rust
145    /// # fn main() -> sequoia_openpgp::Result<()> {
146    /// use sequoia_openpgp as openpgp;
147    /// use openpgp::Packet;
148    /// use openpgp::parse::{Parse, PacketParserResult, PacketParserBuilder};
149    ///
150    /// // Parse a compressed message.
151    /// let message_data: &[u8] = // ...
152    /// #    include_bytes!("../../tests/data/messages/compressed-data-algo-0.pgp");
153    /// let mut ppr = PacketParserBuilder::from_bytes(message_data)?
154    ///     .max_recursion_depth(0)
155    ///     .build()?;
156    /// while let PacketParserResult::Some(mut pp) = ppr {
157    ///     assert_eq!(pp.recursion_depth(), 0);
158    ///
159    ///     // Start parsing the next packet, recursing.
160    ///     ppr = pp.recurse()?.1;
161    /// }
162    /// # Ok(()) }
163    /// ```
164    pub fn max_recursion_depth(mut self, value: u8) -> Self {
165        self.settings.max_recursion_depth = value;
166        self
167    }
168
169    /// Sets the maximum size in bytes of non-container packets.
170    ///
171    /// Packets that exceed this limit will be returned as
172    /// `Packet::Unknown`, with the error set to
173    /// `Error::PacketTooLarge`.
174    ///
175    /// This limit applies to any packet type that is *not* a
176    /// container packet, i.e. any packet that is not a literal data
177    /// packet, a compressed data packet, a symmetrically encrypted
178    /// data packet, or an AEAD encrypted data packet.
179    ///
180    /// The default is [`DEFAULT_MAX_PACKET_SIZE`].
181    ///
182    /// [`DEFAULT_MAX_PACKET_SIZE`]: crate::parse::DEFAULT_MAX_PACKET_SIZE
183    ///
184    /// # Examples
185    ///
186    /// ```rust
187    /// # fn main() -> sequoia_openpgp::Result<()> {
188    /// use sequoia_openpgp as openpgp;
189    /// use openpgp::{Error, Packet};
190    /// use openpgp::packet::Tag;
191    /// use openpgp::parse::{Parse, PacketParserResult, PacketParserBuilder};
192    /// use openpgp::serialize::MarshalInto;
193    ///
194    /// // Parse a signed message.
195    /// let message_data: &[u8] = // ...
196    /// #    include_bytes!("../../tests/data/messages/signed-1.gpg");
197    /// let mut ppr = PacketParserBuilder::from_bytes(message_data)?
198    ///     .max_packet_size(256)    // Only parse 256 bytes of headers.
199    ///     .buffer_unread_content() // Used below.
200    ///     .build()?;
201    /// while let PacketParserResult::Some(mut pp) = ppr {
202    ///     match &pp.packet {
203    ///         Packet::OnePassSig(p) =>
204    ///             // The OnePassSig packet was small enough.
205    ///             assert!(p.serialized_len() < 256),
206    ///         Packet::Literal(p) =>
207    ///             // Likewise the `Literal` packet, excluding the body.
208    ///             assert!(p.serialized_len() - p.body().len() < 256),
209    ///         Packet::Unknown(p) =>
210    ///             // The signature packet was too big.
211    ///             assert_eq!(
212    ///                 &Error::PacketTooLarge(Tag::Signature, 307, 256),
213    ///                 p.error().downcast_ref().unwrap()),
214    ///         _ => unreachable!(),
215    ///     }
216    ///
217    ///     // Start parsing the next packet, recursing.
218    ///     ppr = pp.recurse()?.1;
219    /// }
220    /// # Ok(()) }
221    /// ```
222    pub fn max_packet_size(mut self, value: u32) -> Self {
223        self.settings.max_packet_size = value;
224        self
225    }
226
227    /// Causes `PacketParser::build()` to buffer any unread content.
228    ///
229    /// The unread content can be accessed using [`Literal::body`],
230    /// [`Unknown::body`], or [`Container::body`].
231    ///
232    ///   [`Literal::body`]: crate::packet::Literal::body()
233    ///   [`Unknown::body`]: crate::packet::Unknown::body()
234    ///   [`Container::body`]: crate::packet::Container::body()
235    ///
236    /// # Examples
237    ///
238    /// ```rust
239    /// # fn main() -> sequoia_openpgp::Result<()> {
240    /// use sequoia_openpgp as openpgp;
241    /// use openpgp::Packet;
242    /// use openpgp::parse::{Parse, PacketParserResult, PacketParserBuilder};
243    ///
244    /// // Parse a simple message.
245    /// let message_data = b"\xcb\x12t\x00\x00\x00\x00\x00Hello world.";
246    /// let mut ppr = PacketParserBuilder::from_bytes(message_data)?
247    ///     .buffer_unread_content()
248    ///     .build()?;
249    /// while let PacketParserResult::Some(mut pp) = ppr {
250    ///     // Start parsing the next packet, recursing.
251    ///     let (packet, tmp) = pp.recurse()?;
252    ///     ppr = tmp;
253    ///
254    ///     match packet {
255    ///         Packet::Literal(l) => assert_eq!(l.body(), b"Hello world."),
256    ///         _ => unreachable!(),
257    ///     }
258    /// }
259    /// # Ok(()) }
260    /// ```
261    pub fn buffer_unread_content(mut self) -> Self {
262        self.settings.buffer_unread_content = true;
263        self
264    }
265
266    /// Causes `PacketParser::finish()` to drop any unread content.
267    ///
268    /// This is the default.
269    ///
270    /// # Examples
271    ///
272    /// ```rust
273    /// # fn main() -> sequoia_openpgp::Result<()> {
274    /// use sequoia_openpgp as openpgp;
275    /// use openpgp::Packet;
276    /// use openpgp::parse::{Parse, PacketParserResult, PacketParserBuilder};
277    ///
278    /// // Parse a simple message.
279    /// let message_data = b"\xcb\x12t\x00\x00\x00\x00\x00Hello world.";
280    /// let mut ppr = PacketParserBuilder::from_bytes(message_data)?
281    ///     .drop_unread_content()
282    ///     .build()?;
283    /// while let PacketParserResult::Some(mut pp) = ppr {
284    ///     // Start parsing the next packet, recursing.
285    ///     let (packet, tmp) = pp.recurse()?;
286    ///     ppr = tmp;
287    ///
288    ///     match packet {
289    ///         Packet::Literal(l) => assert_eq!(l.body(), b""),
290    ///         _ => unreachable!(),
291    ///     }
292    /// }
293    /// # Ok(()) }
294    /// ```
295    pub fn drop_unread_content(mut self) -> Self {
296        self.settings.buffer_unread_content = false;
297        self
298    }
299
300    /// Controls mapping.
301    ///
302    /// Note that enabling mapping buffers all the data.
303    ///
304    /// # Examples
305    ///
306    /// ```
307    /// # fn main() -> sequoia_openpgp::Result<()> {
308    /// use sequoia_openpgp as openpgp;
309    /// use openpgp::parse::{Parse, PacketParserBuilder};
310    ///
311    /// let message_data = b"\xcb\x12t\x00\x00\x00\x00\x00Hello world.";
312    /// let pp = PacketParserBuilder::from_bytes(message_data)?
313    ///     .map(true) // Enable mapping.
314    ///     .build()?
315    ///     .expect("One packet, not EOF");
316    /// let map = pp.map().expect("Mapping is enabled");
317    ///
318    /// assert_eq!(map.iter().nth(0).unwrap().name(), "CTB");
319    /// assert_eq!(map.iter().nth(0).unwrap().offset(), 0);
320    /// assert_eq!(map.iter().nth(0).unwrap().as_bytes(), &[0xcb]);
321    /// # Ok(()) }
322    /// ```
323    pub fn map(mut self, enable: bool) -> Self {
324        self.settings.map = enable;
325        self
326    }
327
328    /// Controls dearmoring.
329    ///
330    /// By default, if the input does not appear to be plain binary
331    /// OpenPGP data, we assume that it is ASCII-armored.  This method
332    /// can be used to tweak the behavior.  See [`Dearmor`] for
333    /// details.
334    ///
335    ///
336    /// # Examples
337    ///
338    /// ```
339    /// # fn main() -> sequoia_openpgp::Result<()> {
340    /// use sequoia_openpgp as openpgp;
341    /// use openpgp::parse::{Parse, PacketParserBuilder, Dearmor};
342    ///
343    /// let message_data = b"\xcb\x12t\x00\x00\x00\x00\x00Hello world.";
344    /// let pp = PacketParserBuilder::from_bytes(message_data)?
345    ///     .dearmor(Dearmor::Disabled) // Disable dearmoring.
346    ///     .build()?
347    ///     .expect("One packet, not EOF");
348    /// # Ok(()) }
349    /// ```
350    pub fn dearmor(mut self, mode: Dearmor) -> Self {
351        self.dearmor = mode;
352        self
353    }
354
355    /// Controls automatic hashing.
356    ///
357    /// When encountering a [`OnePassSig`] packet, the packet parser
358    /// will, by default, start hashing later packets using the hash
359    /// algorithm specified in the packet.  In some cases, this is not
360    /// needed, and hashing will incur a non-trivial overhead.
361    ///
362    /// If automatic hashing is disabled, then hashing may be
363    /// explicitly enabled using [`PacketParser::start_hashing`] while
364    /// parsing each [`OnePassSig`] packet.
365    ///
366    ///   [`OnePassSig`]: crate::packet::OnePassSig
367    ///
368    /// # Examples
369    ///
370    /// ```
371    /// # fn main() -> sequoia_openpgp::Result<()> {
372    /// # use sequoia_openpgp as openpgp;
373    /// # use openpgp::parse::{Parse, PacketParserBuilder};
374    /// #
375    /// let message_data = b"\xcb\x12t\x00\x00\x00\x00\x00Hello world.";
376    /// let pp = PacketParserBuilder::from_bytes(message_data)?
377    ///     .automatic_hashing(false) // Disable automatic hashing.
378    ///     .build()?
379    ///     .expect("One packet, not EOF");
380    /// # Ok(()) }
381    /// ```
382    pub fn automatic_hashing(mut self, enable: bool) -> Self {
383        self.settings.automatic_hashing = enable;
384        self
385    }
386
387    /// Controls transparent transformation of messages using the
388    /// cleartext signature framework into signed messages.
389    ///
390    /// XXX: This could be controlled by `Dearmor`, but we cannot add
391    /// values to that now.
392    pub(crate) fn csf_transformation(mut self, enable: bool) -> Self {
393        self.csf_transformation = enable;
394        self
395    }
396
397    /// Builds the `PacketParser`.
398    ///
399    /// # Examples
400    ///
401    /// ```rust
402    /// # fn main() -> sequoia_openpgp::Result<()> {
403    /// use sequoia_openpgp as openpgp;
404    /// use openpgp::parse::{Parse, PacketParserResult, PacketParserBuilder};
405    ///
406    /// // Parse a message.
407    /// let message_data: &[u8] = // ...
408    /// #    include_bytes!("../../tests/data/messages/compressed-data-algo-0.pgp");
409    /// let mut ppr = PacketParserBuilder::from_bytes(message_data)?
410    ///     // Customize the `PacketParserBuilder` here.
411    ///     .build()?;
412    /// while let PacketParserResult::Some(mut pp) = ppr {
413    ///     // ...
414    ///
415    ///     // Start parsing the next packet, recursing.
416    ///     ppr = pp.recurse()?.1;
417    /// }
418    /// # Ok(()) }
419    #[allow(clippy::redundant_pattern_matching)]
420    pub fn build(mut self)
421        -> Result<PacketParserResult<'a>>
422        where Self: 'a
423    {
424        let state = PacketParserState::new(self.settings);
425
426        let dearmor_mode = match self.dearmor {
427            Dearmor::Enabled(mode) => Some(mode),
428            Dearmor::Disabled => None,
429            Dearmor::Auto(mode) => {
430                if self.bio.eof() {
431                    None
432                } else {
433                    let mut reader = buffered_reader::Dup::with_cookie(
434                        self.bio, Cookie::default());
435                    let header = packet::Header::parse(&mut reader);
436                    self.bio = Box::new(reader).into_inner().unwrap();
437                    if let Ok(header) = header {
438                        if let Err(_) = header.valid(false) {
439                            // Invalid header: better try an ASCII armor
440                            // decoder.
441                            Some(mode)
442                        } else {
443                            None
444                        }
445                    } else {
446                        // Failed to parse the header: better try an ASCII
447                        // armor decoder.
448                        Some(mode)
449                    }
450                }
451            }
452        };
453
454        if let Some(mode) = dearmor_mode {
455            // Add a top-level filter so that it is peeled off when
456            // the packet parser is finished.  We use level -2 for that.
457            self.bio =
458                armor::Reader::from_cookie_reader_csft(self.bio, Some(mode),
459                    Cookie::new(ARMOR_READER_LEVEL), self.csf_transformation)
460                .into_boxed();
461        }
462
463        // Parse the first packet.
464        match PacketParser::parse(Box::new(self.bio), state, vec![ 0 ])? {
465            ParserResult::Success(mut pp) => {
466                // We successfully parsed the first packet's header.
467                pp.state.message_validator.push(
468                    pp.packet.tag(), pp.packet.version(), &[0]);
469                pp.state.keyring_validator.push(pp.packet.tag());
470                pp.state.cert_validator.push(pp.packet.tag());
471                Ok(PacketParserResult::Some(pp))
472            },
473            ParserResult::EOF((reader, state, _path)) => {
474                // `bio` is empty.  We're done.
475                Ok(PacketParserResult::EOF(PacketParserEOF::new(state, reader)))
476            }
477        }
478    }
479}
480
481#[cfg(test)]
482mod tests {
483    use super::*;
484
485    #[test]
486    fn armor() {
487        // Not ASCII armor encoded data.
488        let msg = crate::tests::message("sig.gpg");
489
490        // Make sure we can read the first packet.
491        let ppr = PacketParserBuilder::from_bytes(msg).unwrap()
492            .dearmor(Dearmor::Disabled)
493            .build();
494        assert_match!(Ok(PacketParserResult::Some(ref _pp)) = ppr);
495
496        let ppr = PacketParserBuilder::from_bytes(msg).unwrap()
497            .dearmor(Dearmor::Auto(Default::default()))
498            .build();
499        assert_match!(Ok(PacketParserResult::Some(ref _pp)) = ppr);
500
501        let ppr = PacketParserBuilder::from_bytes(msg).unwrap()
502            .dearmor(Dearmor::Enabled(Default::default()))
503            .build();
504        assert_match!(Err(_) = ppr);
505
506        // ASCII armor encoded data.
507        let msg = crate::tests::message("a-cypherpunks-manifesto.txt.ed25519.sig");
508
509        // Make sure we can read the first packet.
510        let ppr = PacketParserBuilder::from_bytes(msg).unwrap()
511            .dearmor(Dearmor::Disabled)
512            .build();
513        assert_match!(Err(_) = ppr);
514
515        let ppr = PacketParserBuilder::from_bytes(msg).unwrap()
516            .dearmor(Dearmor::Auto(Default::default()))
517            .build();
518        assert_match!(Ok(PacketParserResult::Some(ref _pp)) = ppr);
519
520        let ppr = PacketParserBuilder::from_bytes(msg).unwrap()
521            .dearmor(Dearmor::Enabled(Default::default()))
522            .build();
523        assert_match!(Ok(PacketParserResult::Some(ref _pp)) = ppr);
524    }
525}