diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2019-12-13 14:25:32 +0100 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2019-12-13 20:57:51 +0100 |
commit | d84d1c3fa460f814f400ffed42897601da525e69 (patch) | |
tree | c4597230f00411ae243c8f11b25901b8a8f3436a | |
parent | c11e74020f374888caf9be63ec300bcbd1ff24b0 (diff) |
sqv: Improve tests.
- Test all kinds of revocations, test signature that predates the
primary key. Same with a subkey.
27 files changed, 402 insertions, 65 deletions
diff --git a/sqv/tests/data/revoked-key-cert-not-revoked.pgp b/sqv/tests/data/revoked-key-cert-not-revoked.pgp Binary files differnew file mode 100644 index 00000000..b54bd712 --- /dev/null +++ b/sqv/tests/data/revoked-key-cert-not-revoked.pgp diff --git a/sqv/tests/data/revoked-key-cert-revoked-compromised.pgp b/sqv/tests/data/revoked-key-cert-revoked-compromised.pgp Binary files differnew file mode 100644 index 00000000..97ddd18e --- /dev/null +++ b/sqv/tests/data/revoked-key-cert-revoked-compromised.pgp diff --git a/sqv/tests/data/revoked-key-cert-revoked-compromised.sk.pgp b/sqv/tests/data/revoked-key-cert-revoked-compromised.sk.pgp Binary files differnew file mode 100644 index 00000000..ad6cf793 --- /dev/null +++ b/sqv/tests/data/revoked-key-cert-revoked-compromised.sk.pgp diff --git a/sqv/tests/data/revoked-key-cert-revoked-key_retired.pgp b/sqv/tests/data/revoked-key-cert-revoked-key_retired.pgp Binary files differnew file mode 100644 index 00000000..f16ab029 --- /dev/null +++ b/sqv/tests/data/revoked-key-cert-revoked-key_retired.pgp diff --git a/sqv/tests/data/revoked-key-cert-revoked-key_retired.sk.pgp b/sqv/tests/data/revoked-key-cert-revoked-key_retired.sk.pgp Binary files differnew file mode 100644 index 00000000..95762735 --- /dev/null +++ b/sqv/tests/data/revoked-key-cert-revoked-key_retired.sk.pgp diff --git a/sqv/tests/data/revoked-key-cert-revoked-no_subpacket.pgp b/sqv/tests/data/revoked-key-cert-revoked-no_subpacket.pgp Binary files differnew file mode 100644 index 00000000..d7c2ecc5 --- /dev/null +++ b/sqv/tests/data/revoked-key-cert-revoked-no_subpacket.pgp diff --git a/sqv/tests/data/revoked-key-cert-revoked-no_subpacket.sk.pgp b/sqv/tests/data/revoked-key-cert-revoked-no_subpacket.sk.pgp Binary files differnew file mode 100644 index 00000000..9c554046 --- /dev/null +++ b/sqv/tests/data/revoked-key-cert-revoked-no_subpacket.sk.pgp diff --git a/sqv/tests/data/revoked-key-cert-revoked-private.pgp b/sqv/tests/data/revoked-key-cert-revoked-private.pgp Binary files differnew file mode 100644 index 00000000..9fc6568c --- /dev/null +++ b/sqv/tests/data/revoked-key-cert-revoked-private.pgp diff --git a/sqv/tests/data/revoked-key-cert-revoked-private.sk.pgp b/sqv/tests/data/revoked-key-cert-revoked-private.sk.pgp Binary files differnew file mode 100644 index 00000000..528732ec --- /dev/null +++ b/sqv/tests/data/revoked-key-cert-revoked-private.sk.pgp diff --git a/sqv/tests/data/revoked-key-cert-revoked-superseded.pgp b/sqv/tests/data/revoked-key-cert-revoked-superseded.pgp Binary files differnew file mode 100644 index 00000000..b9debcf9 --- /dev/null +++ b/sqv/tests/data/revoked-key-cert-revoked-superseded.pgp diff --git a/sqv/tests/data/revoked-key-cert-revoked-superseded.sk.pgp b/sqv/tests/data/revoked-key-cert-revoked-superseded.sk.pgp Binary files differnew file mode 100644 index 00000000..cae4e042 --- /dev/null +++ b/sqv/tests/data/revoked-key-cert-revoked-superseded.sk.pgp diff --git a/sqv/tests/data/revoked-key-cert-revoked-uid_retired.pgp b/sqv/tests/data/revoked-key-cert-revoked-uid_retired.pgp Binary files differnew file mode 100644 index 00000000..f54fe768 --- /dev/null +++ b/sqv/tests/data/revoked-key-cert-revoked-uid_retired.pgp diff --git a/sqv/tests/data/revoked-key-cert-revoked-uid_retired.sk.pgp b/sqv/tests/data/revoked-key-cert-revoked-uid_retired.sk.pgp Binary files differnew file mode 100644 index 00000000..910e89fc --- /dev/null +++ b/sqv/tests/data/revoked-key-cert-revoked-uid_retired.sk.pgp diff --git a/sqv/tests/data/revoked-key-cert-revoked-unknown.pgp b/sqv/tests/data/revoked-key-cert-revoked-unknown.pgp Binary files differnew file mode 100644 index 00000000..3f896d7a --- /dev/null +++ b/sqv/tests/data/revoked-key-cert-revoked-unknown.pgp diff --git a/sqv/tests/data/revoked-key-cert-revoked-unknown.sk.pgp b/sqv/tests/data/revoked-key-cert-revoked-unknown.sk.pgp Binary files differnew file mode 100644 index 00000000..9950548b --- /dev/null +++ b/sqv/tests/data/revoked-key-cert-revoked-unknown.sk.pgp diff --git a/sqv/tests/data/revoked-key-cert-revoked-unspecified.pgp b/sqv/tests/data/revoked-key-cert-revoked-unspecified.pgp Binary files differnew file mode 100644 index 00000000..2c5e53df --- /dev/null +++ b/sqv/tests/data/revoked-key-cert-revoked-unspecified.pgp diff --git a/sqv/tests/data/revoked-key-cert-revoked-unspecified.sk.pgp b/sqv/tests/data/revoked-key-cert-revoked-unspecified.sk.pgp Binary files differnew file mode 100644 index 00000000..358eaf65 --- /dev/null +++ b/sqv/tests/data/revoked-key-cert-revoked-unspecified.sk.pgp diff --git a/sqv/tests/data/revoked-key-keyring.pgp b/sqv/tests/data/revoked-key-keyring.pgp Binary files differdeleted file mode 100644 index 6c49e422..00000000 --- a/sqv/tests/data/revoked-key-keyring.pgp +++ /dev/null diff --git a/sqv/tests/data/revoked-key-sig-t0.pgp b/sqv/tests/data/revoked-key-sig-t0.pgp Binary files differnew file mode 100644 index 00000000..a2d5d733 --- /dev/null +++ b/sqv/tests/data/revoked-key-sig-t0.pgp diff --git a/sqv/tests/data/revoked-key-sig-t0.sk.pgp b/sqv/tests/data/revoked-key-sig-t0.sk.pgp Binary files differnew file mode 100644 index 00000000..83dcea08 --- /dev/null +++ b/sqv/tests/data/revoked-key-sig-t0.sk.pgp diff --git a/sqv/tests/data/revoked-key-sig-t1-t2.pgp b/sqv/tests/data/revoked-key-sig-t1-t2.pgp Binary files differindex 4e9135bb..63ed6170 100644 --- a/sqv/tests/data/revoked-key-sig-t1-t2.pgp +++ b/sqv/tests/data/revoked-key-sig-t1-t2.pgp diff --git a/sqv/tests/data/revoked-key-sig-t1-t2.sk.pgp b/sqv/tests/data/revoked-key-sig-t1-t2.sk.pgp Binary files differnew file mode 100644 index 00000000..2f81ca3d --- /dev/null +++ b/sqv/tests/data/revoked-key-sig-t1-t2.sk.pgp diff --git a/sqv/tests/data/revoked-key-sig-t2-t3.pgp b/sqv/tests/data/revoked-key-sig-t2-t3.pgp Binary files differindex e1476136..2e2f5611 100644 --- a/sqv/tests/data/revoked-key-sig-t2-t3.pgp +++ b/sqv/tests/data/revoked-key-sig-t2-t3.pgp diff --git a/sqv/tests/data/revoked-key-sig-t2-t3.sk.pgp b/sqv/tests/data/revoked-key-sig-t2-t3.sk.pgp Binary files differnew file mode 100644 index 00000000..1b42061d --- /dev/null +++ b/sqv/tests/data/revoked-key-sig-t2-t3.sk.pgp diff --git a/sqv/tests/data/revoked-key-sig-t3-now.pgp b/sqv/tests/data/revoked-key-sig-t3-now.pgp Binary files differindex 9946a51e..e3541c6e 100644 --- a/sqv/tests/data/revoked-key-sig-t3-now.pgp +++ b/sqv/tests/data/revoked-key-sig-t3-now.pgp diff --git a/sqv/tests/data/revoked-key-sig-t3-now.sk.pgp b/sqv/tests/data/revoked-key-sig-t3-now.sk.pgp Binary files differnew file mode 100644 index 00000000..338af8b1 --- /dev/null +++ b/sqv/tests/data/revoked-key-sig-t3-now.sk.pgp diff --git a/sqv/tests/revoked-key.rs b/sqv/tests/revoked-key.rs index bc1748cf..fdfe24b5 100644 --- a/sqv/tests/revoked-key.rs +++ b/sqv/tests/revoked-key.rs @@ -1,3 +1,43 @@ +//! Tests revocations and binding signatures over time. +//! +//! These tests create a certificate with a signing capable primary +//! key (subkey), and revoke it later on, then re-legitimize it using +//! a new signature. We then ask sqv to verify a signature at +//! different points in time. Hard revocations of the key invalidate +//! the signature at any point in time, whereas in the case of soft +//! revocations, the keys can be re-legitimized. +//! +//! All tests are run in three flavors: +//! +//! 0. The primary key makes the signatures and is revoked. +//! 1. The subkey makes the signatures, primary key is revoked. +//! 2. The subkey makes the signatures and is revoked. +//! +//! As extra subtlety, we bind the subkey *after* the t1-t2 signature. +//! +//! Timeline: v +//! | +//! t0 -| - Signature revoked-key-sig-t0.pgp +//! | +//! t1 -| - Primary key creation +//! | +//! | - Subkey creation +//! | +//! | - Signature revoked-key-sig-t1-t2.pgp +//! | +//! | - Subkey is bound +//! | +//! t2 -| - Revocation of (sub)key +//! | +//! | - Signature revoked-key-sig-t2-t3.pgp +//! | +//! t3 -| - New direct/binding signature +//! | +//! | - Signature revoked-key-sig-t3-now.pgp +//! | +//! now -| +//! v + extern crate assert_cli; #[cfg(test)] @@ -5,48 +45,204 @@ mod integration { use assert_cli::Assert; use std::path; - #[test] - fn not_valid_at_signature_ctime() { - // A hard revocation is never ignored. + fn sqv(keyring: &str, sig: &str) -> Assert { Assert::cargo_binary("sqv") .current_dir(path::Path::new("tests").join("data")) .with_args( - &["--keyring", - &"revoked-key-keyring.pgp", - &"revoked-key-sig-t1-t2.pgp", - &"msg.txt"]) - .fails() - .and().stderr().contains("revoked") - .unwrap(); + &["--trace", + "--keyring", + &format!("revoked-key-cert-{}.pgp", keyring), + &format!("revoked-key-sig-{}.pgp", sig), + "msg.txt"]) + } + + /// Tests flavor 0, primary key signs and is revoked. + fn f0(keyring: &str, sig: &str) -> Assert { + sqv(keyring, sig) + } + + /// Tests flavor 1, subkey signs and primary key is revoked. + fn f1(keyring: &str, sig: &str) -> Assert { + sqv(keyring, &format!("{}.sk", sig)) } + /// Tests flavor 2, subkey signs and is revoked. + fn f2(keyring: &str, sig: &str) -> Assert { + sqv(&format!("{}.sk", keyring), &format!("{}.sk", sig)) + } + + /// Base case, cert is not revoked. #[test] - fn revoked_at_signature_ctime() { - Assert::cargo_binary("sqv") - .current_dir(path::Path::new("tests").join("data")) - .with_args( - &["--keyring", - &"revoked-key-keyring.pgp", - &"revoked-key-sig-t2-t3.pgp", - &"msg.txt"]) - .fails() - .and().stderr().contains("revoked") - .unwrap(); + fn not_revoked() { + let c = "not-revoked"; + f0(c, "t0").fails().and().stderr().contains("predates").unwrap(); + f0(c, "t1-t2").unwrap(); + f0(c, "t2-t3").unwrap(); + f0(c, "t3-now").unwrap(); + + f1(c, "t0").fails().and().stderr().contains("predates").unwrap(); + f1(c, "t1-t2").fails().unwrap(); + f1(c, "t2-t3").unwrap(); + f1(c, "t3-now").unwrap(); + + // f2 is not used here, because we don't have any revocations. } + /// The hard revocation reasons. All signatures are invalid. #[test] - fn unrevoked() { - // Hard revocations are never ignored. - Assert::cargo_binary("sqv") - .current_dir(path::Path::new("tests").join("data")) - .with_args( - &["--keyring", - &"revoked-key-keyring.pgp", - &"revoked-key-sig-t3-now.pgp", - &"msg.txt"]) - .fails() - .and().stderr().contains("revoked") - .unwrap(); + fn revoked_no_subpacket() { + let c = "revoked-no_subpacket"; + f0(c, "t0").fails().unwrap(); + f0(c, "t1-t2").fails().and().stderr().contains("revoked").unwrap(); + f0(c, "t2-t3").fails().and().stderr().contains("revoked").unwrap(); + f0(c, "t3-now").fails().and().stderr().contains("revoked").unwrap(); + + f1(c, "t0").fails().unwrap(); + f1(c, "t1-t2").fails().unwrap(); + f1(c, "t2-t3").fails().and().stderr().contains("revoked").unwrap(); + f1(c, "t3-now").fails().and().stderr().contains("revoked").unwrap(); + + f2(c, "t0").fails().unwrap(); + f2(c, "t1-t2").fails().unwrap(); + f2(c, "t2-t3").fails().and().stderr().contains("revoked").unwrap(); + f2(c, "t3-now").fails().and().stderr().contains("revoked").unwrap(); + } + + #[test] + fn revoked_unspecified() { + let c = "revoked-unspecified"; + f0(c, "t0").fails().unwrap(); + f0(c, "t1-t2").fails().and().stderr().contains("revoked").unwrap(); + f0(c, "t2-t3").fails().and().stderr().contains("revoked").unwrap(); + f0(c, "t3-now").fails().and().stderr().contains("revoked").unwrap(); + + f1(c, "t0").fails().unwrap(); + f1(c, "t1-t2").fails().unwrap(); + f1(c, "t2-t3").fails().and().stderr().contains("revoked").unwrap(); + f1(c, "t3-now").fails().and().stderr().contains("revoked").unwrap(); + + f2(c, "t0").fails().unwrap(); + f2(c, "t1-t2").fails().unwrap(); + f2(c, "t2-t3").fails().and().stderr().contains("revoked").unwrap(); + f2(c, "t3-now").fails().and().stderr().contains("revoked").unwrap(); + } + + #[test] + fn revoked_compromised() { + let c = "revoked-compromised"; + f0(c, "t0").fails().unwrap(); + f0(c, "t1-t2").fails().and().stderr().contains("revoked").unwrap(); + f0(c, "t2-t3").fails().and().stderr().contains("revoked").unwrap(); + f0(c, "t3-now").fails().and().stderr().contains("revoked").unwrap(); + + f1(c, "t0").fails().unwrap(); + f1(c, "t1-t2").fails().unwrap(); + f1(c, "t2-t3").fails().and().stderr().contains("revoked").unwrap(); + f1(c, "t3-now").fails().and().stderr().contains("revoked").unwrap(); + + f2(c, "t0").fails().unwrap(); + f2(c, "t1-t2").fails().unwrap(); + f2(c, "t2-t3").fails().and().stderr().contains("revoked").unwrap(); + f2(c, "t3-now").fails().and().stderr().contains("revoked").unwrap(); + } + + #[test] + fn revoked_private() { + let c = "revoked-private"; + f0(c, "t0").fails().unwrap(); + f0(c, "t1-t2").fails().and().stderr().contains("revoked").unwrap(); + f0(c, "t2-t3").fails().and().stderr().contains("revoked").unwrap(); + f0(c, "t3-now").fails().and().stderr().contains("revoked").unwrap(); + + f1(c, "t0").fails().unwrap(); + f1(c, "t1-t2").fails().unwrap(); + f1(c, "t2-t3").fails().and().stderr().contains("revoked").unwrap(); + f1(c, "t3-now").fails().and().stderr().contains("revoked").unwrap(); + + f2(c, "t0").fails().unwrap(); + f2(c, "t1-t2").fails().unwrap(); + f2(c, "t2-t3").fails().and().stderr().contains("revoked").unwrap(); + f2(c, "t3-now").fails().and().stderr().contains("revoked").unwrap(); + } + + #[test] + fn revoked_unknown() { + let c = "revoked-unknown"; + f0(c, "t0").fails().unwrap(); + f0(c, "t1-t2").fails().and().stderr().contains("revoked").unwrap(); + f0(c, "t2-t3").fails().and().stderr().contains("revoked").unwrap(); + f0(c, "t3-now").fails().and().stderr().contains("revoked").unwrap(); + + f1(c, "t0").fails().unwrap(); + f1(c, "t1-t2").fails().unwrap(); + f1(c, "t2-t3").fails().and().stderr().contains("revoked").unwrap(); + f1(c, "t3-now").fails().and().stderr().contains("revoked").unwrap(); + + f2(c, "t0").fails().unwrap(); + f2(c, "t1-t2").fails().unwrap(); + f2(c, "t2-t3").fails().and().stderr().contains("revoked").unwrap(); + f2(c, "t3-now").fails().and().stderr().contains("revoked").unwrap(); + } + + /// The soft revocation reasons. Only the signature dated prior + /// to the key creation and the one directly after the revocation + /// are invalid. + #[test] + fn revoked_superseded() { + let c = "revoked-superseded"; + f0(c, "t0").fails().and().stderr().contains("predates").unwrap(); + f0(c, "t1-t2").unwrap(); + f0(c, "t2-t3").fails().and().stderr().contains("revoked").unwrap(); + f0(c, "t3-now").unwrap(); + + f1(c, "t0").fails().and().stderr().contains("predates").unwrap(); + f1(c, "t1-t2").fails().unwrap(); + f1(c, "t2-t3").fails().and().stderr().contains("revoked").unwrap(); + f1(c, "t3-now").unwrap(); + + f2(c, "t0").fails().and().stderr().contains("predates").unwrap(); + f2(c, "t1-t2").fails().unwrap(); + f2(c, "t2-t3").fails().and().stderr().contains("revoked").unwrap(); + f2(c, "t3-now").unwrap(); + } + + #[test] + fn revoked_key_retired() { + let c = "revoked-key_retired"; + f0(c, "t0").fails().and().stderr().contains("predates").unwrap(); + f0(c, "t1-t2").unwrap(); + f0(c, "t2-t3").fails().and().stderr().contains("revoked").unwrap(); + f0(c, "t3-now").unwrap(); + + f1(c, "t0").fails().and().stderr().contains("predates").unwrap(); + f1(c, "t1-t2").fails().unwrap(); + f1(c, "t2-t3").fails().and().stderr().contains("revoked").unwrap(); + f1(c, "t3-now").unwrap(); + + f2(c, "t0").fails().and().stderr().contains("predates").unwrap(); + f2(c, "t1-t2").fails().unwrap(); + f2(c, "t2-t3").fails().and().stderr().contains("revoked").unwrap(); + f2(c, "t3-now").unwrap(); + } + + /// XXX: This is an odd one. + #[test] + fn revoked_uid_retired() { + let c = "revoked-uid_retired"; + f0(c, "t0").fails().and().stderr().contains("predates").unwrap(); + f0(c, "t1-t2").unwrap(); + f0(c, "t2-t3").fails().and().stderr().contains("revoked").unwrap(); + f0(c, "t3-now").unwrap(); + + f1(c, "t0").fails().and().stderr().contains("predates").unwrap(); + f1(c, "t1-t2").fails().unwrap(); + f1(c, "t2-t3").fails().and().stderr().contains("revoked").unwrap(); + f1(c, "t3-now").unwrap(); + + f2(c, "t0").fails().and().stderr().contains("predates").unwrap(); + f2(c, "t1-t2").fails().unwrap(); + f2(c, "t2-t3").fails().and().stderr().contains("revoked").unwrap(); + f2(c, "t3-now").unwrap(); } } @@ -65,6 +261,7 @@ fn create_key() { key::{ Key4, PrimaryRole, + SubordinateRole, }, }, serialize::Serialize, @@ -74,55 +271,181 @@ fn create_key() { KeyFlags, SignatureType, HashAlgorithm, + ReasonForRevocation, } }; use chrono::offset::TimeZone; let msg = b"Hello, World"; + let t0 = chrono::offset::Utc.timestamp(915145200, 0); // 1999-01-01 let t1 = chrono::offset::Utc.timestamp(946681200, 0); // 2000-01-01 let t2 = chrono::offset::Utc.timestamp(978303600, 0); // 2001-01-01 let t3 = chrono::offset::Utc.timestamp(1009839600, 0); // 2002-01-01 let f1: f32 = 0.4; // Chosen by fair dice roll. let f2: f32 = 0.7; // Likewise. let t12 = t1 + chrono::Duration::days((300.0 * f1) as i64); + let t_sk_binding = t12 + chrono::Duration::days(1); let t23 = t2 + chrono::Duration::days((300.0 * f2) as i64); + + // Create primary key. let mut key: Key<_, PrimaryRole> = Key4::generate_ecc(true, Curve::Ed25519).unwrap().into(); key.set_creation_time(t1).unwrap(); let mut signer = key.clone().into_keypair().unwrap(); - // 1st binding sig valid from t1 on + // Create subkey. + let mut subkey: Key<_, SubordinateRole> = + Key4::generate_ecc(true, Curve::Ed25519).unwrap().into(); + subkey.set_creation_time(t1 + chrono::Duration::days(1)).unwrap(); + let mut sk_signer = subkey.clone().into_keypair().unwrap(); + + // 1st direct key signature valid from t1 on let mut b = signature::Builder::new(SignatureType::DirectKey) .set_features(&Features::sequoia()).unwrap() - .set_key_flags(&KeyFlags::default().set_signing(true)).unwrap() + .set_key_flags(&KeyFlags::default() + .set_signing(true).set_certification(true)).unwrap() .set_signature_creation_time(t1).unwrap() - .set_key_expiration_time(Some(std::time::Duration::new( - 20 * 52 * 7 * 24 * 60 * 60, 0))).unwrap() .set_issuer_fingerprint(key.fingerprint()).unwrap() .set_issuer(key.fingerprint().into()).unwrap() .set_preferred_hash_algorithms(vec![HashAlgorithm::SHA512]) .unwrap(); - let bind1 = b.sign_primary_key_binding(&mut signer).unwrap(); + let direct1 = b.sign_primary_key_binding(&mut signer).unwrap(); - // Revocation sig valid from t2 on - b = signature::Builder::new(SignatureType::KeyRevocation) - .set_signature_creation_time(t2).unwrap() + // 1st subkey binding signature valid from t_sk_binding on + b = signature::Builder::new(SignatureType::SubkeyBinding) + .set_key_flags(&KeyFlags::default().set_signing(true)).unwrap() + .set_signature_creation_time(t_sk_binding).unwrap() .set_issuer_fingerprint(key.fingerprint()).unwrap() - .set_issuer(key.fingerprint().into()).unwrap(); - let rev = b.sign_primary_key_binding(&mut signer).unwrap(); + .set_issuer(key.fingerprint().into()).unwrap() + .set_embedded_signature( + signature::Builder::new(SignatureType::PrimaryKeyBinding) + .set_signature_creation_time(t_sk_binding).unwrap() + .set_issuer_fingerprint(subkey.fingerprint()).unwrap() + .set_issuer(subkey.keyid()).unwrap() + .sign_subkey_binding(&mut sk_signer, &key, &subkey).unwrap()) + .unwrap(); + let sk_bind1 = b.sign_subkey_binding(&mut signer, &key, &subkey).unwrap(); - // 2nd binding sig valid from t3 on + // 2nd direct key signature valid from t3 on b = signature::Builder::new(SignatureType::DirectKey) .set_features(&Features::sequoia()).unwrap() - .set_key_flags(&KeyFlags::default().set_signing(true)).unwrap() + .set_key_flags(&KeyFlags::default() + .set_signing(true).set_certification(true)).unwrap() .set_signature_creation_time(t3).unwrap() - .set_key_expiration_time(Some(std::time::Duration::new( - 20 * 52 * 7 * 24 * 60 * 60, 0))).unwrap() .set_issuer_fingerprint(key.fingerprint()).unwrap() .set_issuer(key.fingerprint().into()).unwrap() .set_preferred_hash_algorithms(vec![HashAlgorithm::SHA512]) .unwrap(); - let bind2 = b.sign_primary_key_binding(&mut signer).unwrap(); + let direct2 = b.sign_primary_key_binding(&mut signer).unwrap(); + + // 2nd subkey binding signature valid from t3 on + let mut b = signature::Builder::new(SignatureType::SubkeyBinding) + .set_key_flags(&KeyFlags::default().set_signing(true)).unwrap() + .set_signature_creation_time(t3).unwrap() + .set_issuer_fingerprint(key.fingerprint()).unwrap() + .set_issuer(key.fingerprint().into()).unwrap() + .set_embedded_signature( + signature::Builder::new(SignatureType::PrimaryKeyBinding) + .set_signature_creation_time(t3).unwrap() + .set_issuer_fingerprint(subkey.fingerprint()).unwrap() + .set_issuer(subkey.keyid()).unwrap() + .sign_subkey_binding(&mut sk_signer, &key, &subkey).unwrap()) + .unwrap(); + let sk_bind2 = b.sign_subkey_binding(&mut signer, &key, &subkey).unwrap(); + + let cert = Cert::from_packet_pile(PacketPile::from(vec![ + key.clone().into(), + direct1.clone().into(), + direct2.clone().into(), + subkey.clone().into(), + sk_bind1.clone().into(), + sk_bind2.clone().into(), + ])).unwrap(); + let mut fd = File::create("revoked-key-cert-not-revoked.pgp").unwrap(); + cert.serialize(&mut fd).unwrap(); + + for (slug, reason) in &[ + ("no_subpacket", None), + ("unspecified", Some(ReasonForRevocation::Unspecified)), + ("superseded", Some(ReasonForRevocation::KeySuperseded)), + ("compromised", Some(ReasonForRevocation::KeyCompromised)), + ("key_retired", Some(ReasonForRevocation::KeyRetired)), + ("uid_retired", Some(ReasonForRevocation::UIDRetired)), + ("private", Some(ReasonForRevocation::Private(100))), + ("unknown", Some(ReasonFor |