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