//! introducers use anyhow::Result; use sequoia_wot as wot; use wot::{CertSynopsis, Certification}; use sequoia_openpgp::{cert::ValidCert, policy::StandardPolicy}; use sequoia_openpgp::Cert; use crate::types::husk_context::HuskContext; use crate::common::crypto; use crate::common::escape; #[derive(thiserror::Error, Debug)] pub enum IntroducerError { #[error("Unsuitable certificate")] UnsuitableCertificate, } #[derive(Debug, Clone)] pub struct Introducer { pub cert: CertSynopsis, pub certification: Certification, } impl Introducer { /// Can this introducer potentially introduce `email`? /// /// This check matches the email with the list of domains of the /// introducer. pub fn can_introduce(&self, email: &str) -> bool { if let Some(regexset) = self.certification.regular_expressions() { if regexset.matches_everything() { // return false, because unlimited certifications are // ignored. false } else { regexset.is_match(email) } } else { false } } /// Has this introducer certified cert? pub fn is_introducing(&self, cert: &Cert) -> bool { let policy = StandardPolicy::new(); let mut result = false; let my_keyid = self.cert.keyid(); if let Ok(vc) = cert.with_policy(&policy, None) { vc.userids() .revoked(false) .for_each(|u| { u.certifications().for_each(|s| { if s.signature_alive(None, None).is_ok() { s.issuers().for_each(|i| { if i == &my_keyid { result = true; } }); } }) }); } result } /// Create an introducer. pub fn create(context: &HuskContext, cert: &ValidCert, domains: Vec) -> Result<()> { // Check if the local trust root certificate should be made an // introducer, which would be an error. if cert.fingerprint() == context.local_trust_root.fingerprint() { return Err(IntroducerError::UnsuitableCertificate.into()); } crypto::authenticate( context, cert, crypto::Role::Introducer, true, Some(domains)) } /// Remove an introducer. pub fn remove(context: &HuskContext, cert: &ValidCert, _demote: bool) -> Result<()> { // Check if the local trust root certificate is used as // introducer, which would be an error. if cert.fingerprint() == context.local_trust_root.fingerprint() { return Err(IntroducerError::UnsuitableCertificate.into()); } crypto::authenticate( context, cert, crypto::Role::Introducer, false, None) } } use std::fmt::{Display, Formatter}; impl Display for Introducer { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { // XXX: This is unsafe as ANSI control chars are not handled. let uids = self.cert.userids() .map(|u| String::from_utf8_lossy(u.userid().value()).to_string()) .collect::>() .join(", "); writeln!(f, "Introducer: {}", self.cert.fingerprint())?; writeln!(f, " UserIDs: {}", uids)?; if let Some(regex_set) = self.certification.regular_expressions() { for regex in regex_set.as_bytes().into_iter() { let regex = String::from_utf8_lossy(regex); if let Ok(domain) = escape::regex_domain_unescape(regex.to_string().as_str()) { writeln!(f," Domain: {}", domain)?; } else { writeln!(f," RegEx: {}", regex)?; } } } Ok(()) } }