1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
|
use std::fs;
use std::time::Duration;
use std::time::SystemTime;
use sequoia_openpgp as openpgp;
use openpgp::Packet;
use openpgp::policy::StandardPolicy;
const P: &StandardPolicy = &StandardPolicy::new();
mod common;
use common::Environment;
#[test]
fn expired_certificate() -> anyhow::Result<()> {
if ! Environment::check_for_faketime()? {
// faketime tests are disabled.
return Ok(());
}
// Consider:
//
// Alice is authorized to add commits at time t0.
let t0 = SystemTime::now() - Duration::new(80 * 60, 0);
//
// At t1, she adds a commit (c1).
let t1 = t0 + Duration::new(30 * 60, 0);
// At t2, her certificate expires.
//
// Note: This corresponds to the current time, because
// set_expiration_time doesn't let us override the signature
// creation time. See:
//
// https://gitlab.com/sequoia-pgp/sequoia/-/issues/1154
let t2 = t1 + Duration::new(50 * 60, 0);
//
// At t3, she adds a commit (c2) that updates her certificate.
let t3 = t2 + Duration::new(70 * 60, 0);
let future = t3 + Duration::new(110 * 60, 0);
//
// The policy in c1 is used to authenticate c2, but the
// certificate in c1's policy is expired. In this case, the
// certificates from c2's policy should be used to update the
// certificates in c1's policy.
let mut e = Environment::at(t0.clone())?;
let p = e.git_state();
let (alice, alice_pgp) = e.gen(
"alice",
t0.clone(),
t2.duration_since(t1.clone()).expect("valid"));
let alice_fpr = &alice.fingerprint().to_string()[..];
e.sq_git(&[
"policy",
"authorize",
"alice",
"--cert-file", &alice_pgp,
"--sign-commit"
])?;
e.git(&["add", "openpgp-policy.toml"])?;
e.git(&[
"commit",
"-m", "Initial commit (@ t0).",
])?;
let root = e.git_current_commit()?;
e.git(&["log"])?;
e.sq_git(&["log", "--trust-root", &root])?;
// Add another commit at t1.
e.time(t1);
fs::write(p.join("2"), "2.")?;
e.git(&["add", "2"])?;
e.git(&[
"commit",
"-m", "@ t1",
&format!("-S{}", alice_fpr),
])?;
let c2 = e.git_current_commit()?;
e.git(&["log"])?;
e.sq_git(&["log", "--trust-root", &root, &c2])?;
// Try adding a commit after her certificate expires. This should fail.
e.time(t3);
fs::write(p.join("3"), "3.")?;
e.git(&["add", "3"])?;
assert!(e.git(&[
"commit",
"-m", "@ t3.",
&format!("-S{}", alice_fpr),
]).is_err());
// Extend Alice's expiration.
let (alice2, _) = {
let vc = alice.with_policy(P, e.time.clone()).expect("valid cert");
let mut primary_signer = alice.primary_key().key().clone()
.parts_into_secret()?.into_keypair()?;
// We really want to set the new signatures' creation time to t2
let mut packets = Vec::new();
for ka in vc.keys() {
let mut subkey_signer = if ka.for_signing() {
Some(ka.key().clone().parts_into_secret()?.into_keypair()?)
} else {
None
};
let sigs = ka.set_expiration_time(
&mut primary_signer,
if let Some(subkey_signer) = subkey_signer.as_mut() {
Some(subkey_signer)
} else {
None
},
Some(future.clone()))
.expect(&format!("can update expiration of {}", ka.key().fingerprint()));
if ka.key().fingerprint() == alice.fingerprint() {
packets.push(Packet::from(alice.primary_key().key().clone()));
} else {
packets.push(Packet::from(ka.key().clone().role_into_subordinate()));
}
packets.extend(sigs.into_iter().map(|sig| Packet::from(sig)));
}
alice.clone().insert_packets(packets).expect("can insert packets")
};
e.import(&alice2).expect("can import");
// Now we should be able to create the commit.
assert!(e.git(&[
"commit",
"-m", "@ t3.",
&format!("-S{}", alice_fpr),
]).is_ok());
let c3_1 = e.git_current_commit()?;
// But we can't verify it, because the certificate in the policy
// is expired!
e.git(&["log"])?;
assert!(e.sq_git(&["log", "--trust-root", &root, &c3_1]).is_err());
// Reset to c2.
e.git(&["reset", "--hard", &c2])?;
assert_eq!(c2, e.git_current_commit()?);
// Update the policy (by looking in the local certificate store).
assert!(e.sq_git(&["policy", "sync", "--disable-keyservers"]).is_ok());
e.git(&["add", "openpgp-policy.toml"])?;
// Now we should be able to create the commit.
assert!(e.git(&[
"commit",
"-m", "@ t3 (try two).",
&format!("-S{}", alice_fpr),
]).is_ok());
let c3_2 = e.git_current_commit()?;
// And we can verify it, because the updated certificate is in the
// new commit's policy file.
e.git(&["log"])?;
assert!(e.sq_git(&["log", "--trust-root", &root, &c3_2]).is_ok());
Ok(())
}
|