diff options
Diffstat (limited to 'tool/src/commands')
-rw-r--r-- | tool/src/commands/decrypt.rs | 354 | ||||
-rw-r--r-- | tool/src/commands/dump.rs | 946 | ||||
-rw-r--r-- | tool/src/commands/inspect.rs | 412 | ||||
-rw-r--r-- | tool/src/commands/key.rs | 248 | ||||
-rw-r--r-- | tool/src/commands/mod.rs | 515 | ||||
-rw-r--r-- | tool/src/commands/sign.rs | 347 |
6 files changed, 0 insertions, 2822 deletions
diff --git a/tool/src/commands/decrypt.rs b/tool/src/commands/decrypt.rs deleted file mode 100644 index 07adabe6..00000000 --- a/tool/src/commands/decrypt.rs +++ /dev/null @@ -1,354 +0,0 @@ -use crossterm::terminal; -use anyhow::Context as _; -use std::collections::HashMap; -use std::io; -use rpassword; - -use sequoia_openpgp as openpgp; -use sequoia_core::Context; -use crate::openpgp::types::SymmetricAlgorithm; -use crate::openpgp::fmt::hex; -use crate::openpgp::crypto::{self, SessionKey}; -use crate::openpgp::{Fingerprint, Cert, KeyID, Result}; -use crate::openpgp::packet; -use crate::openpgp::packet::prelude::*; -use crate::openpgp::parse::{ - Parse, - PacketParser, - PacketParserResult, -}; -use crate::openpgp::parse::stream::{ - VerificationHelper, DecryptionHelper, DecryptorBuilder, MessageStructure, -}; -use crate::openpgp::policy::Policy; -use sequoia_store as store; - -use super::{dump::PacketDumper, VHelper}; - -struct Helper<'a> { - vhelper: VHelper<'a>, - secret_keys: - HashMap<KeyID, Key<key::SecretParts, key::UnspecifiedRole>>, - key_identities: HashMap<KeyID, Fingerprint>, - key_hints: HashMap<KeyID, String>, - dump_session_key: bool, - dumper: Option<PacketDumper>, -} - -impl<'a> Helper<'a> { - fn new(ctx: &'a Context, policy: &'a dyn Policy, - mapping: &'a mut store::Mapping, - signatures: usize, certs: Vec<Cert>, secrets: Vec<Cert>, - dump_session_key: bool, dump: bool) - -> Self - { - let mut keys = HashMap::new(); - let mut identities: HashMap<KeyID, Fingerprint> = HashMap::new(); - let mut hints: HashMap<KeyID, String> = HashMap::new(); - for tsk in secrets { - let hint = match tsk.with_policy(policy, None) - .and_then(|valid_cert| valid_cert.primary_userid()).ok() - { - Some(uid) => format!("{} ({})", uid.userid(), - KeyID::from(tsk.fingerprint())), - None => format!("{}", KeyID::from(tsk.fingerprint())), - }; - - for ka in tsk.keys() - // XXX: Should use the message's creation time that we do not know. - .with_policy(policy, None) - .for_transport_encryption().for_storage_encryption() - .secret() - { - let id: KeyID = ka.key().fingerprint().into(); - keys.insert(id.clone(), ka.key().clone().into()); - identities.insert(id.clone(), tsk.fingerprint()); - hints.insert(id, hint.clone()); - } - } - - Helper { - vhelper: VHelper::new(ctx, mapping, signatures, certs), - secret_keys: keys, - key_identities: identities, - key_hints: hints, - dump_session_key: dump_session_key, - dumper: if dump { - let width = terminal::size().ok().map(|(cols, _)| cols as usize) - .unwrap_or(80); - Some(PacketDumper::new(width, false)) - } else { - None - }, - } - } - - /// Tries to decrypt the given PKESK packet with `keypair` and try - /// to decrypt the packet parser using `decrypt`. - fn try_decrypt<D>(&self, pkesk: &PKESK, - sym_algo: Option<SymmetricAlgorithm>, - keypair: &mut dyn crypto::Decryptor, - decrypt: &mut D) - -> Option<Option<Fingerprint>> - where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool - { - let keyid = keypair.public().fingerprint().into(); - match pkesk.decrypt(keypair, sym_algo) - .and_then(|(algo, sk)| { - if decrypt(algo, &sk) { Some(sk) } else { None } - }) - { - Some(sk) => { - if self.dump_session_key { - eprintln!("Session key: {}", hex::encode(&sk)); - } - Some(self.key_identities.get(&keyid).map(|fp| fp.clone())) - }, - None => None, - } - } -} - -impl<'a> VerificationHelper for Helper<'a> { - fn inspect(&mut self, pp: &PacketParser) -> Result<()> { - if let Some(dumper) = self.dumper.as_mut() { - dumper.packet(&mut io::stderr(), - pp.recursion_depth() as usize, - pp.header().clone(), pp.packet.clone(), - pp.map().map(|m| m.clone()), None)?; - } - Ok(()) - } - - fn get_certs(&mut self, ids: &[openpgp::KeyHandle]) -> Result<Vec<Cert>> { - self.vhelper.get_certs(ids) - } - fn check(&mut self, structure: MessageStructure) -> Result<()> { - self.vhelper.check(structure) - } -} - -impl<'a> DecryptionHelper for Helper<'a> { - fn decrypt<D>(&mut self, pkesks: &[PKESK], skesks: &[SKESK], - sym_algo: Option<SymmetricAlgorithm>, - mut decrypt: D) -> openpgp::Result<Option<Fingerprint>> - where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool - { - // First, we try those keys that we can use without prompting - // for a password. - for pkesk in pkesks { - let keyid = pkesk.recipient(); - if let Some(key) = self.secret_keys.get(&keyid) { - if ! key.secret().is_encrypted() { - if let Some(fp) = key.clone().into_keypair().ok() - .and_then(|mut k| - self.try_decrypt(pkesk, sym_algo, &mut k, &mut decrypt)) - { - return Ok(fp); - } - } - } - } - - // Second, we try those keys that are encrypted. - for pkesk in pkesks { - // Don't ask the user to decrypt a key if we don't support - // the algorithm. - if ! pkesk.pk_algo().is_supported() { - continue; - } - - let keyid = pkesk.recipient(); - if let Some(key) = self.secret_keys.get_mut(&keyid) { - let mut keypair = loop { - if ! key.secret().is_encrypted() { - break key.clone().into_keypair().unwrap(); - } - - let p = rpassword::read_password_from_tty(Some( - &format!( - "Enter password to decrypt key {}: ", - self.key_hints.get(&keyid).unwrap())))?.into(); - - let algo = key.pk_algo(); - if let Some(()) = - key.secret_mut().decrypt_in_place(algo, &p).ok() { - break key.clone().into_keypair().unwrap() - } else { - eprintln!("Bad password."); - } - }; - - if let Some(fp) = - self.try_decrypt(pkesk, sym_algo, &mut keypair, - &mut decrypt) - { - return Ok(fp); - } - } - } - - // Third, we try to decrypt PKESK packets with wildcard - // recipients using those keys that we can use without - // prompting for a password. - for pkesk in pkesks.iter().filter(|p| p.recipient().is_wildcard()) { - for key in self.secret_keys.values() { - if ! key.secret().is_encrypted() { - if let Some(fp) = key.clone().into_keypair().ok() - .and_then(|mut k| - self.try_decrypt(pkesk, sym_algo, &mut k, &mut decrypt)) - { - return Ok(fp); - } - } - } - } - - // Fourth, we try to decrypt PKESK packets with wildcard - // recipients using those keys that are encrypted. - for pkesk in pkesks.iter().filter(|p| p.recipient().is_wildcard()) { - // Don't ask the user to decrypt a key if we don't support - // the algorithm. - if ! pkesk.pk_algo().is_supported() { - continue; - } - - // To appease the borrow checker, iterate over the - // hashmap, awkwardly. - for keyid in self.secret_keys.keys().cloned().collect::<Vec<_>>() - { - let mut keypair = loop { - let key = self.secret_keys.get_mut(&keyid).unwrap(); // Yuck - - if ! key.secret().is_encrypted() { - break key.clone().into_keypair().unwrap(); - } - - let p = rpassword::read_password_from_tty(Some( - &format!( - "Enter password to decrypt key {}: ", - self.key_hints.get(&keyid).unwrap())))?.into(); - - let algo = key.pk_algo(); - if let Some(()) = - key.secret_mut().decrypt_in_place(algo, &p).ok() { - break key.clone().into_keypair().unwrap() - } else { - eprintln!("Bad password."); - } - }; - - if let Some(fp) = - self.try_decrypt(pkesk, sym_algo, &mut keypair, - &mut decrypt) - { - return Ok(fp); - } - } - } - - if skesks.is_empty() { - return - Err(anyhow::anyhow!("No key to decrypt message")); - } - - // Finally, try to decrypt using the SKESKs. - loop { - let password = - rpassword::read_password_from_tty(Some( - "Enter password to decrypt message: "))?.into(); - - for skesk in skesks { - if let Some(sk) = skesk.decrypt(&password).ok() - .and_then(|(algo, sk)| { if decrypt(algo, &sk) { Some(sk) } else { None }}) - { - if self.dump_session_key { - eprintln!("Session key: {}", hex::encode(&sk)); - } - return Ok(None); - } - } - - eprintln!("Bad password."); - } - } -} - -pub fn decrypt(ctx: &Context, policy: &dyn Policy, mapping: &mut store::Mapping, - input: &mut dyn io::Read, output: &mut dyn io::Write, - signatures: usize, certs: Vec<Cert>, secrets: Vec<Cert>, - dump_session_key: bool, - dump: bool, hex: bool) - -> Result<()> { - let helper = Helper::new(ctx, policy, mapping, signatures, certs, secrets, - dump_session_key, dump || hex); - let mut decryptor = DecryptorBuilder::from_reader(input)? - .mapping(hex) - .with_policy(policy, None, helper) - .context("Decryption failed")?; - - io::copy(&mut decryptor, output).context("Decryption failed")?; - - let helper = decryptor.into_helper(); - if let Some(dumper) = helper.dumper.as_ref() { - dumper.flush(&mut io::stderr())?; - } - helper.vhelper.print_status(); - return Ok(()); -} - -pub fn decrypt_unwrap(ctx: &Context, policy: &dyn Policy, - mapping: &mut store::Mapping, - input: &mut dyn io::Read, output: &mut dyn io::Write, - secrets: Vec<Cert>, dump_session_key: bool) - -> Result<()> -{ - let mut helper = Helper::new(ctx, policy, mapping, 0, Vec::new(), secrets, - dump_session_key, false); - - let mut ppr = PacketParser::from_reader(input)?; - - let mut pkesks: Vec<packet::PKESK> = Vec::new(); - let mut skesks: Vec<packet::SKESK> = Vec::new(); - while let PacketParserResult::Some(mut pp) = ppr { - let sym_algo_hint = if let Packet::AED(ref aed) = pp.packet { - Some(aed.symmetric_algo()) - } else { - None - }; - - match pp.packet { - Packet::SEIP(_) | Packet::AED(_) => { - { - let decrypt = |algo, secret: &SessionKey| { - pp.decrypt(algo, secret).is_ok() - }; - helper.decrypt(&pkesks[..], &skesks[..], sym_algo_hint, - decrypt)?; - } - if pp.encrypted() { - return Err( - openpgp::Error::MissingSessionKey( - "No session key".into()).into()); - } - - io::copy(&mut pp, output)?; - return Ok(()); - }, - Packet::MDC(ref mdc) => if ! mdc.valid() { - return Err(openpgp::Error::ManipulatedMessage.into()); - }, - _ => (), - } - - let (p, ppr_tmp) = pp.recurse()?; - match p { - Packet::PKESK(pkesk) => pkesks.push(pkesk), - Packet::SKESK(skesk) => skesks.push(skesk), - _ => (), - } - ppr = ppr_tmp; - } - - Ok(()) -} diff --git a/tool/src/commands/dump.rs b/tool/src/commands/dump.rs deleted file mode 100644 index 6f35f56d..00000000 --- a/tool/src/commands/dump.rs +++ /dev/null @@ -1,946 +0,0 @@ -use std::io::{self, Read}; - -use sequoia_openpgp as openpgp; -use self::openpgp::types::{Duration, Timestamp, SymmetricAlgorithm}; -use self::openpgp::fmt::hex; -use self::openpgp::crypto::mpi; -use self::openpgp::{Packet, Result}; -use self::openpgp::packet::prelude::*; -use self::openpgp::packet::header::CTB; -use self::openpgp::packet::{Header, header::BodyLength, Signature}; -use self::openpgp::packet::signature::subpacket::{Subpacket, SubpacketValue}; -use self::openpgp::crypto::{SessionKey, S2K}; -use self::openpgp::parse::{map::Map, Parse, PacketParserResult}; - -#[derive(Debug)] -pub enum Kind { - Message { - encrypted: bool, - }, - Keyring, - Cert, - Unknown, -} - -/// Converts sequoia_openpgp types for rendering. -pub trait Convert<T> { - /// Performs the conversion. - fn convert(self) -> T; -} - -impl Convert<chrono::Duration> for std::time::Duration { - fn convert(self) -> chrono::Duration { - chrono::Duration::seconds(self.as_secs() as i64) - } -} - -impl Convert<chrono::Duration> for Duration { - fn convert(self) -> chrono::Duration { - chrono::Duration::seconds(self.as_secs() as i64) - } -} - -impl Convert<chrono::DateTime<chrono::offset::Utc>> for std::time::SystemTime { - fn convert(self) -> chrono::DateTime<chrono::offset::Utc> { - chrono::DateTime::<chrono::offset::Utc>::from(self) - } -} - -impl Convert<chrono::DateTime<chrono::offset::Utc>> for Timestamp { - fn convert(self) -> chrono::DateTime<chrono::offset::Utc> { - std::time::SystemTime::from(self).convert() - } -} - -pub fn dump<W>(input: &mut dyn io::Read, output: &mut dyn io::Write, - mpis: bool, hex: bool, sk: Option<&SessionKey>, - width: W) - -> Result<Kind> - where W: Into<Option<usize>> -{ - let mut ppr - = self::openpgp::parse::PacketParserBuilder::from_reader(input)? - .map(hex).build()?; - let mut message_encrypted = false; - let width = width.into().unwrap_or(80); - let mut dumper = PacketDumper::new(width, mpis); - - while let PacketParserResult::Some(mut pp) = ppr { - let additional_fields = match pp.packet { - Packet::Literal(_) => { - let mut prefix = vec![0; 40]; - let n = pp.read(&mut prefix)?; - Some(vec![ - format!("Content: {:?}{}", - String::from_utf8_lossy(&prefix[..n]), - if n == prefix.len() { "..." } else { "" }), - ]) - }, - Packet::SEIP(_) if sk.is_none() => { - message_encrypted = true; - Some(vec!["No session key supplied".into()]) - } - Packet::SEIP(_) if sk.is_some() => { - message_encrypted = true; - let sk = sk.as_ref().unwrap(); - let mut decrypted_with = None; - for algo in 1..20 { - let algo = SymmetricAlgorithm::from(algo); - if let Ok(size) = algo.key_size() { - if size != sk.len() { continue; } - } else { - continue; - } - - if let Ok(_) = pp.decrypt(algo, sk) { - decrypted_with = Some(algo); - break; - } - } - let mut fields = Vec::new(); - fields.push(format!("Session key: {}", hex::encode(sk))); - if let Some(algo) = decrypted_with { - fields.push(format!("Symmetric algo: {}", algo)); - fields.push("Decryption successful".into()); - } else { - fields.push("Decryption failed".into()); - } - Some(fields) - }, - Packet::AED(_) if sk.is_none() => { - message_encrypted = true; - Some(vec!["No session key supplied".into()]) - } - Packet::AED(_) if sk.is_some() => { - message_encrypted = true; - let sk = sk.as_ref().unwrap(); - let algo = if let Packet::AED(ref aed) = pp.packet { - aed.symmetric_algo() - } else { - unreachable!() - }; - - let _ = pp.decrypt(algo, sk); - - let mut fields = Vec::new(); - fields.push(format!("Session key: {}", hex::encode(sk))); - if pp.encrypted() { - fields.push("Decryption failed".into()); - } else { - fields.push("Decryption successful".into()); - } - Some(fields) - }, - _ => None, - }; - - let header = pp.header().clone(); - let map = pp.take_map(); - - let recursion_depth = pp.recursion_depth(); - let packet = pp.packet.clone(); - - dumper.packet(output, recursion_depth as usize, - header, packet, map, additional_fields)?; - - let (_, ppr_) = match pp.recurse() { - Ok(v) => Ok(v), - Err(e) => { - let _ = dumper.flush(output); - Err(e) - }, - }?; - ppr = ppr_; - } - - dumper.flush(output)?; - - if let PacketParserResult::EOF(eof) = ppr { - if eof.is_message().is_ok() { - Ok(Kind::Message { - encrypted: message_encrypted, - }) - } else if eof.is_cert().is_ok() { - Ok(Kind::Cert) - } else if eof.is_keyring().is_ok() { - Ok(Kind::Keyring) - } else { - Ok(Kind::Unknown) - } - } else { - unreachable!() - } -} - -struct Node { - header: Header, - packet: Packet, - map: Option<Map>, - additional_fields: Option<Vec<String>>, - children: Vec<Node>, -} - -impl Node { - fn new(header: Header, packet: Packet, map: Option<Map>, - additional_fields: Option<Vec<String>>) -> Self { - Node { - header: header, - packet: packet, - map: map, - additional_fields: additional_fields, - children: Vec::new(), - } - } - - fn append(&mut self, depth: usize, node: Node) { - if depth == 0 { - self.children.push(node); - } else { - self.children.iter_mut().last().unwrap().append(depth - 1, node); - } - } -} - -pub struct PacketDumper { - width: usize, - mpis: bool, - root: Option<Node>, -} - -impl PacketDumper { - pub fn new(width: usize, mpis: bool) -> Self { - PacketDumper { - width: width, - mpis: mpis, - root: None, - } - } - - pub fn packet(&mut self, output: &mut dyn io::Write, depth: usize, - header: Header, p: Packet, map: Option<Map>, - additional_fields: Option<Vec<String>>) - -> Result<()> { - let node = Node::new(header, p, map, additional_fields); - if self.root.is_none() { - assert_eq!(depth, 0); - self.root = Some(node); - } else { - if depth == 0 { - let root = self.root.take().unwrap(); - self.dump_tree(output, "", &root)?; - self.root = Some(node); - } else { - self.root.as_mut().unwrap().append(depth - 1, node); - } - } - Ok(()) - } - - pub fn flush(&self, output: &mut dyn io::Write) -> Result<()> { - if let Some(root) = self.root.as_ref() { - self.dump_tree(output, "", &root)?; - } - Ok(()) - } - - fn dump_tree(&self, output: &mut dyn io::Write, indent: &str, node: &Node) - -> Result<()> { - let indent_node = - format!("{}{} ", indent, - if node.children.is_empty() { " " } else { "│" }); - self.dump_packet(output, &indent_node, Some(&node.header), &node.packet, - node.map.as_ref(), node.additional_fields.as_ref())?; - if node.children.is_empty() { - return Ok(()); - } - - let last = node.children.len() - 1; - for (i, child) in node.children.iter().enumerate() { - let is_last = i == last; - write!(output, "{}{}── ", indent, - if is_last { "└" } else { "├" })?; - let indent_child = - format!("{}{} ", indent, - if is_last { " " } else { "│" }); - self.dump_tree(output, &indent_child, child)?; - } - Ok(()) - } - - fn dump_packet(&self, output: &mut dyn io::Write, i: &str, - header: Option<&Header>, p: &Packet, map: Option<&Map>, - additional_fields: Option<&Vec<String>>) - -> Result<()> { - use self::openpgp::Packet::*; - - if let Some(tag) = p.kind() { - write!(output, "{}", tag)?; - } else { - write!(output, "Unknown or Unsupported Packet")?; - } - - if let Some(h) = header { - write!(output, ", {} CTB, {}{}", - if let CTB::Old(_) = h.ctb() { "old" } else { "new" }, - if let Some(map) = map { - format!("{} header bytes + ", - map.iter().take(2).map(|f| f.as_bytes().len()) - .sum::<usize>()) - } else { - // XXX: Mapping is disabled. No can do for - // now. Once we save the header in - // packet::Common, we can use this instead of - // relying on the map. - "".into() - }, - match h.length() { - BodyLength::Full(n) => - format!("{} bytes", n), - BodyLength::Partial(n) => - format!("partial length, {} bytes in first chunk", n), - BodyLength::Indeterminate => - "indeterminate length".into(), - })?; - } - writeln!(output)?; - - fn dump_key<P, R>(pd: &PacketDumper, - output: &mut dyn io::Write, i: &str, - k: &Key<P, R>) - -> Result<()> - where P: key::KeyParts, - R: key::KeyRole, - { - writeln!(output, "{} Version: {}", i, k.version())?; - writeln!(output, "{} Creation time: {}", i, - k.creation_time().convert())?; - writeln!(output, "{} Pk algo: {}", i, k.pk_algo())?; - if let Some(bits) = k.mpis().bits() { - writeln!(output, "{} Pk size: {} bits", i, bits)?; - } - writeln!(output, "{} Fingerprint: {}", i, k.fingerprint())?; - writeln!(output, "{} KeyID: {}", i, k.keyid())?; - if pd.mpis { - writeln!(output, "{}", i)?; - writeln!(output, "{} Public Key:", i)?; - - let ii = format!("{} ", i); - match k.mpis() { - mpi::PublicKey::RSA { e, n } => - pd.dump_mpis(output, &ii, - &[e.value(), n.value()], - &["e", "n"])?, - mpi::PublicKey::DSA { p, q, g, y } => - pd.dump_mpis(output, &ii, - &[p.value(), q.value(), g.value(), - y.value()], - &["p", "q", "g", "y"])?, - mpi::PublicKey::ElGamal { p, g, y } => - pd.dump_mpis(output, &ii, - &[p.value(), g.value(), y.value()], - &["p", "g", "y"])?, - mpi::PublicKey::EdDSA { curve, q } => { - writeln!(output, "{} Curve: {}", ii, curve)?; - pd.dump_mpis(output, &ii, &[q.value()], &["q"])?; - }, - mpi::PublicKey::ECDSA { curve, q } => { - writeln!(output, "{} Curve: {}", ii, curve)?; - pd.dump_mpis(output, &ii, &[q.value()], &["q"])?; - }, - mpi::PublicKey::ECDH { curve, q, hash, sym } => { - writeln!(output, "{} Curve: {}", ii, curve)?; - writeln!(output, "{} Hash algo: {}", ii, hash)?; - writeln!(output, "{} Symmetric algo: {}", ii, - sym)?; - pd.dump_mpis(output, &ii, &[q.value()], &["q"])?; - }, - mpi::PublicKey::Unknown { mpis, rest } => { - let keys: Vec<String> = - (0..mpis.len()).map( - |i| format!("mpi{}", i)).collect(); - pd.dump_mpis( - output, &ii, - &mpis.iter().map(|m| { - m.value().iter().as_slice() - }).collect::<Vec<_>>()[..], - &keys.iter().map(|k| k.as_str()) - .collect::<Vec<_>>()[..], - )?; - - pd.dump_mpis(output, &ii, &[&rest[..]], &["rest"])?; - }, - mpi::PublicKey::__Nonexhaustive => unreachable!(), - } - } - - if let Some(secrets) = k.optional_secret() { - writeln!(output, "{}", i)?; - writeln!(output, "{} Secret Key:", i)?; - - let ii = format!("{} ", i); - match secrets { - SecretKeyMaterial::Unencrypted(ref u) => { - writeln!(output, "{}", i)?; - writeln!(output, "{} Unencrypted", ii)?; - if pd.mpis { - u |