1 //! ASCII Armor.
2 //!
3 //! This module deals with ASCII Armored data (see [Section 6 of RFC
4 //! 4880]).
5 //!
6 //! [Section 6 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-6
7 //!
8 //! # Scope
9 //!
10 //! This implements a subset of the ASCII Armor specification. Not
11 //! supported multipart messages.
12 //!
13 //! # Memory allocations
14 //!
15 //! Both the reader and the writer allocate memory in the order of the
16 //! size of chunks read or written.
17 //!
18 //! # Examples
19 //!
20 //! ```rust, no_run
21 //! use sequoia_openpgp as openpgp;
22 //! use std::fs::File;
23 //! use openpgp::armor::{Reader, ReaderMode, Kind};
24 //!
25 //! let mut file = File::open("somefile.asc").unwrap();
26 //! let mut r = Reader::new(&mut file, ReaderMode::Tolerant(Some(Kind::File)));
27 //! ```
28
29 extern crate base64;
30 use buffered_reader::BufferedReader;
31 use std::io::{Cursor, Read, Write};
32 use std::io::{Result, Error, ErrorKind};
33 use std::path::Path;
34 use std::cmp;
35 use std::str;
36 use std::borrow::Cow;
37
38 #[cfg(any(test, feature = "quickcheck"))]
39 use quickcheck::{Arbitrary, Gen};
40
41 use crate::vec_truncate;
42 use crate::packet::prelude::*;
43 use crate::packet::header::{BodyLength, CTBNew, CTBOld};
44 use crate::serialize::MarshalInto;
45
46 /// The encoded output stream must be represented in lines of no more
47 /// than 76 characters each (see (see [RFC 4880, section
48 /// 6.3](https://tools.ietf.org/html/rfc4880#section-6.3). GnuPG uses
49 /// 64.
50 pub(crate) const LINE_LENGTH: usize = 64;
51
52 const LINE_ENDING: &str = "\n";
53
54 /// Specifies the type of data (see [RFC 4880, section 6.2]).
55 ///
56 /// [RFC 4880, section 6.2]: https://tools.ietf.org/html/rfc4880#section-6.2
57 #[derive(Copy, Clone, Debug, PartialEq)]
58 pub enum Kind {
59 /// A generic OpenPGP message. (Since its structure hasn't been
60 /// validated, in this crate's terminology, this is just a
61 /// `PacketPile`.)
62 Message,
63 /// A certificate.
64 PublicKey,
65 /// A transferable secret key.
66 SecretKey,
67 /// A detached signature.
68 Signature,
69 /// A generic file. This is a GnuPG extension.
70 File,
71 }
72
73 #[cfg(any(test, feature = "quickcheck"))]
74 impl Arbitrary for Kind {
arbitrary<G: Gen>(g: &mut G) -> Self75 fn arbitrary<G: Gen>(g: &mut G) -> Self {
76 use self::Kind::*;
77 match u8::arbitrary(g) % 5 {
78 0 => Message,
79 1 => PublicKey,
80 2 => SecretKey,
81 3 => Signature,
82 4 => File,
83 _ => unreachable!(),
84 }
85 }
86 }
87
88 impl Kind {
89 /// Autodetects the kind of data.
detect(blurb: &[u8]) -> Option<Self>90 fn detect(blurb: &[u8]) -> Option<Self> {
91 if blurb.len() < "-----BEGIN PGP MESSAGE-----".len()
92 || ! blurb.starts_with(b"-----BEGIN PGP ")
93 {
94 return None;
95 }
96
97 let kind = &blurb[15..];
98 if kind.starts_with(b"MESSAGE-----") {
99 Some(Kind::Message)
100 } else if kind.starts_with(b"PUBLIC KEY BLOCK-----") {
101 Some(Kind::PublicKey)
102 } else if kind.starts_with(b"PRIVATE KEY BLOCK-----") {
103 Some(Kind::SecretKey)
104 } else if kind.starts_with(b"SIGNATURE-----") {
105 Some(Kind::Signature)
106 } else if kind.starts_with(b"ARMORED FILE-----") {
107 Some(Kind::File)
108 } else {
109 None
110 }
111 }
112
blurb(&self) -> &str113 fn blurb(&self) -> &str {
114 match self {
115 &Kind::Message => "MESSAGE",
116 &Kind::PublicKey => "PUBLIC KEY BLOCK",
117 &Kind::SecretKey => "PRIVATE KEY BLOCK",
118 &Kind::Signature => "SIGNATURE",
119 &Kind::File => "ARMORED FILE",
120 }
121 }
122
begin(&self) -> String123 fn begin(&self) -> String {
124 format!("-----BEGIN PGP {}-----", self.blurb())
125 }
126
end(&self) -> String127 fn end(&self) -> String {
128 format!("-----END PGP {}-----", self.blurb())
129 }
130
131 /// Returns the length of the header.
132 ///
133 /// This does not include any trailing newline. It is simply the
134 /// length of:
135 ///
136 /// ```text
137 /// -----BEGIN PGP BLUB -----
138 /// ```
header_len(&self) -> usize139 fn header_len(&self) -> usize {
140 "-----BEGIN PGP -----".len()
141 + self.blurb().len()
142 }
143 }
144
145 /// A filter that applies ASCII Armor to the data written to it.
146 pub struct Writer<W: Write> {
147 sink: W,
148 kind: Kind,
149 stash: Vec<u8>,
150 column: usize,
151 crc: CRC,
152 header: Vec<u8>,
153 dirty: bool,
154 }
155
156 impl<W: Write> Writer<W> {
157 /// Constructs a new filter for the given type of data.
158 ///
159 /// # Examples
160 ///
161 /// ```
162 /// use std::io::{Read, Write, Cursor};
163 /// use sequoia_openpgp as openpgp;
164 /// use openpgp::armor::{Writer, Kind};
165 ///
166 /// # fn main() -> std::io::Result<()> {
167 /// let mut writer = Writer::new(Vec::new(), Kind::File)?;
168 /// writer.write_all(b"Hello world!")?;
169 /// let buffer = writer.finalize()?;
170 /// assert_eq!(
171 /// String::from_utf8_lossy(&buffer),
172 /// "-----BEGIN PGP ARMORED FILE-----
173 ///
174 /// SGVsbG8gd29ybGQh
175 /// =s4Gu
176 /// -----END PGP ARMORED FILE-----
177 /// ");
178 /// # Ok(())
179 /// # }
180 /// ```
new(inner: W, kind: Kind) -> Result<Self>181 pub fn new(inner: W, kind: Kind) -> Result<Self> {
182 Self::with_headers(inner, kind, Option::<(&str, &str)>::None)
183 }
184
185 /// Constructs a new filter for the given type of data.
186 ///
187 /// # Examples
188 ///
189 /// ```
190 /// use std::io::{Read, Write, Cursor};
191 /// use sequoia_openpgp as openpgp;
192 /// use openpgp::armor::{Writer, Kind};
193 ///
194 /// # fn main() -> std::io::Result<()> {
195 /// let mut writer = Writer::with_headers(Vec::new(), Kind::File,
196 /// vec![("Key", "Value")])?;
197 /// writer.write_all(b"Hello world!")?;
198 /// let buffer = writer.finalize()?;
199 /// assert_eq!(
200 /// String::from_utf8_lossy(&buffer),
201 /// "-----BEGIN PGP ARMORED FILE-----
202 /// Key: Value
203 ///
204 /// SGVsbG8gd29ybGQh
205 /// =s4Gu
206 /// -----END PGP ARMORED FILE-----
207 /// ");
208 /// # Ok(())
209 /// # }
210 /// ```
with_headers<I, K, V>(inner: W, kind: Kind, headers: I) -> Result<Self> where I: IntoIterator<Item = (K, V)>, K: AsRef<str>, V: AsRef<str>,211 pub fn with_headers<I, K, V>(inner: W, kind: Kind, headers: I)
212 -> Result<Self>
213 where I: IntoIterator<Item = (K, V)>,
214 K: AsRef<str>,
215 V: AsRef<str>,
216 {
217 let mut w = Writer {
218 sink: inner,
219 kind,
220 stash: Vec::<u8>::with_capacity(2),
221 column: 0,
222 crc: CRC::new(),
223 header: Vec::with_capacity(128),
224 dirty: false,
225 };
226
227 {
228 let mut cur = Cursor::new(&mut w.header);
229 write!(&mut cur, "{}{}", kind.begin(), LINE_ENDING)?;
230
231 for h in headers {
232 write!(&mut cur, "{}: {}{}", h.0.as_ref(), h.1.as_ref(),
233 LINE_ENDING)?;
234 }
235
236 // A blank line separates the headers from the body.
237 write!(&mut cur, "{}", LINE_ENDING)?;
238 }
239
240 Ok(w)
241 }
242
243 /// Returns a reference to the inner writer.
get_ref(&self) -> &W244 pub fn get_ref(&self) -> &W {
245 &self.sink
246 }
247
248 /// Returns a mutable reference to the inner writer.
get_mut(&mut self) -> &mut W249 pub fn get_mut(&mut self) -> &mut W {
250 &mut self.sink
251 }
252
finalize_headers(&mut self) -> Result<()>253 fn finalize_headers(&mut self) -> Result<()> {
254 if ! self.dirty {
255 self.dirty = true;
256 self.sink.write_all(&self.header)?;
257 // Release memory.
258 crate::vec_truncate(&mut self.header, 0);
259 self.header.shrink_to_fit();
260 }
261 Ok(())
262 }
263
264 /// Writes the footer.
265 ///
266 /// This function needs to be called explicitly before the writer is dropped.
finalize(mut self) -> Result<W>267 pub fn finalize(mut self) -> Result<W> {
268 if ! self.dirty {
269 // No data was written to us, don't emit anything.
270 return Ok(self.sink);
271 }
272 self.finalize_armor()?;
273 Ok(self.sink)
274 }
275
276 /// Writes the footer.
finalize_armor(&mut self) -> Result<()>277 fn finalize_armor(&mut self) -> Result<()> {
278 if ! self.dirty {
279 // No data was written to us, don't emit anything.
280 return Ok(());
281 }
282 self.finalize_headers()?;
283
284 // Write any stashed bytes and pad.
285 if self.stash.len() > 0 {
286 self.sink.write_all(base64::encode_config(
287 &self.stash, base64::STANDARD).as_bytes())?;
288 self.column += 4;
289 }
290
291 // Inserts a line break if necessary.
292 //
293 // Unfortunately, we cannot use
294 //self.linebreak()?;
295 //
296 // Therefore, we inline it here. This is a bit sad.
297 assert!(self.column <= LINE_LENGTH);
298 if self.column == LINE_LENGTH {
299 write!(self.sink, "{}", LINE_ENDING)?;
300 self.column = 0;
301 }
302
303 if self.column > 0 {
304 write!(self.sink, "{}", LINE_ENDING)?;
305 }
306
307 // 24-bit CRC
308 let crc = self.crc.finalize();
309 let bytes = &crc.to_be_bytes()[1..4];
310
311 // CRC and footer.
312 write!(self.sink, "={}{}{}{}",
313 base64::encode_config(&bytes, base64::STANDARD_NO_PAD),
314 LINE_ENDING, self.kind.end(), LINE_ENDING)?;
315
316 self.dirty = false;
317 Ok(())
318 }
319
320 /// Inserts a line break if necessary.
linebreak(&mut self) -> Result<()>321 fn linebreak(&mut self) -> Result<()> {
322 assert!(self.column <= LINE_LENGTH);
323 if self.column == LINE_LENGTH {
324 write!(self.sink, "{}", LINE_ENDING)?;
325 self.column = 0;
326 }
327 Ok(())
328 }
329 }
330
331 impl<W: Write> Write for Writer<W> {
write(&mut self, buf: &[u8]) -> Result<usize>332 fn write(&mut self, buf: &[u8]) -> Result<usize> {
333 self.finalize_headers()?;
334
335 // Update CRC on the unencoded data.
336 self.crc.update(buf);
337
338 let mut input = buf;
339 let mut written = 0;
340
341 // First of all, if there are stashed bytes, fill the stash
342 // and encode it. If writing out the stash fails below, we
343 // might end up with a stash of size 3.
344 assert!(self.stash.len() <= 3);
345 if self.stash.len() > 0 {
346 while self.stash.len() < 3 {
347 if input.len() == 0 {
348 /* We exhausted the input. Return now, any
349 * stashed bytes are encoded when finalizing the
350 * writer. */
351 return Ok(written);
352 }
353 self.stash.push(input[0]);
354 input = &input[1..];
355 written += 1;
356 }
357 assert_eq!(self.stash.len(), 3);
358
359 // If this fails for some reason, and the caller retries
360 // the write, we might end up with a stash of size 3.
361 self.sink
362 .write_all(base64::encode_config(
363 &self.stash, base64::STANDARD_NO_PAD).as_bytes())?;
364 self.column += 4;
365 self.linebreak()?;
366 crate::vec_truncate(&mut self.stash, 0);
367 }
368
369 // Ensure that a multiple of 3 bytes are encoded, stash the
370 // rest from the end of input.
371 while input.len() % 3 > 0 {
372 self.stash.push(input[input.len()-1]);
373 input = &input[..input.len()-1];
374 written += 1;
375 }
376 // We popped values from the end of the input, fix the order.
377 self.stash.reverse();
378 assert!(self.stash.len() < 3);
379
380 // We know that we have a multiple of 3 bytes, encode them and write them out.
381 assert!(input.len() % 3 == 0);
382 let encoded = base64::encode_config(input, base64::STANDARD_NO_PAD);
383 written += input.len();
384 let mut enc = encoded.as_bytes();
385 while enc.len() > 0 {
386 let n = cmp::min(LINE_LENGTH - self.column, enc.len());
387 self.sink
388 .write_all(&enc[..n])?;
389 enc = &enc[n..];
390 self.column += n;
391 self.linebreak()?;
392 }
393
394 assert_eq!(written, buf.len());
395 Ok(written)
396 }
397
flush(&mut self) -> Result<()>398 fn flush(&mut self) -> Result<()> {
399 self.sink.flush()
400 }
401 }
402
403 /// How an ArmorReader should act.
404 #[derive(Debug, Clone, Copy, PartialEq)]
405 pub enum ReaderMode {
406 /// Makes the armor reader tolerant of simple errors.
407 ///
408 /// The armor reader will be tolerant of common formatting errors,
409 /// such as incorrect line folding, but the armor header line
410 /// (e.g., `----- BEGIN PGP MESSAGE -----`) and the footer must be
411 /// intact.
412 ///
413 /// If a Kind is specified, then only ASCII Armor blocks with the
414 /// appropriate header are recognized.
415 ///
416 /// This mode is appropriate when reading from a file.
417 Tolerant(Option<Kind>),
418
419 /// Makes the armor reader very tolerant of errors.
420 ///
421 /// Unlike in `Tolerant` mode, in this mode, the armor reader
422 /// doesn't require an armor header line. Instead, it examines
423 /// chunks that look like valid base64 data, and attempts to parse
424 /// them.
425 ///
426 /// Although this mode looks for OpenPGP fingerprints before
427 /// invoking the full parser, due to the number of false
428 /// positives, this mode of operation is CPU intense, particularly
429 /// on large text files. It is primarily appropriate when reading
430 /// text that the user cut and pasted into a text area.
431 VeryTolerant,
432 }
433
434 /// A filter that strips ASCII Armor from a stream of data.
435 pub struct Reader<'a> {
436 source: Box<dyn BufferedReader<()> + 'a>,
437 kind: Option<Kind>,
438 mode: ReaderMode,
439 buffer: Vec<u8>,
440 crc: CRC,
441 expect_crc: Option<u32>,
442 initialized: bool,
443 headers: Vec<(String, String)>,
444 finalized: bool,
445 prefix_len: usize,
446 prefix_remaining: usize,
447 }
448
449 impl Default for ReaderMode {
default() -> Self450 fn default() -> Self {
451 ReaderMode::Tolerant(None)
452 }
453 }
454
455 impl<'a> Reader<'a> {
456 /// Constructs a new filter for the given type of data.
457 ///
458 /// [ASCII Armor], designed to protect OpenPGP data in transit,
459 /// has been a source of problems if the armor structure is
460 /// damaged. For example, copying data manually from one program
461 /// to another might introduce or drop newlines.
462 ///
463 /// By default, the reader operates in robust mode. It will
464 /// extract the first armored OpenPGP data block it can find, even
465 /// if the armor frame is damaged, or missing.
466 ///
467 /// To select strict mode, specify a kind argument. In strict
468 /// mode, the reader will match on the armor frame. The reader
469 /// ignores any data in front of the Armor Header Line, as long as
470 /// the line the header is only prefixed by whitespace.
471 ///
472 /// [ASCII Armor]: https://tools.ietf.org/html/rfc4880#section-6.2
473 ///
474 /// # Examples
475 ///
476 /// ```
477 /// use std::io::{self, Read};
478 /// use sequoia_openpgp as openpgp;
479 /// use openpgp::Message;
480 /// use openpgp::armor::{Reader, ReaderMode};
481 /// use openpgp::parse::Parse;
482 ///
483 /// # fn main() -> openpgp::Result<()> {
484 /// let data = "yxJiAAAAAABIZWxsbyB3b3JsZCE="; // base64 over literal data packet
485 ///
486 /// let mut cursor = io::Cursor::new(&data);
487 /// let mut reader = Reader::new(&mut cursor, ReaderMode::VeryTolerant);
488 ///
489 /// let mut buf = Vec::new();
490 /// reader.read_to_end(&mut buf)?;
491 ///
492 /// let message = Message::from_bytes(&buf)?;
493 /// assert_eq!(message.body().unwrap().body(),
494 /// b"Hello world!");
495 /// # Ok(())
496 /// # }
497 /// ```
498 ///
499 /// Or, in strict mode:
500 ///
501 /// ```
502 /// use std::io::{self, Result, Read};
503 /// use sequoia_openpgp as openpgp;
504 /// use openpgp::armor::{Reader, ReaderMode, Kind};
505 ///
506 /// # fn main() -> Result<()> {
507 /// let data =
508 /// "-----BEGIN PGP ARMORED FILE-----
509 ///
510 /// SGVsbG8gd29ybGQh
511 /// =s4Gu
512 /// -----END PGP ARMORED FILE-----";
513 ///
514 /// let mut cursor = io::Cursor::new(&data);
515 /// let mut reader = Reader::new(&mut cursor, ReaderMode::Tolerant(Some(Kind::File)));
516 ///
517 /// let mut content = String::new();
518 /// reader.read_to_string(&mut content)?;
519 /// assert_eq!(content, "Hello world!");
520 /// assert_eq!(reader.kind(), Some(Kind::File));
521 /// # Ok(())
522 /// # }
523 /// ```
new<R, M>(inner: R, mode: M) -> Self where R: 'a + Read, M: Into<Option<ReaderMode>>524 pub fn new<R, M>(inner: R, mode: M) -> Self
525 where R: 'a + Read,
526 M: Into<Option<ReaderMode>>
527 {
528 Self::from_buffered_reader(
529 Box::new(buffered_reader::Generic::new(inner, None)),
530 mode)
531 }
532
533 /// Creates a `Reader` from an `io::Read`er.
from_reader<R, M>(reader: R, mode: M) -> Self where R: 'a + Read, M: Into<Option<ReaderMode>>534 pub fn from_reader<R, M>(reader: R, mode: M) -> Self
535 where R: 'a + Read,
536 M: Into<Option<ReaderMode>>
537 {
538 Self::from_buffered_reader(
539 Box::new(buffered_reader::Generic::new(reader, None)),
540 mode)
541 }
542
543 /// Creates a `Reader` from a file.
from_file<P, M>(path: P, mode: M) -> Result<Self> where P: AsRef<Path>, M: Into<Option<ReaderMode>>544 pub fn from_file<P, M>(path: P, mode: M) -> Result<Self>
545 where P: AsRef<Path>,
546 M: Into<Option<ReaderMode>>
547 {
548 Ok(Self::from_buffered_reader(
549 Box::new(buffered_reader::File::open(path)?),
550 mode))
551 }
552
553 /// Creates a `Reader` from a buffer.
from_bytes<M>(bytes: &'a [u8], mode: M) -> Self where M: Into<Option<ReaderMode>>554 pub fn from_bytes<M>(bytes: &'a [u8], mode: M) -> Self
555 where M: Into<Option<ReaderMode>>
556 {
557 Self::from_buffered_reader(
558 Box::new(buffered_reader::Memory::new(bytes)),
559 mode)
560 }
561
from_buffered_reader<C: 'a, M>( inner: Box<dyn BufferedReader<C> + 'a>, mode: M) -> Self where M: Into<Option<ReaderMode>>562 pub(crate) fn from_buffered_reader<C: 'a, M>(
563 inner: Box<dyn BufferedReader<C> + 'a>, mode: M) -> Self
564 where M: Into<Option<ReaderMode>>
565 {
566 let mode = mode.into().unwrap_or(Default::default());
567
568 Reader {
569 source: Box::new(buffered_reader::Generic::new(inner, None)),
570 kind: None,
571 mode,
572 buffer: Vec::<u8>::with_capacity(1024),
573 crc: CRC::new(),
574 expect_crc: None,
575 headers: Vec::new(),
576 initialized: false,
577 finalized: false,
578 prefix_len: 0,
579 prefix_remaining: 0,
580 }
581 }
582
583 /// Returns the kind of data this reader is for.
584 ///
585 /// Useful if the kind of data is not known in advance. If the
586 /// header has not been encountered yet (try reading some data
587 /// first!), this function returns None.
kind(&self) -> Option<Kind>588 pub fn kind(&self) -> Option<Kind> {
589 self.kind
590 }
591
592 /// Returns the armored headers.
593 ///
594 /// The tuples contain a key and a value.
595 ///
596 /// Note: if a key occurs multiple times, then there are multiple
597 /// entries in the vector with the same key; values with the same
598 /// key are *not* combined.
599 ///
600 /// # Examples
601 ///
602 /// ```
603 /// use std::io::{self, Read};
604 /// use sequoia_openpgp as openpgp;
605 /// use openpgp::armor::{Reader, ReaderMode, Kind};
606 ///
607 /// # fn main() -> std::io::Result<()> {
608 /// let data =
609 /// "-----BEGIN PGP ARMORED FILE-----
610 /// First: value
611 /// Header: value
612 ///
613 /// SGVsbG8gd29ybGQh
614 /// =s4Gu
615 /// -----END PGP ARMORED FILE-----";
616 ///
617 /// let mut cursor = io::Cursor::new(&data);
618 /// let mut reader = Reader::new(&mut cursor, ReaderMode::Tolerant(Some(Kind::File)));
619 ///
620 /// let mut content = String::new();
621 /// reader.read_to_string(&mut content)?;
622 /// assert_eq!(reader.headers().unwrap(),
623 /// &[("First".into(), "value".into()),
624 /// ("Header".into(), "value".into())]);
625 /// # Ok(())
626 /// # }
627 /// ```
headers(&mut self) -> Result<&[(String, String)]>628 pub fn headers(&mut self) -> Result<&[(String, String)]> {
629 self.initialize()?;
630 Ok(&self.headers[..])
631 }
632
633 /// Consumes the header if not already done.
initialize(&mut self) -> Result<()>634 fn initialize(&mut self) -> Result<()> {
635 if self.initialized { return Ok(()) }
636
637 // The range of the first 6 bits of a message is limited.
638 // Save cpu cycles by only considering base64 data that starts
639 // with one of those characters.
640 lazy_static!{
641 static ref START_CHARS : Vec<u8> = {
642 let mut valid_start = Vec::new();
643 for &tag in &[ Tag::PKESK, Tag::SKESK,
644 Tag::OnePassSig, Tag::Signature,
645 Tag::PublicKey, Tag::SecretKey,
646 Tag::CompressedData, Tag::Literal ] {
647 let mut ctb = [ 0u8; 1 ];
648 let mut o = [ 0u8; 4 ];
649
650 CTBNew::new(tag).serialize_into(&mut ctb[..]).unwrap();
651 base64::encode_config_slice(&ctb[..], base64::STANDARD, &mut o[..]);
652 valid_start.push(o[0]);
653
654 CTBOld::new(tag, BodyLength::Full(0)).unwrap()
655 .serialize_into(&mut ctb[..]).unwrap();
656 base64::encode_config_slice(&ctb[..], base64::STANDARD, &mut o[..]);
657 valid_start.push(o[0]);
658 }
659
660 // The standard start of an ASCII armor header e.g.,
661 //
662 // -----BEGIN PGP MESSAGE-----
663 valid_start.push('-' as u8);
664
665 valid_start.sort();
666 valid_start.dedup();
667 valid_start
668 };
669
670 }
671
672 // Look for the Armor Header Line, skipping any garbage in the
673 // process.
674 let mut found_blob = false;
675 let start_chars = if self.mode != ReaderMode::VeryTolerant {
676 &[b'-'][..]
677 } else {
678 &START_CHARS[..]
679 };
680
681 let mut lines = 0;
682 let mut prefix = Vec::new();
683 let n = 'search: loop {
684 if lines > 0 {
685 // Find the start of the next line.
686 self.source.drop_through(&[b'\n'], true)?;
687 crate::vec_truncate(&mut prefix, 0);
688 }
689 lines += 1;
690
691 // Ignore leading whitespace, etc.
692 while match self.source.data_hard(1)?[0] {
693 // Skip some whitespace (previously .is_ascii_whitespace())
694 b' ' | b'\t' | b'\r' | b'\n' => true,
695 // Also skip common quote characters
696 b'>' | b'|' | b']' | b'}' => true,
697 // Do not skip anything else
698 _ => false,
699 } {
700 let c = self.source.data(1)?[0];
701 if c == b'\n' {
702 // We found a newline while walking whitespace, reset prefix
703 crate::vec_truncate(&mut prefix, 0);
704 } else {
705 prefix.push(self.source.data_hard(1)?[0]);
706 }
707 self.source.consume(1);
708 }
709
710 // Don't bother if the first byte is not plausible.
711 let start = self.source.data_hard(1)?[0];
712 if !start_chars.binary_search(&start).is_ok()
713 {
714 self.source.consume(1);
715 continue;
716 }
717
718 {
719 let mut input = self.source.data(128)?;
720 let n = input.len();
721
722 if n == 0 {
723 return Err(
724 Error::new(ErrorKind::InvalidInput,
725 "Reached EOF looking for Armor Header Line"));
726 }
727 if n > 128 {
728 input = &input[..128];
729 }
730
731 if input[0] == '-' as u8 {
732 // Possible ASCII-armor header.
733 if let Some(kind) = Kind::detect(&input) {
734 let mut expected_kind = None;
735 if let ReaderMode::Tolerant(Some(kind)) = self.mode {
736 expected_kind = Some(kind);
737 }
738
739 if expected_kind == None {
740 // Found any!
741 self.kind = Some(kind);
742 break 'search kind.header_len();
743 }
744
745 if expected_kind == Some(kind) {
746 // Found it!
747 self.kind = Some(kind);
748 break 'search kind.header_len();
749 }
750 }
751 } else if self.mode == ReaderMode::VeryTolerant {
752 // The user did not specify what kind of data she
753 // wants. We aggressively try to decode any data,
754 // even if we do not see a valid header.
755 if is_armored_pgp_blob(input) {
756 found_blob = true;
757 break 'search 0;
758 }
759 }
760 }
761 };
762 self.source.consume(n);
763
764 if found_blob {
765 // Skip the rest of the initialization.
766 self.initialized = true;
767 self.prefix_len = prefix.len();
768 self.prefix_remaining = prefix.len();
769 return Ok(());
770 }
771
772 // We consumed the header above, but not any trailing
773 // whitespace and the trailing new line. We do that now.
774 // Other data between the header and the new line are not
775 // allowed. But, instead of failing, we try to recover, by
776 // stopping at the first non-whitespace character.
777 let n = {
778 let line = self.source.read_to('\n' as u8)?;
779 line.iter().position(|&c| {
780 !c.is_ascii_whitespace()
781 }).unwrap_or(line.len())
782 };
783 self.source.consume(n);
784
785 let next_prefix = &self.source.data_hard(prefix.len())?[..prefix.len()];
786 if prefix != next_prefix {
787 // If the next line doesn't start with the same prefix, we assume
788 // it was garbage on the front and drop the prefix so long as it
789 // was purely whitespace. Any non-whitespace remains an error
790 // while searching for the armor header if it's not repeated.
791 if prefix.iter().all(|b| (*b as char).is_ascii_whitespace()) {
792 crate::vec_truncate(&mut prefix, 0);
793 } else {
794 // Nope, we have actually failed to read this properly
795 return Err(
796 Error::new(ErrorKind::InvalidInput,
797 "Inconsistent quoting of armored data"));
798 }
799 }
800
801 // Read the key-value headers.
802 let mut n = 0;
803 let mut lines = 0;
804 loop {
805 // Skip any known prefix on lines.
806 //
807 // IMPORTANT: We need to buffer the prefix so that we can
808 // consume it here. So at every point in this loop where
809 // the control flow wraps around, we need to make sure
810 // that we buffer the prefix in addition to the line.
811 self.source.consume(prefix.len());
812
813 self.source.consume(n);
814
815 // Buffer the next line.
816 let line = self.source.read_to('\n' as u8)?;
817 n = line.len();
818 lines += 1;
819
820 let line = str::from_utf8(line);
821 // Ignore---don't error out---lines that are not valid UTF8.
822 if line.is_err() {
823 // Buffer the next line and the prefix that is going
824 // to be consumed in the next iteration.
825 let next_prefix =
826 &self.source.data_hard(n + prefix.len())?[n..n + prefix.len()];
827 if prefix != next_prefix {
828 return Err(
829 Error::new(ErrorKind::InvalidInput,
830 "Inconsistent quoting of armored data"));
831 }
832 continue;
833 }
834
835 let line = line.unwrap();
836
837 // The line almost certainly ends with \n: the only reason
838 // it couldn't is if we encountered EOF. We need to strip
839 // it. But, if it ends with \r\n, then we also want to
840 // strip the \r too.
841 let line = if line.ends_with(&"\r\n"[..]) {
842 // \r\n.
843 &line[..line.len() - 2]
844 } else if line.ends_with("\n") {
845 // \n.
846 &line[..line.len() - 1]
847 } else {
848 // EOF.
849 line
850 };
851
852 /* Process headers. */
853 let key_value = line.splitn(2, ": ").collect::<Vec<&str>>();
854 if key_value.len() == 1 {
855 if line.trim_start().len() == 0 {
856 // Empty line.
857 break;
858 } else if lines == 1 {
859 // This is the first line and we don't have a
860 // key-value pair. It seems more likely that
861 // we're just missing a newline and this invalid
862 // header is actually part of the body.
863 n = 0;
864 break;
865 }
866 } else {
867 let key = key_value[0].trim_start();
868 let value = key_value[1];
869
870 self.headers.push((key.into(), value.into()));
871 }
872
873 // Buffer the next line and the prefix that is going to be
874 // consumed in the next iteration.
875 let next_prefix =
876 &self.source.data_hard(n + prefix.len())?[n..n + prefix.len()];
877 if prefix != next_prefix {
878 return Err(
879 Error::new(ErrorKind::InvalidInput,
880 "Inconsistent quoting of armored data"));
881 }
882 }
883 self.source.consume(n);
884
885 self.initialized = true;
886 self.prefix_len = prefix.len();
887 self.prefix_remaining = prefix.len();
888 Ok(())
889 }
890 }
891
892 // Remove whitespace, etc. from the base64 data.
893 //
894 // This function returns the filtered base64 data (i.e., stripped of
895 // all skipable data like whitespace), and the amount of unfiltered
896 // data that corresponds to. Thus, if we have the following 7 bytes:
897 //
898 // ab cde
899 // 0123456
900 //
901 // This function returns ("abcd", 6), because the 'd' is the last
902 // character in the last complete base64 chunk, and it is at offset 5.
903 //
904 // If 'd' is followed by whitespace, it is undefined whether that
905 // whitespace is included in the count.
906 //
907 // This function only returns full chunks of base64 data. As a
908 // consequence, if base64_data_max is less than 4, then this will not
909 // return any data.
910 //
911 // This function will stop after it sees base64 padding, and if it
912 // sees invalid base64 data.
base64_filter(mut bytes: Cow<[u8]>, base64_data_max: usize, mut prefix_remaining: usize, prefix_len: usize) -> (Cow<[u8]>, usize, usize)913 fn base64_filter(mut bytes: Cow<[u8]>, base64_data_max: usize,
914 mut prefix_remaining: usize, prefix_len: usize)
915 -> (Cow<[u8]>, usize, usize)
916 {
917 let mut leading_whitespace = 0;
918
919 // Round down to the nearest chunk size.
920 let base64_data_max = base64_data_max / 4 * 4;
921
922 // Number of bytes of base64 data. Since we update `bytes` in
923 // place, the base64 data is `&bytes[..base64_len]`.
924 let mut base64_len = 0;
925
926 // Offset of the next byte of unfiltered data to process.
927 let mut unfiltered_offset = 0;
928
929 // Offset of the last byte of the last ***complete*** base64 chunk
930 // in the unfiltered data.
931 let mut unfiltered_complete_len = 0;
932
933 // Number of bytes of padding that we've seen so far.
934 let mut padding = 0;
935
936 while unfiltered_offset < bytes.len()
937 && base64_len < base64_data_max
938 // A valid base64 chunk never starts with padding.
939 && ! (padding > 0 && base64_len % 4 == 0)
940 {
941 // If we have some prefix to skip, skip it.
942 if prefix_remaining > 0 {
943 prefix_remaining -= 1;
944 if unfiltered_offset == 0 {
945 match bytes {
946 Cow::Borrowed(s) => {
947 // We're at the beginning. Avoid moving
948 // data by cutting off the start of the
949 // slice.
950 bytes = Cow::Borrowed(&s[1..]);
951 leading_whitespace += 1;
952 continue;
953 }
954 Cow::Owned(_) => (),
955 }
956 }
957 unfiltered_offset += 1;
958 continue;
959 }
960 match bytes[unfiltered_offset] {
961 // White space.
962 c if c.is_ascii_whitespace() => {
963 if c == b'\n' {
964 prefix_remaining = prefix_len;
965 }
966 if unfiltered_offset == 0 {
967 match bytes {
968 Cow::Borrowed(s) => {
969 // We're at the beginning. Avoid moving
970 // data by cutting off the start of the
971 // slice.
972 bytes = Cow::Borrowed(&s[1..]);
973 leading_whitespace += 1;
974 continue;
975 }
976 Cow::Owned(_) => (),
977 }
978 }
979 }
980
981 // Padding.
982 b'=' => {
983 if padding == 2 {
984 // There can never be more than two bytes of
985 // padding.
986 break;
987 }
988 if base64_len % 4 == 0 {
989 // Padding can never occur at the start of a
990 // base64 chunk.
991 break;
992 }
993
994 if unfiltered_offset != base64_len {
995 bytes.to_mut()[base64_len] = b'=';
996 }
997 base64_len += 1;
998 if base64_len % 4 == 0 {
999 unfiltered_complete_len = unfiltered_offset + 1;
1000 }
1001 padding += 1;
1002 }
1003
1004 // The only thing that can occur after padding is
1005 // whitespace or padding. Those cases were covered above.
1006 _ if padding > 0 => break,
1007
1008 // Base64 data!
1009 b if is_base64_char(&b) => {
1010 if unfiltered_offset != base64_len {
1011 bytes.to_mut()[base64_len] = b;
1012 }
1013 base64_len += 1;
1014 if base64_len % 4 == 0 {
1015 unfiltered_complete_len = unfiltered_offset + 1;
1016 }
1017 }
1018
1019 // Not base64 data.
1020 _ => break,
1021 }
1022
1023 unfiltered_offset += 1;
1024 }
1025
1026 let base64_len = base64_len - (base64_len % 4);
1027 unfiltered_complete_len += leading_whitespace;
1028 match bytes {
1029 Cow::Borrowed(s) =>
1030 (Cow::Borrowed(&s[..base64_len]), unfiltered_complete_len,
1031 prefix_remaining),
1032 Cow::Owned(mut v) => {
1033 vec_truncate(&mut v, base64_len);
1034 (Cow::Owned(v), unfiltered_complete_len, prefix_remaining)
1035 }
1036 }
1037 }
1038
1039 /// Checks whether the given bytes contain armored OpenPGP data.
is_armored_pgp_blob(bytes: &[u8]) -> bool1040 fn is_armored_pgp_blob(bytes: &[u8]) -> bool {
1041 // Get up to 32 bytes of base64 data. That's 24 bytes of data
1042 // (ignoring padding), which is more than enough to get the first
1043 // packet's header.
1044 let (bytes, _, _) = base64_filter(Cow::Borrowed(bytes), 32, 0, 0);
1045
1046 match base64::decode_config(&bytes, base64::STANDARD) {
1047 Ok(d) => {
1048 // Don't consider an empty message to be valid.
1049 if d.len() == 0 {
1050 false
1051 } else {
1052 let mut br = buffered_reader::Memory::new(&d);
1053 if let Ok(header) = Header::parse(&mut br) {
1054 header.ctb().tag().valid_start_of_message()
1055 && header.valid(false).is_ok()
1056 } else {
1057 false
1058 }
1059 }
1060 },
1061 Err(_err) => false,
1062 }
1063 }
1064
1065 /// Checks whether the given byte is in the base64 character set.
is_base64_char(b: &u8) -> bool1066 fn is_base64_char(b: &u8) -> bool {
1067 b.is_ascii_alphanumeric() || *b == '+' as u8 || *b == '/' as u8
1068 }
1069
1070 /// Returns the number of bytes of base64 data are needed to encode
1071 /// `s` bytes of raw data.
base64_size(s: usize) -> usize1072 fn base64_size(s: usize) -> usize {
1073 (s + 3 - 1) / 3 * 4
1074 }
1075
1076 #[test]
base64_size_test()1077 fn base64_size_test() {
1078 assert_eq!(base64_size(0), 0);
1079 assert_eq!(base64_size(1), 4);
1080 assert_eq!(base64_size(2), 4);
1081 assert_eq!(base64_size(3), 4);
1082 assert_eq!(base64_size(4), 8);
1083 assert_eq!(base64_size(5), 8);
1084 assert_eq!(base64_size(6), 8);
1085 assert_eq!(base64_size(7), 12);
1086 }
1087
1088 impl<'a> Read for Reader<'a> {
read(&mut self, buf: &mut [u8]) -> Result<usize>1089 fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
1090 if ! self.initialized {
1091 self.initialize()?;
1092 }
1093
1094 if buf.len() == 0 {
1095 // Short-circuit here. Otherwise, we copy 0 bytes into
1096 // the buffer, which means we decoded 0 bytes, and we
1097 // wrongfully assume that we reached the end of the
1098 // armored block.
1099 return Ok(0);
1100 }
1101
1102 if self.finalized {
1103 assert_eq!(self.buffer.len(), 0);
1104 return Ok(0);
1105 }
1106
1107 let (consumed, decoded) = if self.buffer.len() > 0 {
1108 // We have something buffered, use that.
1109
1110 let amount = cmp::min(buf.len(), self.buffer.len());
1111 buf[..amount].copy_from_slice(&self.buffer[..amount]);
1112 crate::vec_drain_prefix(&mut self.buffer, amount);
1113
1114 (0, amount)
1115 } else {
1116 // We need to decode some data. We consider three cases,
1117 // all a function of the size of `buf`:
1118 //
1119 // - Tiny: if `buf` can hold less than three bytes, then
1120 // we almost certainly have to double buffer: except
1121 // at the very end, a base64 chunk consists of 3 bytes
1122 // of data.
1123 //
1124 // Note: this happens if the caller does `for c in
1125 // Reader::new(...).bytes() ...`. Then it reads one
1126 // byte of decoded data at a time.
1127 //
1128 // - Small: if the caller only requests a few bytes at a
1129 // time, we may as well double buffer to reduce
1130 // decoding overhead.
1131 //
1132 // - Large: if `buf` is large, we can decode directly
1133 // into `buf` and avoid double buffering. But,
1134 // because we ignore whitespace, it is hard to
1135 // determine exactly how much data to read to
1136 // maximally fill `buf`.
1137
1138 // We use 64, because ASCII-armor text usually contains 64
1139 // characters of base64 data per line, and this prevents
1140 // turning the borrow into an own.
1141 const THRESHOLD : usize = 64;
1142
1143 let to_read =
1144 cmp::max(
1145 // Tiny or small:
1146 THRESHOLD + 2,
1147
1148 // Large: a heuristic:
1149
1150 base64_size(buf.len())
1151 // Assume about 2 bytes of whitespace (crlf) per
1152 // 64 character line.
1153 + 2 * ((buf.len() + 63) / 64));
1154
1155 let base64data = self.source.data(to_read)?;
1156 let base64data = if base64data.len() > to_read {
1157 &base64data[..to_read]
1158 } else {
1159 base64data
1160 };
1161
1162 let (base64data, consumed, prefix_remaining)
1163 = base64_filter(Cow::Borrowed(base64data),
1164 // base64_size rounds up, but we want
1165 // to round down as we have to double
1166 // buffer partial chunks.
1167 cmp::max(THRESHOLD, buf.len() / 3 * 4),
1168 self.prefix_remaining,
1169 self.prefix_len);
1170
1171 // We shouldn't have any partial chunks.
1172 assert_eq!(base64data.len() % 4, 0);
1173
1174 let decoded = if base64data.len() / 4 * 3 > buf.len() {
1175 // We need to double buffer. Decode into a vector.
1176 // (Note: the computed size *might* be a slight
1177 // overestimate, because the last base64 chunk may
1178 // include padding.)
1179 self.buffer = base64::decode_config(
1180 &base64data, base64::STANDARD)
1181 .map_err(|e| Error::new(ErrorKind::InvalidData, e))?;
1182
1183 self.crc.update(&self.buffer);
1184
1185 let copied = cmp::min(buf.len(), self.buffer.len());
1186 buf[..copied].copy_from_slice(&self.buffer[..copied]);
1187 crate::vec_drain_prefix(&mut self.buffer, copied);
1188
1189 copied
1190 } else {
1191 // We can decode directly into the caller-supplied
1192 // buffer.
1193 let decoded = base64::decode_config_slice(
1194 &base64data, base64::STANDARD, buf)
1195 .map_err(|e| Error::new(ErrorKind::InvalidData, e))?;
1196
1197 self.crc.update(&buf[..decoded]);
1198
1199 decoded
1200 };
1201
1202 self.prefix_remaining = prefix_remaining;
1203
1204 (consumed, decoded)
1205 };
1206
1207 self.source.consume(consumed);
1208 if decoded == 0 {
1209 self.finalized = true;
1210
1211 /* Look for CRC. The CRC is optional. */
1212 let consumed = {
1213 // Skip whitespace.
1214 while self.source.data(1)?.len() > 0
1215 && self.source.buffer()[0].is_ascii_whitespace()
1216 {
1217 self.source.consume(1);
1218 }
1219
1220 let data = self.source.data(5)?;
1221 let data = if data.len() > 5 {
1222 &data[..5]
1223 } else {
1224 data
1225 };
1226
1227 if data.len() == 5
1228 && data[0] == '=' as u8
1229 && data[1..5].iter().all(is_base64_char)
1230 {
1231 /* Found. */
1232 let crc = match base64::decode_config(
1233 &data[1..5], base64::STANDARD)
1234 {
1235 Ok(d) => d,
1236 Err(e) => return Err(Error::new(ErrorKind::InvalidInput, e)),
1237 };
1238
1239 assert_eq!(crc.len(), 3);
1240 let crc =
1241 (crc[0] as u32) << 16
1242 | (crc[1] as u32) << 8
1243 | crc[2] as u32;
1244
1245 self.expect_crc = Some(crc);
1246 5
1247 } else {
1248 0
1249 }
1250 };
1251 self.source.consume(consumed);
1252
1253 // Skip any expected prefix
1254 self.source.data_consume_hard(self.prefix_len)?;
1255 // Look for a footer.
1256 let consumed = {
1257 // Skip whitespace.
1258 while self.source.data(1)?.len() > 0
1259 && self.source.buffer()[0].is_ascii_whitespace()
1260 {
1261 self.source.consume(1);
1262 }
1263
1264 // If we had a header, we require a footer.
1265 if let Some(kind) = self.kind {
1266 let footer = kind.end();
1267 let got = self.source.data(footer.len())?;
1268 let got = if got.len() > footer.len() {
1269 &got[..footer.len()]
1270 } else {
1271 got
1272 };
1273 if footer.as_bytes() != got {
1274 return Err(Error::new(ErrorKind::InvalidInput,
1275 "Invalid ASCII Armor footer."));
1276 }
1277
1278 footer.len()
1279 } else {
1280 0
1281 }
1282 };
1283 self.source.consume(consumed);
1284
1285 if let Some(crc) = self.expect_crc {
1286 if self.crc.finalize() != crc {
1287 return Err(Error::new(ErrorKind::InvalidInput,
1288 "Bad CRC sum."));
1289 }
1290 }
1291 }
1292
1293 Ok(decoded)
1294 }
1295 }
1296
1297 const CRC24_INIT: u32 = 0xB704CE;
1298 const CRC24_POLY: u32 = 0x1864CFB;
1299
1300 struct CRC {
1301 n: u32,
1302 }
1303
1304 /// Computess the CRC-24, (see [RFC 4880, section 6.1]).
1305 ///
1306 /// [RFC 4880, section 6.1]: https://tools.ietf.org/html/rfc4880#section-6.1
1307 impl CRC {
new() -> Self1308 fn new() -> Self {
1309 CRC { n: CRC24_INIT }
1310 }
1311
update(&mut self, buf: &[u8]) -> &Self1312 fn update(&mut self, buf: &[u8]) -> &Self {
1313 for octet in buf {
1314 self.n ^= (*octet as u32) << 16;
1315 for _ in 0..8 {
1316 self.n <<= 1;
1317 if self.n & 0x1000000 > 0 {
1318 self.n ^= CRC24_POLY;
1319 }
1320 }
1321 }
1322 self
1323 }
1324
finalize(&self) -> u321325 fn finalize(&self) -> u32 {
1326 self.n & 0xFFFFFF
1327 }
1328 }
1329
1330 #[cfg(test)]
1331 mod test {
1332 use std::io::{Cursor, Read, Write};
1333 use super::CRC;
1334 use super::Kind;
1335 use super::Writer;
1336
1337 #[test]
crc()1338 fn crc() {
1339 let b = b"foobarbaz";
1340 let crcs = [
1341 0xb704ce,
1342 0x6d2804,
1343 0xa2d10d,
1344 0x4fc255,
1345 0x7aafca,
1346 0xc79c46,
1347 0x7334de,
1348 0x77dc72,
1349 0x000f65,
1350 0xf40d86,
1351 ];
1352
1353 for len in 0..b.len() + 1 {
1354 assert_eq!(CRC::new().update(&b[..len]).finalize(), crcs[len]);
1355 }
1356 }
1357
1358 macro_rules! t {
1359 ( $path: expr ) => {
1360 include_bytes!(concat!("../tests/data/armor/", $path))
1361 }
1362 }
1363 macro_rules! vectors {
1364 ( $prefix: expr, $suffix: expr ) => {
1365 &[t!(concat!($prefix, "-0", $suffix)),
1366 t!(concat!($prefix, "-1", $suffix)),
1367 t!(concat!($prefix, "-2", $suffix)),
1368 t!(concat!($prefix, "-3", $suffix)),
1369 t!(concat!($prefix, "-47", $suffix)),
1370 t!(concat!($prefix, "-48", $suffix)),
1371 t!(concat!($prefix, "-49", $suffix)),
1372 t!(concat!($prefix, "-50", $suffix)),
1373 t!(concat!($prefix, "-51", $suffix))]
1374 }
1375 }
1376
1377 const TEST_BIN: &[&[u8]] = vectors!("test", ".bin");
1378 const TEST_ASC: &[&[u8]] = vectors!("test", ".asc");
1379 const LITERAL_BIN: &[&[u8]] = vectors!("literal", ".bin");
1380 const LITERAL_ASC: &[&[u8]] = vectors!("literal", ".asc");
1381 const LITERAL_NO_HEADER_ASC: &[&[u8]] =
1382 vectors!("literal", "-no-header.asc");
1383 const LITERAL_NO_HEADER_WITH_CHKSUM_ASC: &[&[u8]] =
1384 vectors!("literal", "-no-header-with-chksum.asc");
1385 const LITERAL_NO_NEWLINES_ASC: &[&[u8]] =
1386 vectors!("literal", "-no-newlines.asc");
1387
1388 #[test]
enarmor()1389 fn enarmor() {
1390 for (bin, asc) in TEST_BIN.iter().zip(TEST_ASC.iter()) {
1391 let mut w =
1392 Writer::new(Vec::new(), Kind::File).unwrap();
1393 w.write(&[]).unwrap(); // Avoid zero-length optimization.
1394 w.write_all(bin).unwrap();
1395 let buf = w.finalize().unwrap();
1396 assert_eq!(String::from_utf8_lossy(&buf),
1397 String::from_utf8_lossy(asc));
1398 }
1399 }
1400
1401 #[test]
enarmor_bytewise()1402 fn enarmor_bytewise() {
1403 for (bin, asc) in TEST_BIN.iter().zip(TEST_ASC.iter()) {
1404 let mut w = Writer::new(Vec::new(), Kind::File).unwrap();
1405 w.write(&[]).unwrap(); // Avoid zero-length optimization.
1406 for b in bin.iter() {
1407 w.write(&[*b]).unwrap();
1408 }
1409 let buf = w.finalize().unwrap();
1410 assert_eq!(String::from_utf8_lossy(&buf),
1411 String::from_utf8_lossy(asc));
1412 }
1413 }
1414
1415 #[test]
drop_writer()1416 fn drop_writer() {
1417 // No ASCII frame shall be emitted if the writer is dropped
1418 // unused.
1419 assert!(Writer::new(Vec::new(), Kind::File).unwrap()
1420 .finalize().unwrap().is_empty());
1421
1422 // However, if the user insists, we will encode a zero-byte
1423 // string.
1424 let mut w = Writer::new(Vec::new(), Kind::File).unwrap();
1425 w.write(&[]).unwrap();
1426 let buf = w.finalize().unwrap();
1427 assert_eq!(
1428 &buf[..],
1429 &b"-----BEGIN PGP ARMORED FILE-----\n\
1430 \n\
1431 =twTO\n\
1432 -----END PGP ARMORED FILE-----\n"[..]);
1433 }
1434
1435 use super::{Reader, ReaderMode};
1436
1437 #[test]
dearmor_robust()1438 fn dearmor_robust() {
1439 for (i, reference) in LITERAL_BIN.iter().enumerate() {
1440 for test in &[LITERAL_ASC[i],
1441 LITERAL_NO_HEADER_WITH_CHKSUM_ASC[i],
1442 LITERAL_NO_HEADER_ASC[i],
1443 LITERAL_NO_NEWLINES_ASC[i]] {
1444 let mut r = Reader::new(Cursor::new(test),
1445 ReaderMode::VeryTolerant);
1446 let mut dearmored = Vec::<u8>::new();
1447 r.read_to_end(&mut dearmored).unwrap();
1448
1449 assert_eq!(&dearmored, reference);
1450 }
1451 }
1452 }
1453
1454 #[test]
dearmor_binary()1455 fn dearmor_binary() {
1456 for bin in TEST_BIN.iter() {
1457 let mut r = Reader::new(
1458 Cursor::new(bin), ReaderMode::Tolerant(Some(Kind::Message)));
1459 let mut buf = [0; 5];
1460 let e = r.read(&mut buf);
1461 assert!(e.is_err());
1462 }
1463 }
1464
1465 #[test]
dearmor_wrong_kind()1466 fn dearmor_wrong_kind() {
1467 let mut r = Reader::new(
1468 Cursor::new(&include_bytes!("../tests/data/armor/test-0.asc")[..]),
1469 ReaderMode::Tolerant(Some(Kind::Message)));
1470 let mut buf = [0; 5];
1471 let e = r.read(&mut buf);
1472 assert!(e.is_err());
1473 }
1474
1475 #[test]
dearmor_wrong_crc()1476 fn dearmor_wrong_crc() {
1477 let mut r = Reader::new(
1478 Cursor::new(
1479 &include_bytes!("../tests/data/armor/test-0.bad-crc.asc")[..]),
1480 ReaderMode::Tolerant(Some(Kind::File)));
1481 let mut buf = [0; 5];
1482 let e = r.read(&mut buf);
1483 assert!(e.is_err());
1484 }
1485
1486 #[test]
dearmor_wrong_footer()1487 fn dearmor_wrong_footer() {
1488 let mut r = Reader::new(
1489 Cursor::new(
1490 &include_bytes!("../tests/data/armor/test-2.bad-footer.asc")[..]
1491 ),
1492 ReaderMode::Tolerant(Some(Kind::File)));
1493 let mut read = 0;
1494 loop {
1495 let mut buf = [0; 5];
1496 match r.read(&mut buf) {
1497 Ok(0) => panic!("Reached EOF, but expected an error!"),
1498 Ok(r) => read += r,
1499 Err(_) => break,
1500 }
1501 }
1502 assert!(read <= 2);
1503 }
1504
1505 #[test]
dearmor_no_crc()1506 fn dearmor_no_crc() {
1507 let mut r = Reader::new(
1508 Cursor::new(
1509 &include_bytes!("../tests/data/armor/test-1.no-crc.asc")[..]),
1510 ReaderMode::Tolerant(Some(Kind::File)));
1511 let mut buf = [0; 5];
1512 let e = r.read(&mut buf);
1513 assert!(e.unwrap() == 1 && buf[0] == 0xde);
1514 }
1515
1516 #[test]
dearmor_with_header()1517 fn dearmor_with_header() {
1518 let mut r = Reader::new(
1519 Cursor::new(
1520 &include_bytes!("../tests/data/armor/test-3.with-headers.asc")[..]
1521 ),
1522 ReaderMode::Tolerant(Some(Kind::File)));
1523 assert_eq!(r.headers().unwrap(),
1524 &[("Comment".into(), "Some Header".into()),
1525 ("Comment".into(), "Another one".into())]);
1526 let mut buf = [0; 5];
1527 let e = r.read(&mut buf);
1528 assert!(e.is_ok());
1529 }
1530
1531 #[test]
dearmor_any()1532 fn dearmor_any() {
1533 let mut r = Reader::new(
1534 Cursor::new(
1535 &include_bytes!("../tests/data/armor/test-3.with-headers.asc")[..]
1536 ),
1537 ReaderMode::VeryTolerant);
1538 let mut buf = [0; 5];
1539 let e = r.read(&mut buf);
1540 assert!(r.kind() == Some(Kind::File));
1541 assert!(e.is_ok());
1542 }
1543
1544 #[test]
dearmor_with_garbage()1545 fn dearmor_with_garbage() {
1546 let armored =
1547 include_bytes!("../tests/data/armor/test-3.with-headers.asc");
1548 // Slap some garbage in front and make sure it still reads ok.
1549 let mut b: Vec<u8> = "Some\ngarbage\nlines\n\t\r ".into();
1550 b.extend_from_slice(armored);
1551 let mut r = Reader::new(Cursor::new(b), ReaderMode::VeryTolerant);
1552 let mut buf = [0; 5];
1553 let e = r.read(&mut buf);
1554 assert_eq!(r.kind(), Some(Kind::File));
1555 assert!(e.is_ok());
1556
1557 // Again, but this time add a non-whitespace character in the
1558 // line of the header.
1559 let mut b: Vec<u8> = "Some\ngarbage\nlines\n\t.\r ".into();
1560 b.extend_from_slice(armored);
1561 let mut r = Reader::new(Cursor::new(b), ReaderMode::VeryTolerant);
1562 let mut buf = [0; 5];
1563 let e = r.read(&mut buf);
1564 assert!(e.is_err());
1565 }
1566
1567 #[test]
dearmor()1568 fn dearmor() {
1569 for (bin, asc) in TEST_BIN.iter().zip(TEST_ASC.iter()) {
1570 let mut r = Reader::new(
1571 Cursor::new(asc),
1572 ReaderMode::Tolerant(Some(Kind::File)));
1573 let mut dearmored = Vec::<u8>::new();
1574 r.read_to_end(&mut dearmored).unwrap();
1575
1576 assert_eq!(&dearmored, bin);
1577 }
1578 }
1579
1580 #[test]
dearmor_bytewise()1581 fn dearmor_bytewise() {
1582 for (bin, asc) in TEST_BIN.iter().zip(TEST_ASC.iter()) {
1583 let r = Reader::new(
1584 Cursor::new(asc),
1585 ReaderMode::Tolerant(Some(Kind::File)));
1586 let mut dearmored = Vec::<u8>::new();
1587 for c in r.bytes() {
1588 dearmored.push(c.unwrap());
1589 }
1590
1591 assert_eq!(&dearmored, bin);
1592 }
1593 }
1594
1595 #[test]
dearmor_yuge()1596 fn dearmor_yuge() {
1597 let yuge_key = crate::tests::key("yuge-key-so-yuge-the-yugest.asc");
1598 let mut r = Reader::new(Cursor::new(&yuge_key[..]),
1599 ReaderMode::VeryTolerant);
1600 let mut dearmored = Vec::<u8>::new();
1601 r.read_to_end(&mut dearmored).unwrap();
1602
1603 let r = Reader::new(Cursor::new(&yuge_key[..]),
1604 ReaderMode::VeryTolerant);
1605 let mut dearmored = Vec::<u8>::new();
1606 for c in r.bytes() {
1607 dearmored.push(c.unwrap());
1608 }
1609 }
1610
1611 #[test]
dearmor_quoted()1612 fn dearmor_quoted() {
1613 let mut r = Reader::new(
1614 Cursor::new(
1615 &include_bytes!("../tests/data/armor/test-3.with-headers-quoted.asc")[..]
1616 ),
1617 ReaderMode::VeryTolerant);
1618 let mut buf = [0; 5];
1619 let e = r.read(&mut buf);
1620 assert!(r.kind() == Some(Kind::File));
1621 assert!(e.is_ok());
1622 }
1623
1624 #[test]
dearmor_quoted_a_lot()1625 fn dearmor_quoted_a_lot() {
1626 let mut r = Reader::new(
1627 Cursor::new(
1628 &include_bytes!("../tests/data/armor/test-3.with-headers-quoted-a-lot.asc")[..]
1629 ),
1630 ReaderMode::VeryTolerant);
1631 let mut buf = [0; 5];
1632 // Loop over the input to ensure we read and verify all the way to the
1633 // end of the input in order to check the checksum and footer validation
1634 loop {
1635 let e = r.read(&mut buf);
1636 assert!(r.kind() == Some(Kind::File));
1637 assert!(e.is_ok());
1638 if e.unwrap() == 0 {
1639 break;
1640 }
1641 }
1642 }
1643
1644 #[test]
dearmor_quoted_badly()1645 fn dearmor_quoted_badly() {
1646 let mut r = Reader::new(
1647 Cursor::new(
1648 &include_bytes!("../tests/data/armor/test-3.with-headers-quoted-badly.asc")[..]
1649 ),
1650 ReaderMode::VeryTolerant);
1651 let mut buf = [0; 5];
1652 let e = r.read(&mut buf);
1653 assert!(e.is_err());
1654 }
1655
1656 quickcheck! {
1657 fn roundtrip(kind: Kind, payload: Vec<u8>) -> bool {
1658 if payload.is_empty() {
1659 // Empty payloads do not emit an armor framing unless
1660 // one does an explicit empty write (and .write_all()
1661 // does not).
1662 return true;
1663 }
1664
1665 let mut w = Writer::new(Vec::new(), kind).unwrap();
1666 w.write_all(&payload).unwrap();
1667 let encoded = w.finalize().unwrap();
1668
1669 let mut recovered = Vec::new();
1670 Reader::new(Cursor::new(&encoded),
1671 ReaderMode::Tolerant(Some(kind)))
1672 .read_to_end(&mut recovered)
1673 .unwrap();
1674
1675 let mut recovered_any = Vec::new();
1676 Reader::new(Cursor::new(&encoded), ReaderMode::VeryTolerant)
1677 .read_to_end(&mut recovered_any)
1678 .unwrap();
1679
1680 payload == recovered && payload == recovered_any
1681 }
1682 }
1683
1684 /// Tests issue #404, zero-sized reads break reader.
1685 ///
1686 /// See: https://gitlab.com/sequoia-pgp/sequoia/-/issues/404
1687 #[test]
zero_sized_read()1688 fn zero_sized_read() {
1689 let mut r = Reader::from_bytes(crate::tests::file("armor/test-1.asc"),
1690 None);
1691 let mut buf = Vec::new();
1692 r.read(&mut buf).unwrap();
1693 r.read(&mut buf).unwrap();
1694 }
1695
1696 /// Crash in armor parser due to indexing not aligned with UTF-8
1697 /// characters.
1698 ///
1699 /// See: https://gitlab.com/sequoia-pgp/sequoia/-/issues/515
1700 #[test]
issue_515()1701 fn issue_515() {
1702 let data = [63, 9, 45, 10, 45, 10, 45, 45, 45, 45, 45, 66, 69,
1703 71, 73, 78, 32, 80, 71, 80, 32, 77, 69, 83, 83,
1704 65, 71, 69, 45, 45, 45, 45, 45, 45, 152, 152, 152,
1705 152, 152, 152, 255, 29, 152, 152, 152, 152, 152,
1706 152, 152, 152, 152, 152, 10, 91, 45, 10, 45, 14,
1707 0, 36, 0, 0, 30, 122, 4, 2, 204, 152];
1708
1709 let mut reader = Reader::from_bytes(&data[..], None);
1710 let mut buf = Vec::new();
1711 // `data` is malformed, expect an error.
1712 reader.read_to_end(&mut buf).unwrap_err();
1713 }
1714
1715 /// Crash in armor parser due to improper use of the buffered
1716 /// reader protocol when consuming quoting prefix.
1717 ///
1718 /// See: https://gitlab.com/sequoia-pgp/sequoia/-/issues/516
1719 #[test]
issue_516()1720 fn issue_516() {
1721 let data = [
1722 144, 32, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 125, 13, 125,
1723 125, 93, 125, 125, 93, 125, 13, 13, 125, 125, 45, 45, 45,
1724 45, 45, 66, 69, 71, 73, 78, 32, 80, 71, 80, 32, 77, 69,
1725 83, 83, 65, 71, 69, 45, 45, 45, 45, 45, 125, 13, 125,
1726 125, 93, 125, 125, 93, 125, 13, 13, 125, 125, 45, 0, 0,
1727 0, 0, 0, 0, 0, 0, 125, 205, 21, 1, 21, 21, 21, 1, 1, 1,
1728 1, 21, 149, 21, 21, 21, 21, 32, 4, 141, 141, 141, 141,
1729 202, 74, 11, 125, 8, 21, 50, 50, 194, 48, 147, 93, 174,
1730 23, 23, 23, 23, 23, 23, 147, 147, 147, 23, 23, 23, 23,
1731 23, 23, 48, 125, 125, 93, 125, 13, 125, 125, 125, 93,
1732 125, 125, 13, 13, 125, 125, 13, 13, 93, 125, 13, 125, 45,
1733 125, 125, 45, 45, 66, 69, 71, 73, 78, 32, 80, 71, 45, 45,
1734 125, 10, 45, 45, 0, 0, 10, 45, 45, 210, 10, 0, 0, 87, 0,
1735 0, 0, 150, 10, 0, 0, 241, 87, 45, 0, 0, 121, 121, 10, 10,
1736 21, 58];
1737 let mut reader = Reader::from_bytes(&data[..], None);
1738 let mut buf = Vec::new();
1739 // `data` is malformed, expect an error.
1740 reader.read_to_end(&mut buf).unwrap_err();
1741 }
1742
1743 /// Crash in armor parser due to improper use of the buffered
1744 /// reader protocol when consuming quoting prefix.
1745 ///
1746 /// See: https://gitlab.com/sequoia-pgp/sequoia/-/issues/517
1747 #[test]
issue_517()1748 fn issue_517() {
1749 let data = [13, 45, 45, 45, 45, 45, 66, 69, 71, 73, 78, 32, 80,
1750 71, 80, 32, 77, 69, 83, 83, 65, 71, 69, 45, 45, 45,
1751 45, 45, 10, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
1752 13, 13, 139];
1753 let mut reader = Reader::from_bytes(&data[..], None);
1754 let mut buf = Vec::new();
1755 // `data` is malformed, expect an error.
1756 reader.read_to_end(&mut buf).unwrap_err();
1757 }
1758 }
1759