diff --git a/src/cli/cli_args.rs b/src/cli/cli_args.rs index 539bfc2..1247fd2 100644 --- a/src/cli/cli_args.rs +++ b/src/cli/cli_args.rs @@ -80,9 +80,18 @@ pub enum IntroducerSubcommand { pub struct IntroducerAddCommand { #[clap( long = "cert", - help = "Certificate to declare as introducer." + help = "Certificate to declare as introducer.", + conflicts_with = "cert_file" )] - pub cert: String, + pub cert: Option, + + #[clap( + long = "cert-file", + help = "Import certificate from CERT_FILE and declare it as introducer.", + value_name = "CERT_FILE", + conflicts_with = "cert" + )] + pub cert_file: Option, #[clap( long = "domains", @@ -146,7 +155,7 @@ pub struct LocalAddCommand { long = "cert", help = "Certificate to add." )] - pub cert: String, + pub cert_file: String, } #[derive(Parser, Debug)] diff --git a/src/commands.rs b/src/commands.rs index 499b8a9..e5d5df6 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -16,6 +16,10 @@ pub enum CommandError { CertNotFound(Fingerprint), #[error("Certificate not usable: {0}")] CertNotUsable(Fingerprint), + #[error("Certificate {0} is not alive (is expired or created in the future)")] + CertNotAlive(Fingerprint), + #[error("Certificate is revoked: {0}")] + CertRevoked(Fingerprint), } pub async fn dispatch(cli: CliArgs, config: HuskConfigContainer) -> Result<()> { diff --git a/src/commands/introducer/add.rs b/src/commands/introducer/add.rs index 695a712..084d051 100644 --- a/src/commands/introducer/add.rs +++ b/src/commands/introducer/add.rs @@ -3,10 +3,14 @@ use anyhow::Result; use sequoia_cert_store::Store; use sequoia_openpgp::cert::ValidCert; +use sequoia_openpgp::cert; +use sequoia_openpgp::parse::Parse; use sequoia_openpgp::{Fingerprint, KeyHandle}; +use sequoia_openpgp::types::RevocationStatus; use crate::cli::cli_args::IntroducerAddCommand; use crate::commands::CommandError; +use crate::common::crypto; use crate::config::HuskConfigContainer; use crate::types::husk_context::HuskContext; use crate::types::introducer::Introducer; @@ -14,29 +18,46 @@ use crate::types::introducer::Introducer; pub async fn dispatch(cmd: IntroducerAddCommand, config: HuskConfigContainer) -> Result<()> { println!("add"); println!(" {:?}", cmd.cert); + println!(" {:?}", cmd.cert_file); println!(" {:?}", cmd.domains); let context = HuskContext::new(&config.into())?; let policy = &context.policy; let cert_store = &context.cert_store; - // Get the certificate - let fpr = Fingerprint::from_hex(&cmd.cert)?; - let certs = cert_store.lookup_by_cert(&KeyHandle::try_from(&fpr)?) - .map_err(|_| CommandError::CertNotFound(fpr.clone()))?; + if let Some(cert_file) = cmd.cert_file { + let c = cert::Cert::from_file(cert_file)?; + let vc = c.with_policy(policy, None)?; - let certs: Vec = certs.iter() - .filter_map(|c| - c.with_policy(policy, None).ok() - ) - .collect(); + // guards + if vc.alive().is_err() { + return Err(CommandError::CertNotAlive(vc.fingerprint()).into()); + } + if matches!(vc.revocation_status(), RevocationStatus::Revoked(_)) { + return Err(CommandError::CertRevoked(vc.fingerprint()).into()); + } + if !crypto::has_certification_capability(&vc) { + return Err(CommandError::CertNotUsable(vc.fingerprint()).into()); + } - if let Some(cert) = certs.first() { - Introducer::create(&context, cert, cmd.domains)?; - } else { - return Err(CommandError::CertNotUsable(fpr).into()); + Introducer::create(&context, &vc, cmd.domains)?; + } else if let Some(cert) = cmd.cert { + let fpr = Fingerprint::from_hex(cert.as_str())?; + let certs = cert_store.lookup_by_cert(&KeyHandle::try_from(&fpr)?) + .map_err(|_| CommandError::CertNotFound(fpr.clone()))?; + + let certs: Vec = certs.iter() + .filter_map(|c| + c.with_policy(policy, None).ok() + ) + .collect(); + + if let Some(cert) = certs.first() { + Introducer::create(&context, &cert, cmd.domains)?; + } else { + return Err(CommandError::CertNotUsable(fpr).into()); + } } - Ok(()) } diff --git a/src/commands/local/add.rs b/src/commands/local/add.rs index 059c4ec..756abb1 100644 --- a/src/commands/local/add.rs +++ b/src/commands/local/add.rs @@ -5,7 +5,7 @@ use crate::config::HuskConfigContainer; pub async fn dispatch(cmd: LocalAddCommand, _config: &HuskConfigContainer) -> Result<()> { println!("add"); - println!(" {:?}", cmd.cert); + println!(" {:?}", cmd.cert_file); Ok(()) } diff --git a/src/common/crypto.rs b/src/common/crypto.rs index d7865cf..57dc08a 100644 --- a/src/common/crypto.rs +++ b/src/common/crypto.rs @@ -472,6 +472,15 @@ pub fn authenticate(context: &HuskContext, cert: &ValidCert, activate: bool, dom Ok(()) } +pub fn has_certification_capability(cert: &ValidCert) -> bool { + cert.keys() + .for_certification() + .alive() + .supported() + .revoked(false) + .count() > 0 +} + #[cfg(test)] pub mod tests {