diff options
Diffstat (limited to 'sq/src/commands/certify.rs')
-rw-r--r-- | sq/src/commands/certify.rs | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/sq/src/commands/certify.rs b/sq/src/commands/certify.rs new file mode 100644 index 00000000..22a0772e --- /dev/null +++ b/sq/src/commands/certify.rs @@ -0,0 +1,142 @@ +use std::time::{SystemTime, Duration}; + +use sequoia_openpgp as openpgp; +use openpgp::Result; +use openpgp::cert::prelude::*; +use openpgp::packet::prelude::*; +use openpgp::parse::Parse; +use openpgp::policy::Policy; +use openpgp::serialize::Serialize; +use openpgp::types::SignatureType; + +use crate::parse_duration; +use crate::SECONDS_IN_YEAR; + +pub fn certify(p: &impl Policy, m: &clap::ArgMatches, _force: bool) + -> Result<()> +{ + let certifier = m.value_of("certifier").unwrap(); + let cert = m.value_of("certificate").unwrap(); + let userid = m.value_of("userid").unwrap(); + + let certifier = Cert::from_file(certifier)?; + let cert = Cert::from_file(cert)?; + let vc = cert.with_policy(p, None)?; + + let trust_depth: u8 = m.value_of("depth") + .map(|s| s.parse()).unwrap_or(Ok(0))?; + let trust_amount: u8 = m.value_of("amount") + .map(|s| s.parse()).unwrap_or(Ok(120))?; + let regex = m.values_of("regex").map(|v| v.collect::<Vec<_>>()) + .unwrap_or(vec![]); + if trust_depth == 0 && regex.len() > 0 { + return Err( + anyhow::format_err!("A regex only makes sense \ + if the trust depth is greater than 0")); + } + + let local = m.is_present("local"); + let non_revocable = m.is_present("non-revocable"); + let expires = m.value_of("expires"); + let expires_in = m.value_of("expires-in"); + + + // Find the matching User ID. + let mut u = None; + for ua in vc.userids() { + if let Ok(a_userid) = std::str::from_utf8(ua.userid().value()) { + if a_userid == userid { + u = Some(ua.userid()); + break; + } + } + } + + let userid = if let Some(userid) = u { + userid + } else { + eprintln!("User ID: '{}' not found.\nValid User IDs:", userid); + let mut have_valid = false; + for ua in vc.userids() { + if let Ok(u) = std::str::from_utf8(ua.userid().value()) { + have_valid = true; + eprintln!(" - {}", u); + } + } + if ! have_valid { + eprintln!(" - Certificate has no valid User IDs."); + } + return Err(anyhow::format_err!("No matching User ID found")); + }; + + // Create the certification. + let mut builder + = SignatureBuilder::new(SignatureType::GenericCertification); + + if trust_depth != 0 || trust_amount != 120 { + builder = builder.set_trust_signature(trust_depth, trust_amount)?; + } + + for regex in regex { + builder = builder.add_regular_expression(regex)?; + } + + if local { + builder = builder.set_exportable_certification(false)?; + } + + if non_revocable { + builder = builder.set_revocable(false)?; + } + + match (expires, expires_in) { + (None, None) => + // Default expiration. + builder = builder.set_signature_validity_period( + Duration::new(5 * SECONDS_IN_YEAR, 0))?, + (Some(t), None) if t == "never" => + // The default is no expiration; there is nothing to do. + (), + (Some(t), None) => { + let now = builder.signature_creation_time() + .unwrap_or_else(std::time::SystemTime::now); + let expiration = SystemTime::from( + crate::parse_iso8601(t, chrono::NaiveTime::from_hms(0, 0, 0))?); + let validity = expiration.duration_since(now)?; + builder = builder.set_signature_creation_time(now)? + .set_signature_validity_period(validity)?; + }, + (None, Some(d)) if d == "never" => + // The default is no expiration; there is nothing to do. + (), + (None, Some(d)) => { + let d = parse_duration(d)?; + builder = builder.set_signature_validity_period(d)?; + }, + (Some(_), Some(_)) => unreachable!("conflicting args"), + } + + + // Sign it. + let mut signer = certifier.primary_key().key().clone() + .parts_into_secret()?.into_keypair()?; + + let certification = builder + .sign_userid_binding( + &mut signer, + cert.primary_key().component(), + userid)?; + let cert = cert.insert_packets(certification.clone())?; + assert!(cert.clone().into_packets().any(|p| { + match p { + Packet::Signature(sig) => sig == certification, + _ => false, + } + })); + + + // And export it. + cert.armored().serialize(&mut std::io::stdout())?; + + Ok(()) +} |