Skip to main content

sequoia_octopus_librnp/dump_packets/
dump.rs

1use std::io::{self, Read};
2
3use sequoia_openpgp as openpgp;
4use openpgp::armor::ReaderMode;
5use self::openpgp::types::SymmetricAlgorithm;
6use self::openpgp::fmt::hex;
7use self::openpgp::crypto::mpi;
8use self::openpgp::{Packet, Result};
9use self::openpgp::packet;
10use self::openpgp::packet::prelude::*;
11use self::openpgp::packet::header::CTB;
12use self::openpgp::packet::{Header, header::BodyLength, Signature};
13use self::openpgp::packet::signature::subpacket::{Subpacket, SubpacketValue};
14use self::openpgp::crypto::S2K;
15use self::openpgp::parse::{
16    Dearmor,
17    Parse,
18    PacketParserBuilder,
19    PacketParserResult,
20    map::Map,
21};
22
23/// Converts sequoia_openpgp types for rendering.
24pub trait Convert<T> {
25    /// Performs the conversion.
26    fn convert(self) -> T;
27}
28
29impl Convert<humantime::FormattedDuration> for std::time::Duration {
30    fn convert(self) -> humantime::FormattedDuration {
31        humantime::format_duration(self)
32    }
33}
34
35impl Convert<humantime::FormattedDuration> for openpgp::types::Duration {
36    fn convert(self) -> humantime::FormattedDuration {
37        humantime::format_duration(self.into())
38    }
39}
40
41impl Convert<chrono::DateTime<chrono::offset::Utc>> for std::time::SystemTime {
42    fn convert(self) -> chrono::DateTime<chrono::offset::Utc> {
43        chrono::DateTime::<chrono::offset::Utc>::from(self)
44    }
45}
46
47impl Convert<chrono::DateTime<chrono::offset::Utc>> for openpgp::types::Timestamp {
48    fn convert(self) -> chrono::DateTime<chrono::offset::Utc> {
49        std::time::SystemTime::from(self).convert()
50    }
51}
52
53/// Holds a session key as parsed from the command line, with an optional
54/// algorithm specifier.
55///
56/// This struct does not implement [`Display`] to prevent accidental leaking
57/// of key material. If you are sure you want to print a session key, use
58/// [`display_sensitive`].
59///
60/// [`Display`]: std::fmt::Display
61/// [`display_sensitive`]: SessionKey::display_sensitive
62#[derive(Debug, Clone)]
63pub struct SessionKey {
64    pub session_key: openpgp::crypto::SessionKey,
65    pub symmetric_algo: Option<SymmetricAlgorithm>,
66}
67
68
69impl SessionKey {
70    /// Returns an object that implements Display for explicitly opting into
71    /// printing a `SessionKey`.
72    pub fn display_sensitive(&self) -> SessionKeyDisplay {
73        SessionKeyDisplay { csk: self }
74    }
75}
76
77/// Helper struct for intentionally printing session keys with format! and {}.
78///
79/// This struct implements the `Display` trait to print the session key. This
80/// construct requires the user to explicitly call
81/// [`SessionKey::display_sensitive`]. By requiring the user to opt-in, this
82/// will hopefully reduce that the chance that the session key is inadvertently
83/// leaked, e.g., in a log that may be publicly posted.
84pub struct SessionKeyDisplay<'a> {
85    csk: &'a SessionKey,
86}
87
88/// Print the session key without prefix in hexadecimal representation.
89impl<'a> std::fmt::Display for SessionKeyDisplay<'a> {
90    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
91        let sk = self.csk;
92        write!(f, "{}", hex::encode(&sk.session_key))
93    }
94}
95
96#[derive(Debug)]
97pub enum Kind {
98    Message {
99        encrypted: bool,
100    },
101    Keyring,
102    Cert,
103    Unknown,
104}
105
106#[allow(clippy::redundant_pattern_matching)]
107pub fn dump<W>(input: &mut (dyn io::Read + Sync + Send),
108               output: &mut dyn io::Write,
109               max_decompressed_literal_data: Option<usize>,
110               mpis: bool, hex: bool,
111               sk: Option<&SessionKey>,
112               width: W)
113               -> Result<Kind>
114    where W: Into<Option<usize>>
115{
116    rnp_function!(dump, crate::TRACE);
117
118    // If no limit is supplied, stop after 100 MB.
119    let max_decompressed_literal_data
120        = max_decompressed_literal_data.unwrap_or(100 * 1024 * 1024);
121    let mut saw_decompression_packet = false;
122
123    let mut ppr
124        = self::openpgp::parse::PacketParserBuilder::from_reader(input)?;
125
126    // To produce hex dumps, we need to enable mapping, but also turn
127    // on buffering.  This makes sure that the map contains the whole
128    // packet content, even if it has not been parsed (such as when
129    // encountering unknown or junk pseudo packets).
130    if hex {
131        ppr = ppr.map(true).buffer_unread_content();
132    }
133
134    let mut ppr = ppr.build()?;
135
136    let width = width.into().unwrap_or(80);
137    let mut first_armor_block = true;
138    let mut is_keyring = true;
139
140  loop {
141    let mut dumper = PacketDumper::new(width, mpis);
142    let mut message_encrypted = false;
143    let mut pkesks = vec![];
144    let mut skesks = vec![];
145
146    while let PacketParserResult::Some(mut pp) = ppr {
147        let additional_fields = match pp.packet {
148            Packet::PKESK(ref p) => {
149                pkesks.push(p.clone());
150                vec![]
151            },
152            Packet::SKESK(ref p) => {
153                skesks.push(p.clone());
154                vec![]
155            },
156            Packet::CompressedData(_) => {
157                t!("Encountered compressed data packet.  \
158                    Activating zip bomb protection.");
159                saw_decompression_packet = true;
160                Vec::new()
161            }
162            Packet::Literal(_) => {
163                let mut prefix = vec![0; 40];
164                let n = pp.read(&mut prefix)?;
165                let summary = vec![
166                    format!("Content: {:?}{}",
167                            String::from_utf8_lossy(&prefix[..n]),
168                            if n == prefix.len() { "..." } else { "" }),
169                ];
170
171                if saw_decompression_packet {
172                    // Protect against a possible zip bomb.
173                    t!("Zip bomb protection activated.  Will abort after \
174                        reading more than {} bytes of literal data.",
175                       max_decompressed_literal_data);
176
177                    const BUFFER_SIZE: usize = 1024 * 1024;
178                    let mut buffer = vec![0; BUFFER_SIZE];
179                    let mut literal_data_read = prefix.len();
180                    while literal_data_read <= max_decompressed_literal_data {
181                        let remaining
182                            = max_decompressed_literal_data - literal_data_read + 1;
183
184                        let read = pp.read(
185                            &mut buffer[..remaining.min(BUFFER_SIZE)])?;
186                        if read == 0 {
187                            // EOF.
188                            break;
189                        }
190
191                        literal_data_read += read;
192                    }
193                    t!("Read {} bytes of literal data",
194                       literal_data_read);
195
196                    if literal_data_read > max_decompressed_literal_data {
197                        t!("Zip bomb detected");
198                        return Err(crate::Error::BadParameters.into());
199                    } else {
200                        t!("No zip bomb detected");
201                    }
202                }
203
204                summary
205            },
206            Packet::SEIP(ref s) => {
207		let version = s.version();
208                message_encrypted = true;
209
210                if let Some(sk) = &sk {
211                    let decrypted_with = match s {
212		        SEIP::V2(s) => {
213			    // No need for guessing.
214			    let algo = s.symmetric_algo();
215			    pp.decrypt(algo, &sk.session_key).ok().map(|_| algo)
216		        },
217		        _ => {
218			    // We don't know which algorithm to use,
219			    // try to find one that decrypts the message.
220			    (1u8..=19)
221                                .map(SymmetricAlgorithm::from)
222                                .find(|algo| pp.decrypt(*algo, &sk.session_key).is_ok())
223		        },
224                    };
225
226                    let mut fields = Vec::new();
227                    fields.push(format!("Session key: {}", hex::encode(&sk.session_key)));
228                    if let Some(algo) = decrypted_with {
229		        if version == 1 {
230			    // For v2, the packet already contains that
231			    // information.
232			    fields.push(format!("Symmetric algo: {}", algo));
233		        }
234                        fields.push("Decryption successful".into());
235                    } else {
236                        fields.push("Decryption failed".into());
237                    }
238                    fields
239                } else {
240                    vec!["No session key supplied".into()]
241                }
242            },
243            _ => Vec::new(),
244        };
245
246        let header = pp.header().clone();
247        let map = pp.take_map();
248
249        let recursion_depth = pp.recursion_depth();
250        let packet = pp.packet.clone();
251
252        dumper.packet(output, recursion_depth as usize,
253                      header, packet, map, additional_fields)?;
254
255        let (_, ppr_) = match pp.recurse() {
256            Ok(v) => Ok(v),
257            Err(e) => {
258                let _ = dumper.flush(output);
259                Err(e)
260            },
261        }?;
262        ppr = ppr_;
263    }
264
265    dumper.flush(output)?;
266
267    if let PacketParserResult::EOF(eof) = ppr {
268        let is_message = eof.is_message().is_ok() && first_armor_block;
269        let is_cert = eof.is_cert().is_ok() && first_armor_block;
270        is_keyring &= eof.is_keyring().is_ok();
271        first_armor_block = false;
272
273        // Now, the parser is exhausted, but we may find another
274        // armored blob.  Note that this can only happen if the first
275        // set of packets was also armored.
276        match PacketParserBuilder::from_buffered_reader(eof.into_reader())
277            .and_then(
278                |builder| builder
279                    .dearmor(Dearmor::Enabled(
280                        ReaderMode::Tolerant(None)))
281                    .build())
282        {
283            Ok(ppr_) => {
284                writeln!(output, "Note: There is another block of armored \
285                                  OpenPGP data.")?;
286
287                if is_message {
288                    writeln!(output, "Note: Data concatenated to a message is \
289                                      likely an error.")?;
290                } else if is_cert || is_keyring {
291                    writeln!(output, "Note: This is a non-standard extension \
292                                      to OpenPGP.")?;
293                }
294                writeln!(output)?;
295
296                ppr = ppr_;
297                continue;
298            },
299            Err(_) => break if is_message {
300                Ok(Kind::Message {
301                    encrypted: message_encrypted,
302                })
303            } else if is_cert {
304                Ok(Kind::Cert)
305            } else if is_keyring {
306                Ok(Kind::Keyring)
307            } else {
308                Ok(Kind::Unknown)
309            }
310        }
311    } else {
312        unreachable!()
313    }
314  }
315}
316
317struct Node {
318    header: Header,
319    packet: Packet,
320    map: Option<Map>,
321    additional_fields: Vec<String>,
322    children: Vec<Node>,
323}
324
325impl Node {
326    fn new(header: Header, packet: Packet, map: Option<Map>,
327           additional_fields: Vec<String>) -> Self {
328        Node {
329            header,
330            packet,
331            map,
332            additional_fields,
333            children: Vec::new(),
334        }
335    }
336
337    fn append(&mut self, depth: usize, node: Node) {
338        if depth == 0 {
339            self.children.push(node);
340        } else {
341            self.children.iter_mut().last().unwrap().append(depth - 1, node);
342        }
343    }
344}
345
346pub struct PacketDumper {
347    width: usize,
348    mpis: bool,
349    root: Option<Node>,
350}
351
352impl PacketDumper {
353    pub fn new(width: usize, mpis: bool) -> Self {
354        PacketDumper {
355            width,
356            mpis,
357            root: None,
358        }
359    }
360
361    pub fn packet(&mut self, output: &mut dyn io::Write, depth: usize,
362                  header: Header, p: Packet, map: Option<Map>,
363                  additional_fields: Vec<String>)
364                  -> Result<()> {
365        let node = Node::new(header, p, map, additional_fields);
366        if self.root.is_none() {
367            assert_eq!(depth, 0);
368            self.root = Some(node);
369        } else if depth == 0 {
370            let root = self.root.take().unwrap();
371            self.dump_tree(output, "", &root)?;
372            self.root = Some(node);
373        } else {
374            self.root.as_mut().unwrap().append(depth - 1, node);
375        }
376        Ok(())
377    }
378
379    pub fn flush(&self, output: &mut dyn io::Write) -> Result<()> {
380        if let Some(root) = self.root.as_ref() {
381            self.dump_tree(output, "", root)?;
382        }
383        Ok(())
384    }
385
386    fn dump_tree(&self, output: &mut dyn io::Write, indent: &str, node: &Node)
387                 -> Result<()> {
388        let indent_node =
389            format!("{}{} ", indent,
390                    if node.children.is_empty() { " " } else { "│" });
391        self.dump_packet(output, &indent_node, Some(&node.header), &node.packet,
392                         node.map.as_ref(), &node.additional_fields)?;
393        if node.children.is_empty() {
394            return Ok(());
395        }
396
397        let last = node.children.len() - 1;
398        for (i, child) in node.children.iter().enumerate() {
399            let is_last = i == last;
400            write!(output, "{}{}── ", indent,
401                   if is_last { "└" } else { "├" })?;
402            let indent_child =
403                format!("{}{}   ", indent,
404                        if is_last { " " } else { "│" });
405            self.dump_tree(output, &indent_child, child)?;
406        }
407        Ok(())
408    }
409
410    fn dump_packet(&self, mut output: &mut dyn io::Write, i: &str,
411                  header: Option<&Header>, p: &Packet, map: Option<&Map>,
412                  additional_fields: &Vec<String>)
413                  -> Result<()> {
414        use self::openpgp::Packet::*;
415
416        if let Some(tag) = p.kind() {
417            write!(output, "{}", tag)?;
418        } else {
419            write!(output, "Unknown or Unsupported Packet")?;
420        }
421
422        if let Some(h) = header {
423            write!(output, ", {} CTB, {}{}",
424                   if let CTB::Old(_) = h.ctb() { "old" } else { "new" },
425                   if let Some(map) = map {
426                       format!("{} header bytes + ",
427                               map.iter().take(2).map(|f| f.as_bytes().len())
428                                   .sum::<usize>())
429                   } else {
430                       // XXX: Mapping is disabled.  No can do for
431                       // now.  Once we save the header in
432                       // packet::Common, we can use this instead of
433                       // relying on the map.
434                       "".into()
435                   },
436                   match h.length() {
437                       BodyLength::Full(n) =>
438                           format!("{} bytes", n),
439                       BodyLength::Partial(n) =>
440                           format!("partial length, {} bytes in first chunk", n),
441                       BodyLength::Indeterminate =>
442                           "indeterminate length".into(),
443                   })?;
444        }
445        writeln!(output)?;
446
447        #[allow(deprecated)]
448        match p {
449            Unknown(ref u) => {
450                writeln!(output, "{}  Tag: {}", i, u.tag())?;
451                writeln!(output, "{}  Error: {}", i, u.error())?;
452            },
453
454            PublicKey(ref k) => self.dump_key(output, i, k)?,
455            PublicSubkey(ref k) => self.dump_key(output, i, k)?,
456            SecretKey(ref k) => self.dump_key(output, i, k)?,
457            SecretSubkey(ref k) => self.dump_key(output, i, k)?,
458
459            Signature(s) => self.dump_signature(output, i, s)?,
460
461            OnePassSig(ref o) => {
462                writeln!(output, "{}  Version: {}", i, o.version())?;
463                writeln!(output, "{}  Type: {}", i, o.typ())?;
464                writeln!(output, "{}  Pk algo: {}", i, o.pk_algo())?;
465                writeln!(output, "{}  Hash algo: {}", i, o.hash_algo())?;
466                writeln!(output, "{}  Issuer: {}", i, o.issuer())?;
467		if let packet::OnePassSig::V6(o) = o {
468                    writeln!(output, "{}  Salt: {}", i, hex::encode(o.salt()))?;
469		}
470                writeln!(output, "{}  Last: {}", i, o.last())?;
471            },
472
473            Trust(ref p) => {
474                writeln!(output, "{}  Value:", i)?;
475                let mut hd = hex::Dumper::new(
476                    &mut output,
477                    self.indentation_for_hexdump(&format!("{}  ", i), 16));
478                hd.write_ascii(p.value())?;
479            },
480
481            UserID(ref u) => {
482                writeln!(output, "{}  Value: {}", i,
483                         String::from_utf8_lossy(u.value()))?;
484            },
485
486            UserAttribute(ref u) => {
487                use self::openpgp::packet::user_attribute::{Subpacket, Image};
488                use openpgp::serialize::MarshalInto;
489
490                for subpacket in u.subpackets() {
491                    match subpacket {
492                        Ok(Subpacket::Image(image)) => match image {
493                            Image::JPEG(data) =>
494                                writeln!(output, "{}    JPEG: {} bytes", i,
495                                         data.len())?,
496                            Image::Private(n, data) =>
497                                writeln!(output,
498                                         "{}    Private image({}): {} bytes", i,
499                                         n, data.len())?,
500                            Image::Unknown(n, data) =>
501                                writeln!(output,
502                                         "{}    Unknown image({}): {} bytes", i,
503                                         n, data.len())?,
504                            _ =>
505                                writeln!(output,
506                                         "{}    Unknown image: {} bytes", i,
507                                         image.serialized_len())?,
508                        },
509                        Ok(Subpacket::Unknown(n, data)) =>
510                            writeln!(output,
511                                     "{}    Unknown subpacket({}): {} bytes", i,
512                                     n, data.len())?,
513                        Ok(u) =>
514                            writeln!(output,
515                                     "{}    Unknown subpacket: {} bytes", i,
516                                     u.serialized_len())?,
517                        Err(e) =>
518                            writeln!(output,
519                                     "{}    Invalid subpacket encoding: {}", i,
520                                     e)?,
521                    }
522                }
523            },
524
525            Marker(_) => {
526            },
527
528            Literal(ref l) => {
529                writeln!(output, "{}  Format: {}", i, l.format())?;
530                if let Some(filename) = l.filename() {
531                    writeln!(output, "{}  Filename: {:?}", i,
532                             String::from_utf8_lossy(filename))?;
533                }
534                if let Some(timestamp) = l.date() {
535                    writeln!(output, "{}  Timestamp: {}", i,
536                             timestamp.convert())?;
537                }
538            },
539
540            CompressedData(ref c) => {
541                writeln!(output, "{}  Algorithm: {}", i, c.algo())?;
542            },
543
544            PKESK(ref p) => {
545                writeln!(output, "{}  Version: {}", i, p.version())?;
546                writeln!(output, "{}  Recipient: {}", i,
547                         p.recipient().as_ref().map(ToString::to_string)
548                         .unwrap_or_else(|| "<anonymous recipient>".into()))?;
549                writeln!(output, "{}  Pk algo: {}", i, p.pk_algo())?;
550                if self.mpis {
551                    writeln!(output, "{}", i)?;
552                    writeln!(output, "{}  Encrypted session key:", i)?;
553
554                    let ii = format!("{}    ", i);
555                    match p.esk() {
556                        mpi::Ciphertext::X25519 { e, key } =>
557                            self.dump_mpis(output, &ii,
558                                           &[&e[..], key],
559                                           &["e", "key"])?,
560                        mpi::Ciphertext::X448 { e, key } =>
561                            self.dump_mpis(output, &ii,
562                                           &[&e[..], key],
563                                           &["e", "key"])?,
564                        mpi::Ciphertext::RSA { c } =>
565                            self.dump_mpis(output, &ii,
566                                           &[c.value()],
567                                           &["c"])?,
568                        mpi::Ciphertext::ElGamal { e, c } =>
569                            self.dump_mpis(output, &ii,
570                                           &[e.value(), c.value()],
571                                           &["e", "c"])?,
572                        mpi::Ciphertext::ECDH { e, key } =>
573                            self.dump_mpis(output, &ii,
574                                           &[e.value(), key],
575                                           &["e", "key"])?,
576                        mpi::Ciphertext::Unknown { mpis, rest } => {
577                            let keys: Vec<String> =
578                                (0..mpis.len()).map(
579                                    |i| format!("mpi{}", i)).collect();
580                            self.dump_mpis(
581                                output, &ii,
582                                &mpis.iter().map(|m| {
583                                    m.value().iter().as_slice()
584                                }).collect::<Vec<_>>()[..],
585                                &keys.iter().map(|k| k.as_str())
586                                    .collect::<Vec<_>>()[..],
587                            )?;
588
589                            self.dump_mpis(output, &ii, &[rest], &["rest"])?;
590                        },
591
592                        // crypto::mpi::Ciphertext is non-exhaustive.
593                        u => writeln!(output, "{}Unknown variant: {:?}", ii, u)?,
594                    }
595                }
596            },
597
598            SKESK(ref s) => {
599                writeln!(output, "{}  Version: {}", i, s.version())?;
600                match s {
601                    self::openpgp::packet::SKESK::V4(ref s) => {
602                        writeln!(output, "{}  Symmetric algo: {}", i,
603                                 s.symmetric_algo())?;
604                        write!(output, "{}  S2K: ", i)?;
605                        self.dump_s2k(output, i, s.s2k())?;
606                        if let Ok(Some(esk)) = s.esk() {
607                            writeln!(output, "{}  ESK: {}", i,
608                                     hex::encode(esk))?;
609                        }
610                    },
611
612                    self::openpgp::packet::SKESK::V6(ref s) => {
613                        writeln!(output, "{}  Symmetric algo: {}", i,
614                                 s.symmetric_algo())?;
615                        writeln!(output, "{}  AEAD: {}", i,
616                                 s.aead_algo())?;
617                        write!(output, "{}  S2K: ", i)?;
618                        self.dump_s2k(output, i, s.s2k())?;
619                        writeln!(output, "{}  IV: {}", i,
620                                 hex::encode(s.aead_iv()))?;
621                        writeln!(output, "{}  ESK: {}", i,
622                                 hex::encode(s.esk()))?;
623                    },
624
625                    // SKESK is non-exhaustive.
626                    u => writeln!(output, "{}    Unknown variant: {:?}", i, u)?,
627                }
628            },
629
630            SEIP(ref s) => {
631                writeln!(output, "{}  Version: {}", i, s.version())?;
632                match s {
633                    packet::SEIP::V1(_) => (),
634                    packet::SEIP::V2(s) => {
635                        writeln!(output, "{}  Symmetric algo: {}", i, s.symmetric_algo())?;
636                        writeln!(output, "{}  AEAD algo: {}", i, s.aead())?;
637                        writeln!(output, "{}  Chunk size: {}", i, s.chunk_size())?;
638                        writeln!(output, "{}  Salt: {}", i, hex::encode(s.salt()))?;
639                    },
640                    _ => (),
641                }
642            },
643
644            MDC(ref m) => {
645                writeln!(output, "{}  Digest: {}",
646                         i, hex::encode(m.digest()))?;
647                writeln!(output, "{}  Computed digest: {}",
648                         i, hex::encode(m.computed_digest()))?;
649                writeln!(output, "{}  Valid: {}",
650                         i, m.valid())?;
651            },
652
653            Padding(_) => {
654                // Nothing to do.
655            }
656
657            // openpgp::Packet is non-exhaustive.
658            u => writeln!(output, "{}    Unknown variant: {:?}", i, u)?,
659        }
660
661        for field in additional_fields {
662            writeln!(output, "{}  {}", i, field)?;
663        }
664
665        writeln!(output, "{}", i)?;
666
667        if let Some(map) = map {
668            if map.iter().next().is_none() {
669                // There is no data, we cannot dump anything.
670                return Ok(());
671            }
672
673            let mut hd = hex::Dumper::new(output, self.indentation_for_hexdump(
674                i, map.iter()
675                    .map(|f| if f.name() == "body" { 16 } else { f.name().len() })
676                    .max()
677                    .expect("we checked that there is one entry")));
678
679            for field in map.iter() {
680                if field.name() == "body" {
681                    hd.write_ascii(field.as_bytes())?;
682                } else {
683                    hd.write(field.as_bytes(), field.name())?;
684                }
685            }
686
687            let output = hd.into_inner();
688            writeln!(output, "{}", i)?;
689        }
690
691        Ok(())
692    }
693
694    /// Dumps the given key packet.
695    fn dump_key<P, R>(&self, output: &mut dyn io::Write, i: &str,
696                      k: &Key<P, R>)
697                      -> Result<()>
698    where
699        P: key::KeyParts,
700        R: key::KeyRole,
701    {
702        self.dump_key_internal(
703            output, i, k.parts_as_unspecified().role_as_unspecified())
704    }
705
706    /// Dumps the given key packet, prevents monomorphization of this
707    /// big function.
708    fn dump_key_internal(&self, output: &mut dyn io::Write, i: &str,
709                         k: &Key<key::UnspecifiedParts, key::UnspecifiedRole>)
710                         -> Result<()>
711    {
712        writeln!(output, "{}  Version: {}", i, k.version())?;
713        writeln!(output, "{}  Creation time: {}", i,
714                 k.creation_time().convert())?;
715        writeln!(output, "{}  Pk algo: {}", i, k.pk_algo())?;
716        if let Some(bits) = k.mpis().bits() {
717            writeln!(output, "{}  Pk size: {} bits", i, bits)?;
718        }
719        writeln!(output, "{}  Fingerprint: {}", i, k.fingerprint())?;
720        writeln!(output, "{}  KeyID: {}", i, k.keyid())?;
721        if self.mpis {
722            writeln!(output, "{}", i)?;
723            writeln!(output, "{}  Public Key:", i)?;
724
725            let ii = format!("{}    ", i);
726            match k.mpis() {
727                mpi::PublicKey::X25519 { u } =>
728                    self.dump_mpis(output, &ii, &[u], &["u"])?,
729                mpi::PublicKey::X448 { u } =>
730                    self.dump_mpis(output, &ii, &[&u[..]], &["u"])?,
731                mpi::PublicKey::Ed25519 { a } =>
732                    self.dump_mpis(output, &ii, &[a], &["a"])?,
733                mpi::PublicKey::Ed448 { a } =>
734                    self.dump_mpis(output, &ii, &[&a[..]], &["a"])?,
735                mpi::PublicKey::RSA { e, n } =>
736                    self.dump_mpis(output, &ii,
737                                 &[e.value(), n.value()],
738                                 &["e", "n"])?,
739                mpi::PublicKey::DSA { p, q, g, y } =>
740                    self.dump_mpis(output, &ii,
741                                 &[p.value(), q.value(), g.value(),
742                                   y.value()],
743                                 &["p", "q", "g", "y"])?,
744                mpi::PublicKey::ElGamal { p, g, y } =>
745                    self.dump_mpis(output, &ii,
746                                 &[p.value(), g.value(), y.value()],
747                                 &["p", "g", "y"])?,
748                mpi::PublicKey::EdDSA { curve, q } => {
749                    writeln!(output, "{}  Curve: {}", ii, curve)?;
750                    self.dump_mpis(output, &ii, &[q.value()], &["q"])?;
751                },
752                mpi::PublicKey::ECDSA { curve, q } => {
753                    writeln!(output, "{}  Curve: {}", ii, curve)?;
754                    self.dump_mpis(output, &ii, &[q.value()], &["q"])?;
755                },
756                mpi::PublicKey::ECDH { curve, q, hash, sym } => {
757                    writeln!(output, "{}  Curve: {}", ii, curve)?;
758                    writeln!(output, "{}  KDF hash algo: {}", ii, hash)?;
759                    writeln!(output, "{}  KEK symmetric algo: {}", ii,
760                             sym)?;
761                    self.dump_mpis(output, &ii, &[q.value()], &["q"])?;
762                },
763                mpi::PublicKey::Unknown { mpis, rest } => {
764                    let keys: Vec<String> =
765                        (0..mpis.len()).map(
766                            |i| format!("mpi{}", i)).collect();
767                    self.dump_mpis(
768                        output, &ii,
769                        &mpis.iter().map(|m| {
770                            m.value().iter().as_slice()
771                        }).collect::<Vec<_>>()[..],
772                        &keys.iter().map(|k| k.as_str())
773                            .collect::<Vec<_>>()[..],
774                    )?;
775
776                    self.dump_mpis(output, &ii, &[&rest[..]], &["rest"])?;
777                },
778
779                // crypto::mpi:Publickey is non-exhaustive
780                u => writeln!(output, "{}Unknown variant: {:?}", ii, u)?,
781            }
782        }
783
784        if let Some(secrets) = k.optional_secret() {
785            writeln!(output, "{}", i)?;
786            writeln!(output, "{}  Secret Key:", i)?;
787
788            let ii = format!("{}    ", i);
789            match secrets {
790                SecretKeyMaterial::Unencrypted(ref u) => {
791                    writeln!(output, "{}", i)?;
792                    writeln!(output, "{}  Unencrypted", ii)?;
793                    if self.mpis {
794                        u.map(|mpis| -> Result<()> {
795                            match mpis
796                            {
797                                mpi::SecretKeyMaterial::X25519 { x } =>
798                                    self.dump_mpis(output, &ii,
799                                                   &[x], &["x"])?,
800                                mpi::SecretKeyMaterial::X448 { x } =>
801                                    self.dump_mpis(output, &ii,
802                                                   &[&x[..]], &["x"])?,
803                                mpi::SecretKeyMaterial::Ed25519 { x } =>
804                                    self.dump_mpis(output, &ii,
805                                                   &[x], &["x"])?,
806                                mpi::SecretKeyMaterial::Ed448 { x } =>
807                                    self.dump_mpis(output, &ii,
808                                                   &[&x[..]], &["x"])?,
809                                mpi::SecretKeyMaterial::RSA { d, p, q, u } =>
810                                    self.dump_mpis(output, &ii,
811                                                 &[d.value(), p.value(),
812                                                   q.value(), u.value()],
813                                                 &["d", "p", "q", "u"])?,
814                                mpi::SecretKeyMaterial::DSA { x } =>
815                                    self.dump_mpis(output, &ii, &[x.value()],
816                                                 &["x"])?,
817                                mpi::SecretKeyMaterial::ElGamal { x } =>
818                                    self.dump_mpis(output, &ii, &[x.value()],
819                                                 &["x"])?,
820                                mpi::SecretKeyMaterial::EdDSA { scalar } =>
821                                    self.dump_mpis(output, &ii,
822                                                 &[scalar.value()],
823                                                 &["scalar"])?,
824                                mpi::SecretKeyMaterial::ECDSA { scalar } =>
825                                    self.dump_mpis(output, &ii,
826                                                 &[scalar.value()],
827                                                 &["scalar"])?,
828                                mpi::SecretKeyMaterial::ECDH { scalar } =>
829                                    self.dump_mpis(output, &ii,
830                                                 &[scalar.value()],
831                                                 &["scalar"])?,
832                                mpi::SecretKeyMaterial::Unknown { mpis, rest } => {
833                                    let keys: Vec<String> =
834                                        (0..mpis.len()).map(
835                                            |i| format!("mpi{}", i)).collect();
836                                    self.dump_mpis(
837                                        output, &ii,
838                                        &mpis.iter().map(|m| {
839                                            m.value().iter().as_slice()
840                                        }).collect::<Vec<_>>()[..],
841                                        &keys.iter().map(|k| k.as_str())
842                                            .collect::<Vec<_>>()[..],
843                                    )?;
844
845                                    self.dump_mpis(output, &ii, &[rest],
846                                                 &["rest"])?;
847                                },
848
849                                // crypto::mpi::SecretKeyMaterial is non-exhaustive.
850                                u => writeln!(output, "{}Unknown variant: {:?}", ii, u)?,
851                            }
852                            Ok(())
853                        })?;
854                    }
855                }
856                SecretKeyMaterial::Encrypted(ref e) => {
857                    writeln!(output, "{}", i)?;
858                    writeln!(output, "{}  Encrypted", ii)?;
859                    write!(output, "{}  S2K: ", ii)?;
860                    self.dump_s2k(output, &ii, e.s2k())?;
861                    writeln!(output, "{}  Sym. algo: {}", ii,
862                             e.algo())?;
863                    if self.mpis {
864                        if let Ok(ciphertext) = e.ciphertext() {
865                            self.dump_mpis(output, &ii, &[ciphertext],
866                                         &["ciphertext"])?;
867                        }
868                    }
869                },
870            }
871        }
872
873        Ok(())
874    }
875
876    pub fn dump_signature(&self, output: &mut dyn io::Write, i: &str,
877                          s: &Signature)
878                          -> Result<()>
879    {
880        writeln!(output, "{}  Version: {}", i, s.version())?;
881        writeln!(output, "{}  Type: {}", i, s.typ())?;
882        writeln!(output, "{}  Pk algo: {}", i, s.pk_algo())?;
883        writeln!(output, "{}  Hash algo: {}", i, s.hash_algo())?;
884        if s.hashed_area().iter().count() > 0 {
885            writeln!(output, "{}  Hashed area:", i)?;
886            for pkt in s.hashed_area().iter() {
887                self.dump_subpacket(output, i, pkt, s)?;
888            }
889        }
890        if s.unhashed_area().iter().count() > 0 {
891            writeln!(output, "{}  Unhashed area:", i)?;
892            for pkt in s.unhashed_area().iter() {
893                self.dump_subpacket(output, i, pkt, s)?;
894            }
895        }
896        writeln!(output, "{}  Digest prefix: {}", i,
897                 hex::encode(s.digest_prefix()))?;
898	if let packet::Signature::V6(s) = s {
899            writeln!(output, "{}  Salt: {}", i, hex::encode(s.salt()))?;
900	}
901        write!(output, "{}  Level: {} ", i, s.level())?;
902        match s.level() {
903            0 => writeln!(output, "(signature over data)")?,
904            1 => writeln!(output, "(notarization over signatures \
905                                   level 0 and data)")?,
906            n => writeln!(output, "(notarization over signatures \
907                                   level <= {} and data)", n - 1)?,
908        }
909        if self.mpis {
910            writeln!(output, "{}", i)?;
911            writeln!(output, "{}  Signature:", i)?;
912
913            let ii = format!("{}    ", i);
914            match s.mpis() {
915                mpi::Signature::Ed25519 { s } =>
916                    self.dump_mpis(output, &ii, &[&s[..]], &["s"])?,
917                mpi::Signature::Ed448 { s } =>
918                    self.dump_mpis(output, &ii, &[&s[..]], &["s"])?,
919                mpi::Signature::RSA { s } =>
920                    self.dump_mpis(output, &ii,
921                                   &[s.value()],
922                                   &["s"])?,
923                mpi::Signature::DSA { r, s } =>
924                    self.dump_mpis(output, &ii,
925                                   &[r.value(), s.value()],
926                                   &["r", "s"])?,
927                mpi::Signature::ElGamal { r, s } =>
928                    self.dump_mpis(output, &ii,
929                                   &[r.value(), s.value()],
930                                   &["r", "s"])?,
931                mpi::Signature::EdDSA { r, s } =>
932                    self.dump_mpis(output, &ii,
933                                   &[r.value(), s.value()],
934                                   &["r", "s"])?,
935                mpi::Signature::ECDSA { r, s } =>
936                    self.dump_mpis(output, &ii,
937                                   &[r.value(), s.value()],
938                                   &["r", "s"])?,
939                mpi::Signature::Unknown { mpis, rest } => {
940                    let keys: Vec<String> =
941                        (0..mpis.len()).map(
942                            |i| format!("mpi{}", i)).collect();
943                    self.dump_mpis(
944                        output, &ii,
945                        &mpis.iter().map(|m| {
946                            m.value().iter().as_slice()
947                        }).collect::<Vec<_>>()[..],
948                        &keys.iter().map(|k| k.as_str())
949                            .collect::<Vec<_>>()[..],
950                    )?;
951
952                    self.dump_mpis(output, &ii, &[&rest[..]], &["rest"])?;
953                },
954
955                // crypto::mpi::Signature is non-exhaustive.
956                u => writeln!(output, "{}Unknown variant: {:?}", ii, u)?,
957            }
958        }
959
960        Ok(())
961    }
962
963    fn dump_subpacket(&self, output: &mut dyn io::Write, i: &str,
964                      s: &Subpacket, sig: &Signature)
965                      -> Result<()> {
966        use self::SubpacketValue::*;
967
968        let hexdump_unknown = |output: &mut dyn io::Write, buf| -> Result<()> {
969            let mut hd = hex::Dumper::new(output, self.indentation_for_hexdump(
970                &format!("{}    ", i), 0));
971            hd.write_labeled(buf, |_, _| None)?;
972            Ok(())
973        };
974
975        #[allow(deprecated)]
976        match s.value() {
977            Unknown { body, .. } => {
978                writeln!(output, "{}    {:?}{}:", i, s.tag(),
979                         if s.critical() { " (critical)" } else { "" })?;
980                hexdump_unknown(output, body.as_slice())?;
981            },
982            SignatureCreationTime(t) =>
983                write!(output, "{}    Signature creation time: {}", i,
984                       (*t).convert())?,
985            SignatureExpirationTime(t) =>
986                write!(output, "{}    Signature expiration time: {} ({})",
987                       i, t.convert(),
988                       if let Some(creation) = sig.signature_creation_time() {
989                           (creation + (*t).into()).convert().to_string()
990                       } else {
991                           " (no Signature Creation Time subpacket)".into()
992                       })?,
993            ExportableCertification(e) =>
994                write!(output, "{}    Exportable certification: {}", i, e)?,
995            TrustSignature{level, trust} =>
996                write!(output, "{}    Trust signature: level {} trust {}", i,
997                       level, trust)?,
998            RegularExpression(ref r) =>
999                write!(output, "{}    Regular expression: {}", i,
1000                       String::from_utf8_lossy(r))?,
1001            Revocable(r) =>
1002                write!(output, "{}    Revocable: {}", i, r)?,
1003            KeyExpirationTime(t) =>
1004                write!(output, "{}    Key expiration time: {}", i,
1005                       t.convert())?,
1006            PreferredSymmetricAlgorithms(ref c) =>
1007                write!(output, "{}    Symmetric algo preferences: {}", i,
1008                       c.iter().map(|c| format!("{:?}", c))
1009                       .collect::<Vec<String>>().join(", "))?,
1010            RevocationKey(rk) => {
1011                let (pk_algo, fp) = rk.revoker();
1012                write!(output,
1013                       "{}    Revocation key: {}/{}", i,
1014                       fp, pk_algo)?;
1015                if rk.sensitive() {
1016                    write!(output, ", sensitive")?;
1017                }
1018            },
1019            Issuer(ref is) =>
1020                write!(output, "{}    Issuer: {}", i, is)?,
1021            NotationData(n) => if n.flags().human_readable() {
1022                write!(output, "{}    Notation: {}", i, n)?;
1023                if s.critical() {
1024                    write!(output, " (critical)")?;
1025                }
1026                writeln!(output)?;
1027            } else {
1028                write!(output, "{}    Notation: {}", i, n.name())?;
1029                let flags = format!("{:?}", n.flags());
1030                if ! flags.is_empty() {
1031                    write!(output, "{}", flags)?;
1032                }
1033                if s.critical() {
1034                    write!(output, " (critical)")?;
1035                }
1036                writeln!(output)?;
1037                hexdump_unknown(output, n.value())?;
1038            },
1039            PreferredHashAlgorithms(ref h) =>
1040                write!(output, "{}    Hash preferences: {}", i,
1041                       h.iter().map(|h| format!("{:?}", h))
1042                       .collect::<Vec<String>>().join(", "))?,
1043            PreferredCompressionAlgorithms(ref c) =>
1044                write!(output, "{}    Compression preferences: {}", i,
1045                       c.iter().map(|c| format!("{:?}", c))
1046                       .collect::<Vec<String>>().join(", "))?,
1047            KeyServerPreferences(ref p) =>
1048                write!(output, "{}    Keyserver preferences: {:?}", i, p)?,
1049            PreferredKeyServer(ref k) =>
1050                write!(output, "{}    Preferred keyserver: {}", i,
1051                       String::from_utf8_lossy(k))?,
1052            PrimaryUserID(p) =>
1053                write!(output, "{}    Primary User ID: {}", i, p)?,
1054            PolicyURI(ref p) =>
1055                write!(output, "{}    Policy URI: {}", i, String::from_utf8_lossy(p))?,
1056            KeyFlags(ref k) =>
1057                write!(output, "{}    Key flags: {:?}", i, k)?,
1058            SignersUserID(ref u) =>
1059                write!(output, "{}    Signer's User ID: {}", i,
1060                       String::from_utf8_lossy(u))?,
1061            ReasonForRevocation{code, ref reason} => {
1062                write!(output, "{}    Reason for revocation: {}{}{}", i, code,
1063                       if reason.len() > 0 { ", " } else { "" },
1064                       String::from_utf8_lossy(reason))?
1065            }
1066            Features(ref f) =>
1067                write!(output, "{}    Features: {:?}", i, f)?,
1068            SignatureTarget{pk_algo, hash_algo, ref digest} =>
1069                write!(output, "{}    Signature target: {}, {}, {}", i,
1070                       pk_algo, hash_algo, hex::encode(digest))?,
1071            EmbeddedSignature(_) =>
1072            // Embedded signature is dumped below.
1073                write!(output, "{}    Embedded signature: ", i)?,
1074            IssuerFingerprint(ref fp) =>
1075                write!(output, "{}    Issuer Fingerprint: {}", i, fp)?,
1076
1077            IntendedRecipient(ref fp) =>
1078                write!(output, "{}    Intended Recipient: {}", i, fp)?,
1079            ApprovedCertifications(digests) => {
1080                write!(output, "{}    Approved Certifications:", i)?;
1081                if digests.is_empty() {
1082                    writeln!(output, " None")?;
1083                } else {
1084                    writeln!(output)?;
1085                    for d in digests {
1086                        writeln!(output, "{}      {}", i, hex::encode(d))?;
1087                    }
1088                }
1089            },
1090            PreferredAEADCiphersuites(p) =>
1091                write!(output, "{}    AEAD preferences: {}", i,
1092                       p.iter().map(|(symm, aead)|
1093                                    format!("{:?}+{:?}", symm, aead))
1094                       .collect::<Vec<String>>().join(", "))?,
1095
1096            // SubpacketValue is non-exhaustive.
1097            u => writeln!(output, "{}    Unknown variant: {:?}", i, u)?,
1098        }
1099
1100        match s.value() {
1101            Unknown { .. } => (),
1102            NotationData { .. } => (),
1103            EmbeddedSignature(ref sig) => {
1104                if s.critical() {
1105                    write!(output, " (critical)")?;
1106                }
1107                writeln!(output)?;
1108                let indent = format!("{}      ", i);
1109                write!(output, "{}", indent)?;
1110                self.dump_signature(output, &indent, sig)?;
1111            },
1112            _ => {
1113                if s.critical() {
1114                    write!(output, " (critical)")?;
1115                }
1116                writeln!(output)?;
1117            }
1118        }
1119
1120        Ok(())
1121    }
1122
1123    fn dump_s2k(&self, output: &mut dyn io::Write, i: &str, s2k: &S2K)
1124                -> Result<()> {
1125        use self::S2K::*;
1126        #[allow(deprecated)]
1127        match s2k {
1128            Implicit => {
1129                writeln!(output, "Implicit")?;
1130            },
1131            Simple { hash } => {
1132                writeln!(output, "Simple")?;
1133                writeln!(output, "{}    Hash: {}", i, hash)?;
1134            },
1135            Salted { hash, ref salt } => {
1136                writeln!(output, "Salted")?;
1137                writeln!(output, "{}    Hash: {}", i, hash)?;
1138                writeln!(output, "{}    Salt: {}", i, hex::encode(salt))?;
1139            },
1140            Iterated { hash, ref salt, hash_bytes } => {
1141                writeln!(output, "Iterated")?;
1142                writeln!(output, "{}    Hash: {}", i, hash)?;
1143                writeln!(output, "{}    Salt: {}", i, hex::encode(salt))?;
1144                writeln!(output, "{}    Hash bytes: {}", i, hash_bytes)?;
1145            },
1146            Argon2 { salt, t, p, m } => {
1147                writeln!(output, "Argon2")?;
1148                writeln!(output, "{}    Salt: {}", i, hex::encode(salt))?;
1149                writeln!(output, "{}    Passes: {}", i, t)?;
1150                writeln!(output, "{}    Parallelism: {}", i, p)?;
1151                writeln!(output, "{}    Memory: {} ({} KiB)", i, m,
1152                         2usize.pow((*m).into()))?;
1153            },
1154            Private { tag, parameters } => {
1155                writeln!(output, "Private")?;
1156                writeln!(output, "{}    Tag: {}", i, tag)?;
1157                if let Some(p) = parameters.as_ref() {
1158                    writeln!(output, "{}    Parameters: {:?}", i, p)?;
1159                }
1160            },
1161            Unknown { tag, parameters } => {
1162                writeln!(output, "Unknown")?;
1163                writeln!(output, "{}    Tag: {}", i, tag)?;
1164                if let Some(p) = parameters.as_ref() {
1165                    writeln!(output, "{}    Parameters: {:?}", i, p)?;
1166                }
1167            },
1168
1169            // S2K is non-exhaustive
1170            u => writeln!(output, "{}    Unknown variant: {:?}", i, u)?,
1171        }
1172        Ok(())
1173    }
1174
1175    fn dump_mpis(&self, output: &mut dyn io::Write, i: &str,
1176                 chunks: &[&[u8]], keys: &[&str]) -> Result<()> {
1177        assert_eq!(chunks.len(), keys.len());
1178        if chunks.is_empty() {
1179            return Ok(());
1180        }
1181
1182        let max_key_len = keys.iter().map(|k| k.len()).max().unwrap();
1183
1184        for (chunk, key) in chunks.iter().zip(keys.iter()) {
1185            writeln!(output, "{}", i)?;
1186            let mut hd = hex::Dumper::new(
1187                Vec::new(), self.indentation_for_hexdump(i, max_key_len));
1188            hd.write(*chunk, *key)?;
1189            output.write_all(&hd.into_inner())?;
1190        }
1191
1192        Ok(())
1193    }
1194
1195    /// Returns indentation for hex dumps.
1196    ///
1197    /// Returns a prefix of `i` so that a hexdump with labels no
1198    /// longer than `max_label_len` will fit into the target width.
1199    fn indentation_for_hexdump(&self, i: &str, max_label_len: usize) -> String {
1200        let amount = ::std::cmp::max(
1201            0,
1202            ::std::cmp::min(
1203                self.width as isize
1204                    - 63 // Length of address, hex digits, and whitespace.
1205                    - max_label_len as isize,
1206                i.len() as isize),
1207        ) as usize;
1208
1209        format!("{}  ", &i.chars().take(amount).collect::<String>())
1210    }
1211
1212
1213}