91f4400c26
- Resolves a conflict with the user ID designators, and makes the interface more consistent. - Fixes #385.
295 lines
9.7 KiB
Rust
295 lines
9.7 KiB
Rust
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 openpgp::types::RevocationStatus;
|
|
|
|
use super::common::STANDARD_POLICY;
|
|
use super::common::Sq;
|
|
use super::common::time_as_string;
|
|
use super::common::UserIDArg;
|
|
|
|
#[test]
|
|
fn sq_key_expire() -> Result<()> {
|
|
for keystore in [false, true] {
|
|
let mut sq = Sq::new();
|
|
|
|
let (cert, cert_path, _rev_path)
|
|
= sq.key_generate(&[], &["alice <alice@example.org>"]);
|
|
let fpr = cert.fingerprint().to_string();
|
|
|
|
// Two days go by.
|
|
sq.tick(2 * 24 * 60 * 60);
|
|
|
|
let updated_path = sq.scratch_file("updated.pgp");
|
|
let updated2_path = sq.scratch_file("updated2.pgp");
|
|
|
|
if keystore {
|
|
sq.key_import(&cert_path);
|
|
}
|
|
|
|
// Change the key to expire in one day.
|
|
let mut cmd = sq.command();
|
|
cmd.args(["key", "expire", "--expiration", "1d"]);
|
|
if keystore {
|
|
cmd.args(["--cert", &fpr ]);
|
|
} else {
|
|
cmd
|
|
.arg("--overwrite")
|
|
.arg("--cert-file").arg(&cert_path)
|
|
.arg("--output").arg(&updated_path);
|
|
}
|
|
sq.run(cmd, true);
|
|
|
|
let updated = if keystore {
|
|
eprintln!("Updated certificate to expire in one day:\n{}",
|
|
sq.inspect(cert.key_handle()));
|
|
|
|
sq.cert_export(cert.key_handle())
|
|
} else {
|
|
eprintln!("Updated certificate to expire in one day:\n{}",
|
|
sq.inspect(&updated_path));
|
|
|
|
Cert::from_file(&updated_path).expect("valid cert")
|
|
};
|
|
|
|
// It should be alive now.
|
|
let vc = updated.with_policy(STANDARD_POLICY, sq.now()).expect("valid");
|
|
assert!(matches!(vc.alive(), Ok(())));
|
|
|
|
// It should be alive in 1 day minus 1 second.
|
|
let t = sq.now() + Duration::new(24 * 60 * 60 - 1, 0);
|
|
eprintln!("Checking expiration status at {}", time_as_string(t.into()));
|
|
let vc = updated.with_policy(STANDARD_POLICY, t).expect("valid");
|
|
assert!(matches!(vc.alive(), Ok(())));
|
|
|
|
// But in exactly one day, it should be expired.
|
|
let t = sq.now() + Duration::new(24 * 60 * 60, 0);
|
|
eprintln!("Checking expiration status at {}", time_as_string(t.into()));
|
|
let vc = updated.with_policy(STANDARD_POLICY, t).expect("valid");
|
|
assert!(matches!(vc.alive(), Err(_)));
|
|
|
|
// 12 hours go by. Clear the expiration time.
|
|
sq.tick(12 * 60 * 60);
|
|
|
|
let mut cmd = sq.command();
|
|
cmd.args([ "key", "expire", "--expiration", "never" ]);
|
|
if keystore {
|
|
cmd.args([ "--cert", &fpr ]);
|
|
} else {
|
|
cmd.args([
|
|
"--cert-file", &updated_path.to_string_lossy(),
|
|
"--output", &updated2_path.to_string_lossy(),
|
|
]);
|
|
}
|
|
sq.run(cmd, true);
|
|
|
|
let updated = if keystore {
|
|
eprintln!("Updated certificate to expire in one day:\n{}",
|
|
sq.inspect(cert.key_handle()));
|
|
|
|
sq.cert_export(cert.key_handle())
|
|
} else {
|
|
eprintln!("Updated certificate to expire in one day:\n{}",
|
|
sq.inspect(&updated2_path));
|
|
|
|
Cert::from_file(&updated2_path).expect("valid cert")
|
|
};
|
|
|
|
// It should be alive now.
|
|
let vc = updated.with_policy(STANDARD_POLICY, None)
|
|
.expect("valid");
|
|
assert!(matches!(vc.alive(), Ok(())));
|
|
|
|
// It should be alive in 1 day minus 1 second.
|
|
let vc = updated.with_policy(
|
|
STANDARD_POLICY,
|
|
sq.now() + Duration::new(24 * 60 * 60 - 1, 0))
|
|
.expect("valid");
|
|
assert!(matches!(vc.alive(), Ok(())));
|
|
|
|
// And in exactly one day...
|
|
let vc = updated.with_policy(
|
|
STANDARD_POLICY,
|
|
sq.now() + Duration::new(24 * 60 * 60, 0))
|
|
.expect("valid");
|
|
assert!(matches!(vc.alive(), Ok(())));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Tests changing the expiration time of a key without direct key
|
|
/// signature.
|
|
///
|
|
/// These kind of keys are generated by GnuPG.
|
|
#[test]
|
|
fn sq_key_expire_no_direct_key_sig() -> Result<()> {
|
|
use openpgp::{
|
|
packet::{Any, Signature},
|
|
serialize::Serialize,
|
|
types::SignatureType,
|
|
};
|
|
|
|
let mut sq = Sq::new();
|
|
let (cert, _cert_path, _rev_path)
|
|
= sq.key_generate(&[], &[UserIDArg::Email("alice@example.org")]);
|
|
let fipr = cert.fingerprint().to_string();
|
|
|
|
|
|
// Strip the direct key signature.
|
|
let mut p = cert.as_tsk().into_packets().collect::<Vec<_>>();
|
|
p.retain(|p| Any::<Signature>::downcast_ref(p)
|
|
.map(|sig| sig.typ() != SignatureType::DirectKey)
|
|
.unwrap_or(true));
|
|
let cert = Cert::from_packets(p.into_iter())?;
|
|
|
|
// Two days go by.
|
|
sq.tick(2 * 24 * 60 * 60);
|
|
|
|
let cert_path = sq.scratch_file("updated.pgp");
|
|
cert.as_tsk().serialize(&mut std::fs::File::create(&cert_path)?)?;
|
|
sq.key_import(&cert_path);
|
|
|
|
// Change the key to expire in one day.
|
|
let mut cmd = sq.command();
|
|
cmd.args(["key", "expire", "--expiration", "1d", "--cert", &fipr]);
|
|
sq.run(cmd, true);
|
|
|
|
eprintln!("Updated certificate to expire in one day:\n{}",
|
|
sq.inspect(cert.key_handle()));
|
|
let updated = sq.cert_export(cert.key_handle());
|
|
|
|
// It should be alive now.
|
|
let vc = updated.with_policy(STANDARD_POLICY, sq.now()).expect("valid");
|
|
assert!(matches!(vc.alive(), Ok(())));
|
|
|
|
// It should be alive in 1 day minus 1 second.
|
|
let t = sq.now() + Duration::new(24 * 60 * 60 - 1, 0);
|
|
eprintln!("Checking expiration status at {}", time_as_string(t.into()));
|
|
let vc = updated.with_policy(STANDARD_POLICY, t).expect("valid");
|
|
assert!(matches!(vc.alive(), Ok(())));
|
|
|
|
// But in exactly one day, it should be expired.
|
|
let t = sq.now() + Duration::new(24 * 60 * 60, 0);
|
|
eprintln!("Checking expiration status at {}", time_as_string(t.into()));
|
|
let vc = updated.with_policy(STANDARD_POLICY, t).expect("valid");
|
|
assert!(matches!(vc.alive(), Err(_)));
|
|
|
|
// 12 hours go by. Clear the expiration time.
|
|
sq.tick(12 * 60 * 60);
|
|
|
|
let mut cmd = sq.command();
|
|
cmd.args(["key", "expire", "--expiration", "never", "--cert", &fipr]);
|
|
sq.run(cmd, true);
|
|
|
|
eprintln!("Updated certificate to expire in one day:\n{}",
|
|
sq.inspect(cert.key_handle()));
|
|
let updated = sq.cert_export(cert.key_handle());
|
|
|
|
// It should be alive now.
|
|
let vc = updated.with_policy(STANDARD_POLICY, None)
|
|
.expect("valid");
|
|
assert!(matches!(vc.alive(), Ok(())));
|
|
|
|
// It should be alive in 1 day minus 1 second.
|
|
let vc = updated.with_policy(
|
|
STANDARD_POLICY,
|
|
sq.now() + Duration::new(24 * 60 * 60 - 1, 0))
|
|
.expect("valid");
|
|
assert!(matches!(vc.alive(), Ok(())));
|
|
|
|
// And in exactly one day...
|
|
let vc = updated.with_policy(
|
|
STANDARD_POLICY,
|
|
sq.now() + Duration::new(24 * 60 * 60, 0))
|
|
.expect("valid");
|
|
assert!(matches!(vc.alive(), Ok(())));
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn unbound_userid() {
|
|
// Make sure we can extend the expiration time of a certificate
|
|
// that includes an unbound user ID (i.e., a user ID without a
|
|
// self signature).
|
|
|
|
let sq = Sq::new();
|
|
|
|
let cert_path = sq.test_data()
|
|
.join("keys")
|
|
.join("unbound-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());
|
|
|
|
// 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)));
|
|
}
|
|
|
|
|
|
#[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.");
|
|
};
|
|
}
|