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
23pub trait Convert<T> {
25 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#[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 pub fn display_sensitive(&self) -> SessionKeyDisplay {
73 SessionKeyDisplay { csk: self }
74 }
75}
76
77pub struct SessionKeyDisplay<'a> {
85 csk: &'a SessionKey,
86}
87
88impl<'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 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 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 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 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 let algo = s.symmetric_algo();
215 pp.decrypt(algo, &sk.session_key).ok().map(|_| algo)
216 },
217 _ => {
218 (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 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 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 "".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 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 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 }
656
657 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 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 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 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 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 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 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 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 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 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 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 - 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}