summaryrefslogtreecommitdiffstats
path: root/sq/src/commands/key.rs
diff options
context:
space:
mode:
Diffstat (limited to 'sq/src/commands/key.rs')
-rw-r--r--sq/src/commands/key.rs204
1 files changed, 201 insertions, 3 deletions
diff --git a/sq/src/commands/key.rs b/sq/src/commands/key.rs
index dc1df48a..b10da74a 100644
--- a/sq/src/commands/key.rs
+++ b/sq/src/commands/key.rs
@@ -3,14 +3,21 @@ use clap::ArgMatches;
use itertools::Itertools;
use std::time::{SystemTime, Duration};
-use crate::openpgp::Result;
+use crate::openpgp::KeyHandle;
use crate::openpgp::Packet;
-use crate::openpgp::cert::prelude::*;
-use crate::openpgp::types::KeyFlags;
+use crate::openpgp::Result;
use crate::openpgp::armor::{Writer, Kind};
+use crate::openpgp::cert::prelude::*;
+use crate::openpgp::packet::prelude::*;
+use crate::openpgp::packet::signature::subpacket::SubpacketTag;
+use crate::openpgp::parse::Parse;
+use crate::openpgp::policy::Policy;
use crate::openpgp::serialize::Serialize;
+use crate::openpgp::types::KeyFlags;
+use crate::openpgp::types::SignatureType;
use crate::create_or_stdout;
+use crate::decrypt_key;
const SECONDS_IN_DAY : u64 = 24 * 60 * 60;
const SECONDS_IN_YEAR : u64 =
@@ -246,3 +253,194 @@ fn parse_duration(expiry: &str) -> Result<Duration> {
Ok(Duration::new(count * factor, 0))
}
+
+pub fn adopt(m: &ArgMatches, p: &dyn Policy) -> Result<()> {
+ let cert = m.value_of("certificate").unwrap();
+ let cert = Cert::from_file(cert)
+ .context(format!("Parsing {}", cert))?;
+ let mut wanted: Vec<(KeyHandle,
+ Option<(Key<key::PublicParts, key::SubordinateRole>,
+ SignatureBuilder)>)>
+ = vec![];
+
+ // Gather the Key IDs / Fingerprints and make sure they are valid.
+ for id in m.values_of("key").unwrap_or_default() {
+ let h = id.parse::<KeyHandle>()?;
+ if h.is_invalid() {
+ return Err(anyhow::anyhow!(
+ "Invalid Fingerprint or KeyID ('{:?}')", id));
+ }
+ wanted.push((h, None));
+ }
+
+ // Find the corresponding keys.
+ for keyring in m.values_of("keyring").unwrap_or_default() {
+ for cert in CertParser::from_file(keyring)
+ .context(format!("Parsing: {}", keyring))?
+ {
+ let cert = cert.context(format!("Parsing {}", keyring))?;
+ let vc = match cert.with_policy(p, None) {
+ Ok(vc) => vc,
+ Err(err) => {
+ eprintln!("Ignoring {} from '{}': {}",
+ cert.keyid().to_hex(), keyring, err);
+ continue;
+ }
+ };
+
+ for key in vc.keys() {
+ for (id, ref mut keyo) in wanted.iter_mut() {
+ if id.aliases(key.key_handle()) {
+ match keyo {
+ Some((_, _)) =>
+ // We already saw this key.
+ (),
+ None => {
+ let sig = key.binding_signature();
+ let builder: SignatureBuilder = match sig.typ() {
+ SignatureType::SubkeyBinding =>
+ sig.clone().into(),
+ SignatureType::DirectKey
+ | SignatureType::PositiveCertification
+ | SignatureType::CasualCertification
+ | SignatureType::PersonaCertification
+ | SignatureType::GenericCertification =>
+ {
+ // Convert to a binding
+ // signature.
+ let kf = sig.key_flags()
+ .context("Missing required \
+ subpacket, KeyFlags")?;
+ SignatureBuilder::new(
+ SignatureType::SubkeyBinding)
+ .set_key_flags(kf)?
+ },
+ _ => panic!("Unsupported binding \
+ signature: {:?}",
+ sig),
+ };
+
+ *keyo = Some(
+ (key.key().clone().role_into_subordinate(),
+ builder));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ // If we are missing any keys, stop now.
+ let missing: Vec<&KeyHandle> = wanted
+ .iter()
+ .filter_map(|(id, keyo)| {
+ match keyo {
+ Some(_) => None,
+ None => Some(id),
+ }
+ })
+ .collect();
+ if missing.len() > 0 {
+ return Err(anyhow::anyhow!(
+ "Keys not found: {}",
+ missing.iter().map(|&h| h.to_hex()).join(", ")));
+ }
+
+
+ let passwords = &mut Vec::new();
+
+ // Get a signer.
+ let pk = cert.primary_key().key();
+ let mut pk_signer =
+ decrypt_key(
+ pk.clone().parts_into_secret()?,
+ passwords)?
+ .into_keypair()?;
+
+
+ // Add the keys and signatues to cert.
+ let mut packets: Vec<Packet> = vec![];
+ for (_, ka) in wanted.into_iter() {
+ let (key, builder) = ka.expect("Checked for missing keys above.");
+ let mut builder = builder;
+
+ // If there is a valid backsig, recreate it.
+ let need_backsig = builder.key_flags()
+ .map(|kf| kf.for_signing() || kf.for_certification())
+ .expect("Missing keyflags");
+
+ if need_backsig {
+ // Derive a signer.
+ let mut subkey_signer
+ = decrypt_key(
+ key.clone().parts_into_secret()?,
+ passwords)?
+ .into_keypair()?;
+
+ let backsig = builder.embedded_signatures()
+ .filter(|backsig| {
+ (*backsig).clone().verify_primary_key_binding(
+ &cert.primary_key(),
+ &key).is_ok()
+ })
+ .nth(0)
+ .map(|sig| SignatureBuilder::from(sig.clone()))
+ .unwrap_or_else(|| {
+ SignatureBuilder::new(SignatureType::PrimaryKeyBinding)
+ })
+ .sign_primary_key_binding(&mut subkey_signer, pk, &key)?;
+
+ builder = builder.set_embedded_signature(backsig)?;
+ } else {
+ builder = builder.modify_hashed_area(|mut a| {
+ a.remove_all(SubpacketTag::EmbeddedSignature);
+ Ok(a)
+ })?;
+ }
+
+ let mut sig = builder.sign_subkey_binding(&mut pk_signer, pk, &key)?;
+
+ // Verify it.
+ assert!(sig.verify_subkey_binding(pk_signer.public(), pk, &key)
+ .is_ok());
+
+ packets.push(key.into());
+ packets.push(sig.into());
+ }
+
+ let cert = cert.clone().insert_packets(packets.clone())?;
+
+ cert.as_tsk().armored().serialize(&mut std::io::stdout())?;
+
+ let vc = cert.with_policy(p, None).expect("still valid");
+ for pair in packets[..].chunks(2) {
+ let newkey: &Key<key::PublicParts, key::UnspecifiedRole> = match pair[0] {
+ Packet::PublicKey(ref k) => k.into(),
+ Packet::PublicSubkey(ref k) => k.into(),
+ Packet::SecretKey(ref k) => k.into(),
+ Packet::SecretSubkey(ref k) => k.into(),
+ ref p => panic!("Expected a key, got: {:?}", p),
+ };
+ let newsig: &Signature = match pair[1] {
+ Packet::Signature(ref s) => s,
+ ref p => panic!("Expected a sig, got: {:?}", p),
+ };
+
+ let mut found = false;
+ for key in vc.keys() {
+ if key.fingerprint() == newkey.fingerprint() {
+ for sig in key.self_signatures() {
+ if sig == newsig {
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+ assert!(found, "Subkey: {:?}\nSignature: {:?}", newkey, newsig);
+ }
+
+ Ok(())
+}