diff options
author | Heiko <heiko@schaefer.name> | 2021-04-29 16:45:25 +0200 |
---|---|---|
committer | Heiko <heiko@schaefer.name> | 2021-04-29 16:45:25 +0200 |
commit | 9740c6648104fc80ddaf6c40065feee245c9dcbb (patch) | |
tree | 14b9cc3e19e2028e36d76949cedcb61ee63219f7 | |
parent | 7e76c2e7f9dae8a22533b7a90af32632ce2571ed (diff) | |
download | openpgp-ca-9740c6648104fc80ddaf6c40065feee245c9dcbb.tar.gz |
Move code for valid_certifications_by() to pgp.rs - and add a check of the certification using verify_userid_binding().
-rw-r--r-- | src/cert.rs | 67 | ||||
-rw-r--r-- | src/pgp.rs | 42 |
2 files changed, 67 insertions, 42 deletions
diff --git a/src/cert.rs b/src/cert.rs index fb82bec..a2aecef 100644 --- a/src/cert.rs +++ b/src/cert.rs @@ -10,9 +10,7 @@ use crate::ca::OpenpgpCa; use crate::db::models; use crate::pgp::Pgp; -use sequoia_openpgp::cert::amalgamation::ValidateAmalgamation; -use sequoia_openpgp::packet::Signature; -use sequoia_openpgp::packet::UserID; +use sequoia_openpgp::packet::{Signature, UserID}; use sequoia_openpgp::policy::StandardPolicy; use anyhow::{Context, Result}; @@ -170,10 +168,10 @@ pub fn certs_refresh_ca_certifications( threshold_days: u64, validity_days: u64, ) -> Result<()> { - let ca_fp = oca.ca_get_cert_pub()?.fingerprint(); + let threshold_time = SystemTime::now() + + Duration::from_secs(threshold_days * Pgp::SECONDS_IN_DAY); - let threshold_secs = threshold_days * 24 * 60 * 60; - let threshold_time = SystemTime::now() + Duration::new(threshold_secs, 0); + let ca = oca.ca_get_cert_pub()?; for db_cert in oca .db() @@ -183,37 +181,38 @@ pub fn certs_refresh_ca_certifications( .filter(|c| !c.inactive) { let c = Pgp::armored_to_cert(&db_cert.pub_cert)?; - let mut uids_to_recert = Vec::new(); + + let mut recertify = Vec::new(); for uid in c.userids() { - let ca_certifications: Vec<_> = uid - .certifications() - .filter(|c| c.issuer_fingerprints().any(|fp| *fp == ca_fp)) - .collect(); + // find valid certifications by the CA on this uid + let ca_certifications = + Pgp::valid_certifications_by(&uid, &c, ca.clone()); - let sig_valid_past_threshold = |c: &&Signature| { - if let Some(expiration) = c.signature_expiration_time() { + let sig_valid_past_threshold = |sig: &Signature| { + if let Some(expiration) = sig.signature_expiration_time() { expiration > threshold_time } else { true // signature has no expiration time } }; - // a new certification is created if certifications by the - // CA exist, but none of the existing certifications are - // valid for longer than `threshold_days` + // A new certification is created if + // a) a valid certification by the CA exists, but + // b) no existing certification is valid for longer than + // `threshold_days` if !ca_certifications.is_empty() && !ca_certifications.iter().any(sig_valid_past_threshold) { - // make a new certification for this uid - uids_to_recert.push(uid.userid()); + // A new certification for this uid should be created + recertify.push(uid.userid()); } } - if !uids_to_recert.is_empty() { - // make new certifications for "uids_to_update" + if !recertify.is_empty() { + // Make new certifications for the User IDs identified above let recertified = oca.secret().sign_user_ids( &c, - &uids_to_recert[..], + &recertify[..], Some(validity_days), )?; @@ -281,27 +280,15 @@ pub fn cert_check_ca_sig( cert: &models::Cert, ) -> Result<Vec<UserID>> { let c = Pgp::armored_to_cert(&cert.pub_cert)?; - let ca = oca.ca_get_cert_pub()?; - let mut res = Vec::new(); - let policy = StandardPolicy::new(); - - for uid in c.userids() { - let signed_by_ca = uid - .clone() - .with_policy(&policy, None)? - .bundle() - .certifications() - .iter() - .any(|s| s.issuer_fingerprints().any(|f| f == &ca.fingerprint())); - - if signed_by_ca { - res.push(uid.userid().clone()); - } - } - - Ok(res) + Ok(c.userids() + .filter(|uid| { + !Pgp::valid_certifications_by(&uid, &c, ca.clone()).is_empty() + }) + .map(|uid| uid.userid()) + .cloned() + .collect()) } pub fn cert_check_tsig_on_ca( @@ -25,15 +25,18 @@ use std::convert::identity; use std::time::SystemTime; use anyhow::{Context, Result}; +use sequoia_openpgp::cert::prelude::ComponentAmalgamation; use sha2::Digest; -const POLICY: &StandardPolicy = &StandardPolicy::new(); - const CA_KEY_NOTATION: &str = "openpgp-ca@notations.sequoia-pgp.org"; +const POLICY: &StandardPolicy = &StandardPolicy::new(); + pub struct Pgp {} impl Pgp { + pub const SECONDS_IN_DAY: u64 = 60 * 60 * 24; + fn diceware() -> String { // FIXME: configurable dictionaries, ... ? use chbs::passphrase; @@ -419,4 +422,39 @@ impl Pgp { Ok(res) } + + /// For `uid` (which is part of Cert `c`), find all valid + /// certifications that have been made by `certifier`. + pub(crate) fn valid_certifications_by( + uid: &ComponentAmalgamation<UserID>, + c: &Cert, + certifier: Cert, + ) -> Vec<Signature> { + let certifier_keys: Vec<_> = certifier + .keys() + .with_policy(POLICY, None) + .alive() + .revoked(false) + .for_certification() + .collect(); + + let certifier_fp = certifier.fingerprint(); + + let pk = c.primary_key(); + + uid.certifications() + .filter(|&s| { + // does the signature appear to be issued by `certifier`? + s.issuer_fingerprints() + .any(|issuer| issuer == &certifier_fp) + }) + .filter(|&s| { + // check if the apparent certification by `certifier` is valid + certifier_keys.iter().any(|signer| { + s.clone().verify_userid_binding(&signer, &pk, &uid).is_ok() + }) + }) + .cloned() + .collect() + } } |