Fix setting a certificate's expiration time.

- When setting a certificate's expiration time, we update each user
    ID's self signature.  If a user ID is revoked, creating a new self
    signature will "unrevoke it."

  - Skip user IDs that are revoked.
This commit is contained in:
Neal H. Walfield 2024-10-07 13:27:16 +02:00
parent 9a2f2bc72b
commit 7292c6c347
No known key found for this signature in database
GPG Key ID: 6863C9AD5B4D22D3
3 changed files with 106 additions and 2 deletions

View File

@ -8,6 +8,7 @@ use openpgp::Packet;
use openpgp::Result;
use openpgp::packet::signature::SignatureBuilder;
use openpgp::serialize::Serialize;
use openpgp::types::RevocationStatus;
use openpgp::types::SignatureType;
use sequoia_cert_store::StoreUpdate;
@ -211,6 +212,14 @@ pub fn expire(sq: Sq,
.into());
for uidb in key.userids() {
if let RevocationStatus::Revoked(_)
= uidb.revocation_status(&policy, sq.time)
{
// The user ID is revoked. Skip it. (Adding a new
// self signature would actually "unrevoke it"!)
continue;
}
// Use the binding signature that is valid under our
// policy as of the reference time. If there is none,
// fall back to the most recent binding signature.

View File

@ -0,0 +1,46 @@
-----BEGIN PGP PRIVATE KEY BLOCK-----
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
K0ERWuHkbzoL
=71x1
-----END PGP PRIVATE KEY BLOCK-----

View File

@ -1,9 +1,11 @@
use std::time::Duration;
use sequoia_openpgp as openpgp;
use openpgp::parse::Parse;
use openpgp::Cert;
use openpgp::cert::amalgamation::ValidAmalgamation;
use openpgp::Result;
use sequoia_openpgp as openpgp;
use openpgp::types::RevocationStatus;
use super::common::STANDARD_POLICY;
use super::common::Sq;
@ -234,7 +236,7 @@ fn unbound_userid() {
None,
updated_path.as_path(),
true)
.expect("sq key expire should suceed");
.expect("sq key expire should succeed");
let vc = updated.with_policy(STANDARD_POLICY, sq.now())
.expect("valid cert");
@ -242,3 +244,50 @@ fn unbound_userid() {
assert_eq!(expiration,
Some(sq.now() + std::time::Duration::new(24 * 60 * 60, 0)));
}
#[test]
fn revoked_userid() {
// Make sure we can extend the expiration time of a certificate
// that includes a revoked user ID (i.e., a user ID without a self
// signature), and make sure we DON'T make the user ID valid.
let sq = Sq::new();
let cert_path = sq.test_data()
.join("keys")
.join("retired-userid.pgp");
let cert = Cert::from_file(&cert_path).expect("can read");
let vc = cert.with_policy(STANDARD_POLICY, sq.now())
.expect("valid cert");
// It shouldn't be expired yet.
assert!(vc.primary_key().key_expiration_time().is_none());
let ua = vc.userids().next().expect("have a user ID");
if let RevocationStatus::Revoked(_) = ua.revocation_status() {
} else {
panic!("User ID should be revoked, but isn't.");
};
// Set it to expire in a day.
let updated_path = sq.scratch_file("updated");
let updated = sq.key_expire(cert_path,
"1d",
None,
updated_path.as_path(),
true)
.expect("sq key expire should succeed");
let vc = updated.with_policy(STANDARD_POLICY, sq.now())
.expect("valid cert");
let expiration = vc.primary_key().key_expiration_time();
assert_eq!(expiration,
Some(sq.now() + std::time::Duration::new(24 * 60 * 60, 0)));
let ua = vc.userids().next().expect("have a user ID");
if let RevocationStatus::Revoked(_) = ua.revocation_status() {
} else {
panic!("User ID should be revoked, but isn't.");
};
}