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}