diff options
-rw-r--r-- | openpgp/src/packet/signature/mod.rs | 2 | ||||
-rw-r--r-- | openpgp/src/parse/stream.rs | 4 | ||||
-rw-r--r-- | openpgp/src/serialize/tpk.rs | 4 | ||||
-rw-r--r-- | openpgp/src/tpk/builder.rs | 8 | ||||
-rw-r--r-- | openpgp/src/tpk/keyiter.rs | 2 | ||||
-rw-r--r-- | openpgp/src/tpk/mod.rs | 151 | ||||
-rw-r--r-- | tool/src/commands/decrypt.rs | 2 | ||||
-rw-r--r-- | tool/src/commands/inspect.rs | 2 |
8 files changed, 103 insertions, 72 deletions
diff --git a/openpgp/src/packet/signature/mod.rs b/openpgp/src/packet/signature/mod.rs index 9894d7a9..f53f3705 100644 --- a/openpgp/src/packet/signature/mod.rs +++ b/openpgp/src/packet/signature/mod.rs @@ -1394,7 +1394,7 @@ mod test { .unwrap(); let test2 = TPK::from_bytes( crate::tests::key("test2-signed-by-test1.pgp")).unwrap(); - let uid_binding = &test2.primary_key_signature_full().unwrap().0.unwrap(); + let uid_binding = &test2.primary_key_signature_full(None).unwrap().0.unwrap(); let cert = &uid_binding.certifications()[0]; assert_eq!(cert.verify_userid_binding(cert_key1, diff --git a/openpgp/src/parse/stream.rs b/openpgp/src/parse/stream.rs index 0d5e8e01..4565db6c 100644 --- a/openpgp/src/parse/stream.rs +++ b/openpgp/src/parse/stream.rs @@ -519,7 +519,7 @@ impl<'a, H: VerificationHelper> Verifier<'a, H> { for (i, tpk) in v.tpks.iter().enumerate() { if can_sign(tpk.primary().key(), - tpk.primary_key_signature(), t) { + tpk.primary_key_signature(None), t) { v.keys.insert(tpk.keyid(), (i, 0)); } @@ -1306,7 +1306,7 @@ impl<'a, H: VerificationHelper + DecryptionHelper> Decryptor<'a, H> { }; if can_sign(tpk.primary().key().into(), - tpk.primary_key_signature()) { + tpk.primary_key_signature(None)) { v.keys.insert(tpk.keyid(), (i, 0)); } diff --git a/openpgp/src/serialize/tpk.rs b/openpgp/src/serialize/tpk.rs index 94b945f9..f020e128 100644 --- a/openpgp/src/serialize/tpk.rs +++ b/openpgp/src/serialize/tpk.rs @@ -744,7 +744,7 @@ mod test { let uid_binding = uid.bind( &mut keypair, &tpk, signature::Builder::from( - tpk.primary_key_signature().unwrap().clone()) + tpk.primary_key_signature(None).unwrap().clone()) .set_type(SignatureType::PositiveCertificate) .set_exportable_certification(false).unwrap(), None, None).unwrap(); @@ -755,7 +755,7 @@ mod test { let ua_binding = ua.bind( &mut keypair, &tpk, signature::Builder::from( - tpk.primary_key_signature().unwrap().clone()) + tpk.primary_key_signature(None).unwrap().clone()) .set_type(SignatureType::PositiveCertificate) .set_exportable_certification(false).unwrap(), None, None).unwrap(); diff --git a/openpgp/src/tpk/builder.rs b/openpgp/src/tpk/builder.rs index 44c9085a..3cf4125b 100644 --- a/openpgp/src/tpk/builder.rs +++ b/openpgp/src/tpk/builder.rs @@ -442,10 +442,10 @@ mod tests { .generate().unwrap(); assert_eq!(tpk.userids().count(), 0); - assert_eq!(tpk.primary_key_signature().unwrap().typ(), + assert_eq!(tpk.primary_key_signature(None).unwrap().typ(), crate::constants::SignatureType::DirectKey); assert_eq!(tpk.subkeys().count(), 3); - if let Some(sig) = tpk.primary_key_signature() { + if let Some(sig) = tpk.primary_key_signature(None) { assert!(sig.features().supports_mdc()); assert!(sig.features().supports_aead()); } else { @@ -481,7 +481,7 @@ mod tests { assert_eq!(tpk1.primary().key().pk_algo(), PublicKeyAlgorithm::EdDSA); assert!(tpk1.subkeys().next().is_none()); - if let Some(sig) = tpk1.primary_key_signature() { + if let Some(sig) = tpk1.primary_key_signature(None) { assert!(sig.features().supports_mdc()); assert!(sig.features().supports_aead()); } else { @@ -524,7 +524,7 @@ mod tests { .primary_keyflags(KeyFlags::default()) .add_encryption_subkey() .generate().unwrap(); - let sig_pkts = &tpk1.primary_key_signature().unwrap().hashed_area(); + let sig_pkts = &tpk1.primary_key_signature(None).unwrap().hashed_area(); match sig_pkts.lookup(SubpacketTag::KeyFlags) { Some(Subpacket{ value: SubpacketValue::KeyFlags(ref ks),.. }) => { diff --git a/openpgp/src/tpk/keyiter.rs b/openpgp/src/tpk/keyiter.rs index e865a176..862aa2be 100644 --- a/openpgp/src/tpk/keyiter.rs +++ b/openpgp/src/tpk/keyiter.rs @@ -95,7 +95,7 @@ impl<'a, P: 'a + key::KeyParts, R: 'a + key::KeyRole> Iterator = if ! self.primary { self.primary = true; - (tpk.primary_key_signature(), + (tpk.primary_key_signature(None), tpk.revocation_status(), tpk.primary().key().into()) } else { diff --git a/openpgp/src/tpk/mod.rs b/openpgp/src/tpk/mod.rs index 40dbc094..a514cce1 100644 --- a/openpgp/src/tpk/mod.rs +++ b/openpgp/src/tpk/mod.rs @@ -218,14 +218,9 @@ impl<C> ComponentBinding<C> { hard_revocations_are_final, selfsig_creation_time.rfc822(), t.rfc822()); - - // XXX: TPK::primary_key_signature returns the most recent - // self signature, not the most recent self signature that was - // valid at time `t`. Enable this assert when that is fixed. - // - // if let Some(selfsig) = selfsig { - // assert!(selfsig.signature_alive_at(t)); - // } + if let Some(selfsig) = selfsig { + assert!(selfsig.signature_alive_at(t)); + } macro_rules! check { ($revs:expr) => ({ @@ -1108,36 +1103,45 @@ impl TPK { .map(|b| b.0) } - /// Returns the primary key's current self-signature and, if the - /// self-signature belongs to a user id (as opposed to a direct - /// signature), a reference to the `UserIDBinding`. + /// Returns the primary key's current self-signature as of `t` and + /// the corresponding User ID binding, if any. + /// + /// The primary key's current self-signature as of `t` is, in + /// order of preference: + /// + /// - The binding signature of the primary User ID at time `t`, + /// if the primary User ID is not revoked at time `t`. + /// + /// - The newest, live, direct self signature at time `t`. + /// + /// - The binding signature of the primary User ID at time `t` + /// (this can only happen if there are only revoked User IDs + /// at time `t`). /// - /// Normally, the primary key's current self-signature is the - /// primary user id's newest, non-revoked self-signature. - /// However, if all user ids are revoked and there is a direct - /// signature, that is returned. If there is no direct signature, - /// then we return the newest self-signature on the most recently - /// revoked user id (i.e., the binding signature that was last - /// valid). If there are no user ids at all and no direct - /// signatures, then we return None. - pub fn primary_key_signature_full(&self) + /// If there are no applicable signatures, `None` is returned. + pub fn primary_key_signature_full<T>(&self, t: T) -> Option<(Option<&UserIDBinding>, &Signature)> + where T: Into<Option<time::Tm>> { - // 1. Self-signature from a non-revoked UserID. - if let Some(userid) = self.userids.get(0) { - if userid.self_revocations.len() == 0 { - return Some((Some(&userid), userid.selfsigs.last()?)); + let t = t.into().unwrap_or_else(time::now_utc); + + // 1. Self-signature from the non-revoked primary UserID. + let primary_userid = self.primary_userid_full(t); + if let Some((ref u, ref s, ref r)) = primary_userid { + if !destructures_to!(RevocationStatus::Revoked(_) = r) { + return Some((Some(u), s)); } } // 2. Direct signature. - if let Some(sig) = self.primary.selfsigs.last() { - return Some((None, sig)); + if let Some(s) = self.primary.binding_signature(t) { + return Some((None, s)); } - // 3. Treat User IDs as if they were not revoked. - if let Some(userid) = self.userids.get(0) { - return Some((Some(&userid), userid.selfsigs.last()?)); + // 3. All User IDs are revoked. + if let Some((u, s, r)) = primary_userid { + assert!(destructures_to!(RevocationStatus::Revoked(_) = r)); + return Some((Some(u), s)); } // 4. No user ids and no direct signatures. @@ -1149,8 +1153,10 @@ impl TPK { /// This function is identical to /// `TPK::primary_key_signature_full()`, but it doesn't return the /// `UserIDBinding`. - pub fn primary_key_signature(&self) -> Option<&Signature> { - if let Some((_, sig)) = self.primary_key_signature_full() { + pub fn primary_key_signature<T>(&self, t: T) -> Option<&Signature> + where T: Into<Option<time::Tm>> + { + if let Some((_, sig)) = self.primary_key_signature_full(t) { Some(sig) } else { None @@ -1175,7 +1181,8 @@ impl TPK { pub fn revocation_status_at<T>(&self, t: T) -> RevocationStatus where T: Into<Option<time::Tm>> { - self.primary._revoked(true, self.primary_key_signature(), t) + let t = t.into(); + self.primary._revoked(true, self.primary_key_signature(t), t) } /// Returns the TPK's current revocation status. @@ -1287,7 +1294,7 @@ 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() { + if let Some(Signature::V4(sig)) = self.primary_key_signature(None) { sig.key_expired(self.primary().key()) } else { false @@ -1296,7 +1303,7 @@ impl TPK { /// Returns whether or not the key is expired at the given time. pub fn expired_at(&self, tm: time::Tm) -> bool { - if let Some(Signature::V4(sig)) = self.primary_key_signature() { + if let Some(Signature::V4(sig)) = self.primary_key_signature(tm) { sig.key_expired_at(self.primary().key(), tm) } else { false @@ -1305,7 +1312,7 @@ impl TPK { /// Returns whether or not the TPK is alive. pub fn alive(&self) -> bool { - if let Some(sig) = self.primary_key_signature() { + if let Some(sig) = self.primary_key_signature(None) { sig.key_alive(self.primary().key()) } else { false @@ -1314,7 +1321,7 @@ impl TPK { /// Returns whether or not the key is alive at the given time. pub fn alive_at(&self, tm: time::Tm) -> bool { - if let Some(sig) = self.primary_key_signature() { + if let Some(sig) = self.primary_key_signature(tm) { sig.key_alive_at(self.primary().key(), tm) } else { false @@ -1336,7 +1343,7 @@ impl TPK { { let sig = { let (userid, template) = self - .primary_key_signature_full() + .primary_key_signature_full(Some(now)) .ok_or(Error::MalformedTPK("No self-signature".into()))?; // Recompute the signature. @@ -1691,7 +1698,7 @@ impl TPK { // certification capable. if ! self.subkeys.is_empty() { let pk_can_certify = - self.primary_key_signature() + self.primary_key_signature(None) .map(|sig| sig.key_flags().can_certify()) .unwrap_or(true); @@ -2286,14 +2293,14 @@ mod test { fn merge_with_incomplete_update() { let tpk = TPK::from_bytes(crate::tests::key("about-to-expire.expired.pgp")) .unwrap(); - assert!(tpk.primary_key_signature().unwrap() + assert!(tpk.primary_key_signature(None).unwrap() .key_expired(tpk.primary().key())); let update = TPK::from_bytes(crate::tests::key("about-to-expire.update-no-uid.pgp")) .unwrap(); let tpk = tpk.merge(update).unwrap(); - assert!(! tpk.primary_key_signature().unwrap() + assert!(! tpk.primary_key_signature(None).unwrap() .key_expired(tpk.primary().key())); } @@ -2352,10 +2359,11 @@ mod test { #[test] fn set_expiry() { let now = time::now_utc(); + let a_sec = time::Duration::seconds(1); let (tpk, _) = TPKBuilder::autocrypt(None, Some("Test")) .generate().unwrap(); - let expiry_orig = tpk.primary_key_signature().unwrap() + let expiry_orig = tpk.primary_key_signature(None).unwrap() .key_expiration_time() .expect("Keys expire by default."); @@ -2363,30 +2371,56 @@ mod test { .into_keypair().unwrap(); // Clear the expiration. + let as_of1 = now + time::Duration::seconds(10); let tpk = tpk.set_expiry_as_of( &mut keypair, None, - now + time::Duration::seconds(10)).unwrap(); + as_of1).unwrap(); { - let expiry = tpk.primary_key_signature().unwrap() - .key_expiration_time(); - assert_eq!(expiry, None); + // If t < as_of1, we should get the original expiry. + assert_eq!(tpk.primary_key_signature(now).unwrap() + .key_expiration_time(), + Some(expiry_orig)); + assert_eq!(tpk.primary_key_signature(as_of1 - a_sec).unwrap() + .key_expiration_time(), + Some(expiry_orig)); + // If t >= as_of1, we should get the new expiry. + assert_eq!(tpk.primary_key_signature(as_of1).unwrap() + .key_expiration_time(), + None); } // Shorten the expiry. (The default expiration should be at // least a few weeks, so removing an hour should still keep us // over 0.) - let expiry_expected = expiry_orig - time::Duration::hours(1); - assert!(expiry_expected > time::Duration::seconds(0)); + let expiry_new = expiry_orig - time::Duration::hours(1); + assert!(expiry_new > time::Duration::seconds(0)); + let as_of2 = as_of1 + time::Duration::seconds(10); let tpk = tpk.set_expiry_as_of( &mut keypair, - Some(expiry_expected), - now + time::Duration::seconds(20)).unwrap(); + Some(expiry_new), + as_of2).unwrap(); { - let expiry = tpk.primary_key_signature().unwrap() - .key_expiration_time(); - assert_eq!(expiry.unwrap(), expiry_expected); + // If t < as_of1, we should get the original expiry. + assert_eq!(tpk.primary_key_signature(now).unwrap() + .key_expiration_time(), + Some(expiry_orig)); + assert_eq!(tpk.primary_key_signature(as_of1 - a_sec).unwrap() + .key_expiration_time(), + Some(expiry_orig)); + // If as_of1 <= t < as_of2, we should get the second + // expiry (None). + assert_eq!(tpk.primary_key_signature(as_of1).unwrap() + .key_expiration_time(), + None); + assert_eq!(tpk.primary_key_signature(as_of2 - a_sec).unwrap() + .key_expiration_time(), + None); + // If t <= as_of2, we should get the new expiry. + assert_eq!(tpk.primary_key_signature(as_of2).unwrap() + .key_expiration_time(), + Some(expiry_new)); } } @@ -2402,7 +2436,8 @@ mod test { tpk1.serialize(&mut buf).unwrap(); let tpk2 = TPK::from_bytes(&buf).unwrap(); - assert_eq!(tpk2.primary_key_signature().unwrap().typ(), SignatureType::DirectKey); + assert_eq!(tpk2.primary_key_signature(None).unwrap().typ(), + SignatureType::DirectKey); assert_eq!(tpk2.userids().count(), 0); } @@ -2412,7 +2447,7 @@ mod test { userid_revoked: bool, subkey_revoked: bool) { // If we have a user id---even if it is revoked---we have // a primary key signature. - let typ = tpk.primary_key_signature().unwrap().typ(); + let typ = tpk.primary_key_signature(None).unwrap().typ(); assert_eq!(typ, SignatureType::PositiveCertificate, "{:#?}", tpk); @@ -2653,11 +2688,7 @@ mod test { assert_eq!(tpk.revocation_status_at(te1), RevocationStatus::NotAsFarAsWeKnow); assert_eq!(tpk.revocation_status_at(t12), RevocationStatus::NotAsFarAsWeKnow); - // XXX: TPK::primary_key_signature returns the most recent - // self signature, not the most recent self signature that was - // valid at time `t`. Reenable this test when that is fixed. - // - // assert_match!(RevocationStatus::Revoked(_) = tpk.revocation_status_at(t23)); + assert_match!(RevocationStatus::Revoked(_) = tpk.revocation_status_at(t23)); assert_eq!(tpk.revocation_status_at(t34), RevocationStatus::NotAsFarAsWeKnow); // Merge in the hard revocation. @@ -2707,7 +2738,7 @@ mod test { let tpk = TPK::from_bytes( crate::tests::key( &format!("really-revoked-{}-0-public.pgp", f))).unwrap(); - let selfsig0 = tpk.primary_key_signature().unwrap() + let selfsig0 = tpk.primary_key_signature(None).unwrap() .signature_creation_time().unwrap(); assert!(!revoked(&tpk, Some(selfsig0))); diff --git a/tool/src/commands/decrypt.rs b/tool/src/commands/decrypt.rs index 48b14e46..9906b583 100644 --- a/tool/src/commands/decrypt.rs +++ b/tool/src/commands/decrypt.rs @@ -58,7 +58,7 @@ impl<'a> Helper<'a> { None => format!("{}", tsk.fingerprint().to_keyid()), }; - if can_encrypt(tsk.primary().key(), tsk.primary_key_signature()) { + if can_encrypt(tsk.primary().key(), tsk.primary_key_signature(None)) { let id = tsk.fingerprint().to_keyid(); keys.insert(id.clone(), tsk.primary().key().clone().into()); identities.insert(id.clone(), tsk.fingerprint()); diff --git a/tool/src/commands/inspect.rs b/tool/src/commands/inspect.rs index 95fd07fa..bec79b1d 100644 --- a/tool/src/commands/inspect.rs +++ b/tool/src/commands/inspect.rs @@ -131,7 +131,7 @@ fn inspect_tpk(output: &mut io::Write, tpk: &openpgp::TPK, writeln!(output)?; writeln!(output, " Fingerprint: {}", tpk.fingerprint())?; inspect_revocation(output, "", tpk.revocation_status())?; - inspect_key(output, "", tpk.primary().key(), tpk.primary_key_signature(), + inspect_key(output, "", tpk.primary().key(), tpk.primary_key_signature(None), tpk.primary().certifications(), print_keygrips, print_certifications)?; writeln!(output)?; |