From ba036bfb1f4c6def2f916b9ef135ec7b5a98fcdb Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Thu, 16 May 2019 17:11:26 +0200 Subject: openpgp: Introduce trait Policy. WIP - Add a new trait `sequoia_openpgp::Policy`. Currently, this trait only has one method that returns the current time. This time is used when evaluating signatures. - Use this trait in TPK::{revocation_status,alive,expired}. Remove the corresponding *_at() functions. - Fixes #274. --- openpgp-ffi/src/tpk.rs | 19 ++++++----- openpgp/src/lib.rs | 46 ++++++++++++++++++++++++++ openpgp/src/tpk/builder.rs | 4 +-- openpgp/src/tpk/mod.rs | 79 ++++++++++++++++++-------------------------- sqv/src/sqv.rs | 4 +-- tool/src/commands/inspect.rs | 2 +- 6 files changed, 94 insertions(+), 60 deletions(-) diff --git a/openpgp-ffi/src/tpk.rs b/openpgp-ffi/src/tpk.rs index 259b3a1c..c7ddc1c3 100644 --- a/openpgp-ffi/src/tpk.rs +++ b/openpgp-ffi/src/tpk.rs @@ -11,6 +11,7 @@ use libc::{c_char, c_int, size_t, time_t, uint8_t}; extern crate sequoia_openpgp as openpgp; use self::openpgp::{ + Policy, autocrypt::Autocrypt, crypto, constants::ReasonForRevocation, @@ -162,12 +163,12 @@ fn pgp_tpk_revocation_status_at(tpk: *const TPK, when: time_t) { let when = when as i64; let when = if when == 0 { - None + time::now() } else { - Some(time::at(time::Timespec::new(when, 0))) + time::at(time::Timespec::new(when, 0)) }; - tpk.ref_raw().revocation_status_at(when).move_into_raw() + tpk.ref_raw().revocation_status(&when as &Policy).move_into_raw() } /// Returns the TPK's current revocation status. @@ -179,7 +180,7 @@ fn pgp_tpk_revocation_status_at(tpk: *const TPK, when: time_t) fn pgp_tpk_revocation_status(tpk: *const TPK) -> *mut RevocationStatus<'static> { - tpk.ref_raw().revocation_status().move_into_raw() + tpk.ref_raw().revocation_status(None).move_into_raw() } fn int_to_reason_for_revocation(code: c_int) -> ReasonForRevocation { @@ -330,7 +331,7 @@ fn pgp_tpk_expired(tpk: *const TPK) -> c_int { let tpk = tpk.ref_raw(); - tpk.expired() as c_int + tpk.expired(None) as c_int } /// Returns whether the TPK has expired. @@ -338,7 +339,8 @@ fn pgp_tpk_expired(tpk: *const TPK) fn pgp_tpk_expired_at(tpk: *const TPK, when: time_t) -> c_int { let tpk = tpk.ref_raw(); - tpk.expired_at(time::at(time::Timespec::new(when as i64, 0))) as c_int + tpk.expired(&time::at(time::Timespec::new(when as i64, 0)) as &Policy) + as c_int } /// Returns whether the TPK is alive. @@ -347,7 +349,7 @@ fn pgp_tpk_alive(tpk: *const TPK) -> c_int { let tpk = tpk.ref_raw(); - tpk.alive() as c_int + tpk.alive(None) as c_int } /// Returns whether the TPK is alive at the specified time. @@ -355,7 +357,8 @@ fn pgp_tpk_alive(tpk: *const TPK) fn pgp_tpk_alive_at(tpk: *const TPK, when: time_t) -> c_int { let tpk = tpk.ref_raw(); - tpk.alive_at(time::at(time::Timespec::new(when as i64, 0))) as c_int + tpk.alive(&time::at(time::Timespec::new(when as i64, 0)) as &Policy) + as c_int } /// Changes the TPK's expiration. diff --git a/openpgp/src/lib.rs b/openpgp/src/lib.rs index c9fe4ff5..20515cba 100644 --- a/openpgp/src/lib.rs +++ b/openpgp/src/lib.rs @@ -515,3 +515,49 @@ pub enum RevocationStatus<'a> { /// revocation certificate. NotAsFarAsWeKnow, } + +/// A policy for cryptographic operations. +/// +/// # Example +/// +/// This demonstrates how to evaluate signatures for a different time. +/// +/// ```rust +/// # extern crate time; +/// # use sequoia_openpgp::*; +/// # fn f(tpk: &TPK) -> Result<()> { +/// // First option: Explicit implementation of Policy: +/// struct NextWeek(()); +/// +/// impl Policy for NextWeek { +/// fn current_time(&self) -> time::Tm { +/// time::now() + time::Duration::weeks(1) +/// } +/// } +/// +/// let alive = tpk.alive(&NextWeek(()) as &Policy); +/// +/// // Second option: Use time::Tm as Policy: +/// let next_week = time::now() + time::Duration::weeks(1); +/// let alive_ = tpk.alive(&next_week as &Policy); +/// +/// assert_eq!(alive, alive_); +/// # Ok(()) } +/// ``` +pub trait Policy { + /// Returns the time relative to which signatures are evaluated. + /// + /// The default implementation returns the current time. + fn current_time(&self) -> time::Tm { + time::now() + } +} + +struct DefaultPolicy(()); +impl Policy for DefaultPolicy {} + +impl Policy for time::Tm { + fn current_time(&self) -> time::Tm { + self.clone() + } +} diff --git a/openpgp/src/tpk/builder.rs b/openpgp/src/tpk/builder.rs index e6c8ba2b..8bf3ceb8 100644 --- a/openpgp/src/tpk/builder.rs +++ b/openpgp/src/tpk/builder.rs @@ -555,11 +555,11 @@ mod tests { let (tpk, revocation) = TPKBuilder::new() .set_cipher_suite(CipherSuite::Cv25519) .generate().unwrap(); - assert_eq!(tpk.revocation_status(), + assert_eq!(tpk.revocation_status(None), RevocationStatus::NotAsFarAsWeKnow); let tpk = tpk.merge_packets(vec![revocation.clone().into()]).unwrap(); - assert_eq!(tpk.revocation_status(), + assert_eq!(tpk.revocation_status(None), RevocationStatus::Revoked(&[revocation])); } diff --git a/openpgp/src/tpk/mod.rs b/openpgp/src/tpk/mod.rs index 011a312c..6fd113ef 100644 --- a/openpgp/src/tpk/mod.rs +++ b/openpgp/src/tpk/mod.rs @@ -31,6 +31,8 @@ use { TPK, KeyID, Fingerprint, + Policy, + DefaultPolicy, }; use parse::{Parse, PacketParserResult, PacketParser}; use serialize::SerializeInto; @@ -871,7 +873,7 @@ impl<'a> Iterator for KeyIter<'a> { self.primary = true; (tpk.primary_key_signature(), - tpk.revocation_status(), + tpk.revocation_status(None), tpk.primary()) } else { self.subkey_iter.next() @@ -1705,10 +1707,11 @@ impl TPK { /// Note: this only returns whether the primary key is revoked. If you /// want to know whether a subkey, user id, etc., is revoked, then /// you need to query them separately. - pub fn revocation_status_at(&self, t: T) -> RevocationStatus - where T: Into> + pub fn revocation_status<'a, P>(&self, policy: P) -> RevocationStatus + where P: Into> { - let t = t.into().unwrap_or_else(time::now_utc); + let p = policy.into().unwrap_or(&DefaultPolicy(())); + let t = p.current_time(); let has_self_revs = active_revocation(&self.primary_selfsigs, &self.primary_self_revocations, t); @@ -1728,15 +1731,6 @@ impl TPK { } } - /// Returns the TPK's current revocation status. - /// - /// Note: this only returns whether the primary key is revoked. If you - /// want to know whether a subkey, user id, etc., is revoked, then - /// you need to query them separately. - pub fn revocation_status(&self) -> RevocationStatus { - self.revocation_status_at(None) - } - /// Returns a revocation certificate for the TPK. /// /// # Example @@ -1756,7 +1750,7 @@ impl TPK { /// .set_cipher_suite(CipherSuite::Cv25519) /// .generate()?; /// assert_eq!(RevocationStatus::NotAsFarAsWeKnow, - /// tpk.revocation_status()); + /// tpk.revocation_status(None)); /// /// let mut keypair = tpk.primary().clone().into_keypair()?; /// let sig = tpk.revoke(&mut keypair, ReasonForRevocation::KeyCompromised, @@ -1765,7 +1759,7 @@ impl TPK { /// /// let tpk = tpk.merge_packets(vec![sig.clone().into()])?; /// assert_eq!(RevocationStatus::Revoked(&[sig]), - /// tpk.revocation_status()); + /// tpk.revocation_status(None)); /// # Ok(()) /// # } pub fn revoke(&self, primary_signer: &mut Signer, @@ -1810,13 +1804,13 @@ impl TPK { /// .set_cipher_suite(CipherSuite::Cv25519) /// .generate()?; /// assert_eq!(RevocationStatus::NotAsFarAsWeKnow, - /// tpk.revocation_status()); + /// tpk.revocation_status(None)); /// /// let mut keypair = tpk.primary().clone().into_keypair()?; /// let tpk = tpk.revoke_in_place(&mut keypair, /// ReasonForRevocation::KeyCompromised, /// b"It was the maid :/")?; - /// if let RevocationStatus::Revoked(sigs) = tpk.revocation_status() { + /// if let RevocationStatus::Revoked(sigs) = tpk.revocation_status(None) { /// assert_eq!(sigs.len(), 1); /// assert_eq!(sigs[0].sigtype(), SignatureType::KeyRevocation); /// assert_eq!(sigs[0].reason_for_revocation(), @@ -1837,36 +1831,24 @@ impl TPK { } /// Returns whether or not the TPK has expired. - pub fn expired(&self) -> bool { - if let Some(Signature::V4(sig)) = self.primary_key_signature() { - sig.key_expired(self.primary()) - } else { - false - } - } - - /// Returns whether or not the key is expired at the given time. - pub fn expired_at(&self, tm: time::Tm) -> bool { + pub fn expired<'a, P>(&self, policy: P) -> bool + where P: Into> + { + let p = policy.into().unwrap_or(&DefaultPolicy(())); if let Some(Signature::V4(sig)) = self.primary_key_signature() { - sig.key_expired_at(self.primary(), tm) - } else { - false - } - } - - /// Returns whether or not the TPK is alive. - pub fn alive(&self) -> bool { - if let Some(sig) = self.primary_key_signature() { - sig.key_alive(self.primary()) + sig.key_expired_at(self.primary(), p.current_time()) } else { false } } /// Returns whether or not the key is alive at the given time. - pub fn alive_at(&self, tm: time::Tm) -> bool { + pub fn alive<'a, P>(&self, policy: P) -> bool + where P: Into> + { + let p = policy.into().unwrap_or(&DefaultPolicy(())); if let Some(sig) = self.primary_key_signature() { - sig.key_alive_at(self.primary(), tm) + sig.key_alive_at(self.primary(), p.current_time()) } else { false } @@ -3548,7 +3530,7 @@ mod test { assert_eq!(sigtype, SignatureType::PositiveCertificate, "{:#?}", tpk); - let revoked = tpk.revocation_status(); + let revoked = tpk.revocation_status(None); if direct_revoked { assert_match!(RevocationStatus::Revoked(_) = revoked, "{:#?}", tpk); @@ -3635,7 +3617,7 @@ mod test { let (tpk, _) = TPKBuilder::autocrypt(None, Some("Test")) .generate().unwrap(); assert_eq!(RevocationStatus::NotAsFarAsWeKnow, - tpk.revocation_status()); + tpk.revocation_status(None)); let mut keypair = tpk.primary().clone().into_keypair().unwrap(); let sig = tpk.revoke(&mut keypair, @@ -3644,7 +3626,7 @@ mod test { assert_eq!(sig.sigtype(), SignatureType::KeyRevocation); let tpk = tpk.merge_packets(vec![sig.into()]).unwrap(); - assert_match!(RevocationStatus::Revoked(_) = tpk.revocation_status()); + assert_match!(RevocationStatus::Revoked(_) = tpk.revocation_status(None)); } #[test] @@ -3670,7 +3652,7 @@ mod test { assert_eq!(sig.sigtype(), SignatureType::CertificateRevocation); let tpk = tpk.merge_packets(vec![sig.into()]).unwrap(); assert_eq!(RevocationStatus::NotAsFarAsWeKnow, - tpk.revocation_status()); + tpk.revocation_status(None)); let uid = tpk.userids().skip(1).next().unwrap(); assert_match!(RevocationStatus::Revoked(_) = uid.revoked(None)); @@ -3748,13 +3730,16 @@ mod test { let te1 = t1 - time::Duration::days((300.0 * f1) as i64); let t12 = t1 + time::Duration::days((300.0 * f2) as i64); let t23 = t2 + time::Duration::days((300.0 * f3) as i64); - assert_eq!(tpk.revocation_status_at(te1), RevocationStatus::NotAsFarAsWeKnow); - assert_eq!(tpk.revocation_status_at(t12), RevocationStatus::NotAsFarAsWeKnow); - match tpk.revocation_status_at(t23) { + assert_eq!(tpk.revocation_status(&te1 as &Policy), + RevocationStatus::NotAsFarAsWeKnow); + assert_eq!(tpk.revocation_status(&t12 as &Policy), + RevocationStatus::NotAsFarAsWeKnow); + match tpk.revocation_status(&t23 as &Policy) { RevocationStatus::Revoked(_) => {} _ => unreachable!(), } - assert_eq!(tpk.revocation_status_at(time::now_utc()), RevocationStatus::NotAsFarAsWeKnow); + assert_eq!(tpk.revocation_status(None), + RevocationStatus::NotAsFarAsWeKnow); } #[test] diff --git a/sqv/src/sqv.rs b/sqv/src/sqv.rs index 873ca9e8..e09dc7c9 100644 --- a/sqv/src/sqv.rs +++ b/sqv/src/sqv.rs @@ -14,7 +14,7 @@ use std::process::exit; use std::fs::File; use std::collections::{HashMap, HashSet}; -use openpgp::{TPK, Packet, packet::Signature, KeyID, RevocationStatus}; +use openpgp::{TPK, Packet, packet::Signature, KeyID, RevocationStatus, Policy}; use openpgp::constants::HashAlgorithm; use openpgp::crypto::Hash; use openpgp::parse::{Parse, PacketParserResult, PacketParser}; @@ -273,7 +273,7 @@ fn real_main() -> Result<(), failure::Error> { } } - if tpk.revocation_status_at(t) + if tpk.revocation_status(&t as &Policy) != RevocationStatus::NotAsFarAsWeKnow { eprintln!( diff --git a/tool/src/commands/inspect.rs b/tool/src/commands/inspect.rs index bb7257de..324c0e03 100644 --- a/tool/src/commands/inspect.rs +++ b/tool/src/commands/inspect.rs @@ -130,7 +130,7 @@ fn inspect_tpk(output: &mut io::Write, tpk: &openpgp::TPK, if tpk.is_tsk() { "Secret" } else { "Public" })?; writeln!(output)?; writeln!(output, " Fingerprint: {}", tpk.fingerprint())?; - inspect_revocation(output, "", tpk.revocation_status())?; + inspect_revocation(output, "", tpk.revocation_status(None))?; inspect_key(output, "", tpk.primary(), tpk.primary_key_signature(), tpk.certifications(), print_keygrips, print_certifications)?; -- cgit v1.2.3