summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHeiko <heiko@schaefer.name>2021-04-29 16:45:25 +0200
committerHeiko <heiko@schaefer.name>2021-04-29 16:45:25 +0200
commit9740c6648104fc80ddaf6c40065feee245c9dcbb (patch)
tree14b9cc3e19e2028e36d76949cedcb61ee63219f7
parent7e76c2e7f9dae8a22533b7a90af32632ce2571ed (diff)
downloadopenpgp-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.rs67
-rw-r--r--src/pgp.rs42
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(
diff --git a/src/pgp.rs b/src/pgp.rs
index c5e0e2e..d1a30d8 100644
--- a/src/pgp.rs
+++ b/src/pgp.rs
@@ -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()
+ }
}