Modify the sq key revoke tests to test the cert store integration.
- Modify the `sq key revoke` tests to also test the cert store and
key store integration.
- Somehow this wasn't added to
cca564356c
.
This commit is contained in:
parent
ab0e2a446c
commit
3debf8b584
@ -1,13 +1,13 @@
|
||||
use assert_cmd::Command;
|
||||
|
||||
use tempfile::TempDir;
|
||||
|
||||
use chrono::Duration;
|
||||
use openpgp::parse::Parse;
|
||||
use openpgp::types::ReasonForRevocation;
|
||||
use openpgp::types::RevocationStatus;
|
||||
use openpgp::types::SignatureType;
|
||||
use openpgp::Cert;
|
||||
use openpgp::Packet;
|
||||
use openpgp::PacketPile;
|
||||
use openpgp::Result;
|
||||
use sequoia_openpgp as openpgp;
|
||||
|
||||
@ -18,9 +18,10 @@ use common::STANDARD_POLICY;
|
||||
|
||||
#[test]
|
||||
fn sq_key_revoke() -> Result<()> {
|
||||
let (tmpdir, path, time) = sq_key_generate(None)?;
|
||||
let (tmpdir, cert_path, time) = sq_key_generate(None)?;
|
||||
let cert_path = cert_path.display().to_string();
|
||||
|
||||
let cert = Cert::from_file(&path)?;
|
||||
let cert = Cert::from_file(&cert_path)?;
|
||||
let valid_cert = cert.with_policy(STANDARD_POLICY, Some(time.into()))?;
|
||||
let fingerprint = &valid_cert.clone().fingerprint();
|
||||
|
||||
@ -49,12 +50,6 @@ fn sq_key_revoke() -> Result<()> {
|
||||
None,
|
||||
),
|
||||
(ReasonForRevocation::KeyRetired, "retired", None, None),
|
||||
(
|
||||
ReasonForRevocation::KeyRetired,
|
||||
"retired",
|
||||
None,
|
||||
Some(time + Duration::hours(1)),
|
||||
),
|
||||
(
|
||||
ReasonForRevocation::KeyRetired,
|
||||
"retired",
|
||||
@ -62,12 +57,6 @@ fn sq_key_revoke() -> Result<()> {
|
||||
None,
|
||||
),
|
||||
(ReasonForRevocation::KeySuperseded, "superseded", None, None),
|
||||
(
|
||||
ReasonForRevocation::KeySuperseded,
|
||||
"superseded",
|
||||
None,
|
||||
Some(time + Duration::hours(1)),
|
||||
),
|
||||
(
|
||||
ReasonForRevocation::KeySuperseded,
|
||||
"superseded",
|
||||
@ -88,95 +77,153 @@ fn sq_key_revoke() -> Result<()> {
|
||||
None,
|
||||
),
|
||||
] {
|
||||
let revocation = &path.parent().unwrap().join(format!(
|
||||
"revocation_{}_{}_{}.rev",
|
||||
reason_str,
|
||||
if notations.is_some() {
|
||||
"notations"
|
||||
} else {
|
||||
"no_notations"
|
||||
},
|
||||
if revocation_time.is_some() {
|
||||
"time"
|
||||
} else {
|
||||
"no_time"
|
||||
}
|
||||
));
|
||||
eprintln!("==========================");
|
||||
eprintln!("reason: {}, message: {}, notations: {:?}, time: {:?}",
|
||||
reason, reason_str, notations, revocation_time);
|
||||
|
||||
let mut cmd = Command::cargo_bin("sq")?;
|
||||
cmd.args([
|
||||
"--no-cert-store",
|
||||
"--no-key-store",
|
||||
"key",
|
||||
"revoke",
|
||||
"--output",
|
||||
&revocation.to_string_lossy(),
|
||||
"--cert-file",
|
||||
&path.to_string_lossy(),
|
||||
reason_str,
|
||||
message,
|
||||
]);
|
||||
if let Some(notations) = notations {
|
||||
for (k, v) in notations {
|
||||
cmd.args(["--notation", k, v]);
|
||||
for keystore in [false, true].into_iter() {
|
||||
eprintln!("--------------------------");
|
||||
eprintln!("keystore: {}", keystore);
|
||||
|
||||
let home = TempDir::new().unwrap();
|
||||
let home = home.path().display().to_string();
|
||||
|
||||
let revocation = &tmpdir.path().join(format!(
|
||||
"revocation_{}_{}_{}.rev",
|
||||
reason_str,
|
||||
if notations.is_some() {
|
||||
"notations"
|
||||
} else {
|
||||
"no_notations"
|
||||
},
|
||||
if revocation_time.is_some() {
|
||||
"time"
|
||||
} else {
|
||||
"no_time"
|
||||
}
|
||||
));
|
||||
|
||||
if keystore {
|
||||
// When using the keystore, we need to import the key.
|
||||
let mut cmd = Command::cargo_bin("sq")?;
|
||||
cmd.args([
|
||||
"--home", &home,
|
||||
"key",
|
||||
"import",
|
||||
&cert_path,
|
||||
]);
|
||||
let output = cmd.output()?;
|
||||
if !output.status.success() {
|
||||
panic!(
|
||||
"sq exited with non-zero status code: {}",
|
||||
String::from_utf8(output.stderr)?
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(time) = revocation_time {
|
||||
|
||||
let mut cmd = Command::cargo_bin("sq")?;
|
||||
cmd.args([
|
||||
"--time",
|
||||
&time.format("%Y-%m-%dT%H:%M:%SZ").to_string(),
|
||||
"--home", &home,
|
||||
"key",
|
||||
"revoke",
|
||||
reason_str,
|
||||
message,
|
||||
]);
|
||||
}
|
||||
let output = cmd.output()?;
|
||||
if !output.status.success() {
|
||||
panic!(
|
||||
"sq exited with non-zero status code: {}",
|
||||
String::from_utf8(output.stderr)?
|
||||
);
|
||||
}
|
||||
|
||||
// We should get the primary key and the revocation signature.
|
||||
let packet_pile = PacketPile::from_file(&revocation)?;
|
||||
if keystore {
|
||||
cmd.args([
|
||||
"--cert", &cert.fingerprint().to_string(),
|
||||
]);
|
||||
} else {
|
||||
cmd.args([
|
||||
"--output",
|
||||
&revocation.to_string_lossy(),
|
||||
"--cert-file",
|
||||
&cert_path,
|
||||
]);
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
packet_pile.children().count(),
|
||||
2,
|
||||
"expected the primary key and the revocation signature"
|
||||
);
|
||||
if let Some(notations) = notations {
|
||||
for (k, v) in notations {
|
||||
cmd.args(["--notation", k, v]);
|
||||
}
|
||||
}
|
||||
if let Some(time) = revocation_time {
|
||||
cmd.args([
|
||||
"--time",
|
||||
&time.format("%Y-%m-%dT%H:%M:%SZ").to_string(),
|
||||
]);
|
||||
}
|
||||
let output = cmd.output()?;
|
||||
if !output.status.success() {
|
||||
panic!(
|
||||
"sq exited with non-zero status code: {}",
|
||||
String::from_utf8(output.stderr)?
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(Packet::Signature(sig)) = packet_pile.path_ref(&[1]) {
|
||||
// the issuer is the certificate owner
|
||||
assert_eq!(
|
||||
sig.get_issuers().into_iter().next(),
|
||||
Some(fingerprint.into())
|
||||
);
|
||||
if keystore {
|
||||
// When using the keystore, we need to export the
|
||||
// revoked certificate.
|
||||
|
||||
let cert = Cert::from_file(&path)?;
|
||||
let revoked_cert = cert.insert_packets(sig.clone()).unwrap();
|
||||
let status = revoked_cert
|
||||
.with_policy(STANDARD_POLICY, revocation_time.map(Into::into))
|
||||
.unwrap()
|
||||
.revocation_status();
|
||||
let mut cmd = Command::cargo_bin("sq")?;
|
||||
cmd.args([
|
||||
"--home", &home,
|
||||
"cert",
|
||||
"export",
|
||||
"--cert", &cert.fingerprint().to_string(),
|
||||
]);
|
||||
let output = cmd.output()?;
|
||||
if !output.status.success() {
|
||||
panic!(
|
||||
"sq exited with non-zero status code: {}",
|
||||
String::from_utf8(output.stderr)?
|
||||
);
|
||||
}
|
||||
std::fs::write(&revocation, &output.stdout)
|
||||
.expect(&format!("Writing {}", &revocation.display()));
|
||||
}
|
||||
|
||||
println!("{:?}", sig);
|
||||
println!("{:?}", status);
|
||||
// Verify the revocation.
|
||||
assert!(matches!(status, RevocationStatus::Revoked(_)));
|
||||
let updated = Cert::from_file(&revocation).expect("valid cert");
|
||||
|
||||
// it is a key revocation
|
||||
assert_eq!(sig.typ(), SignatureType::KeyRevocation);
|
||||
if let RevocationStatus::Revoked(sigs)
|
||||
= updated.revocation_status(STANDARD_POLICY, None)
|
||||
{
|
||||
assert_eq!(sigs.len(), 1);
|
||||
let sig = sigs.into_iter().next().unwrap();
|
||||
|
||||
// our reason for revocation and message matches
|
||||
assert_eq!(
|
||||
sig.reason_for_revocation(),
|
||||
Some((reason, message.as_bytes()))
|
||||
);
|
||||
// the issuer is the certificate owner
|
||||
assert_eq!(
|
||||
sig.get_issuers().into_iter().next(),
|
||||
Some(fingerprint.into())
|
||||
);
|
||||
|
||||
// the notations of the revocation match the ones
|
||||
// we passed in
|
||||
compare_notations(sig, notations)?;
|
||||
} else {
|
||||
panic!("Expected a signature, got: {:?}", packet_pile);
|
||||
let revoked_cert = cert.clone().insert_packets(sig.clone()).unwrap();
|
||||
let status = revoked_cert
|
||||
.with_policy(STANDARD_POLICY, revocation_time.map(Into::into))
|
||||
.unwrap()
|
||||
.revocation_status();
|
||||
|
||||
println!("{:?}", sig);
|
||||
println!("{:?}", status);
|
||||
// Verify the revocation.
|
||||
assert!(matches!(status, RevocationStatus::Revoked(_)));
|
||||
|
||||
// it is a key revocation
|
||||
assert_eq!(sig.typ(), SignatureType::KeyRevocation);
|
||||
|
||||
// our reason for revocation and message matches
|
||||
assert_eq!(
|
||||
sig.reason_for_revocation(),
|
||||
Some((reason, message.as_bytes()))
|
||||
);
|
||||
|
||||
// the notations of the revocation match the ones
|
||||
// we passed in
|
||||
compare_notations(sig, notations)?;
|
||||
} else {
|
||||
panic!("Not revoked");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -187,11 +234,13 @@ fn sq_key_revoke() -> Result<()> {
|
||||
|
||||
#[test]
|
||||
fn sq_key_revoke_thirdparty() -> Result<()> {
|
||||
let (tmpdir, path, _) = sq_key_generate(None)?;
|
||||
let cert = Cert::from_file(&path)?;
|
||||
let (tmpdir, cert_path, _) = sq_key_generate(None)?;
|
||||
let cert_path = cert_path.display().to_string();
|
||||
let cert = Cert::from_file(&cert_path)?;
|
||||
|
||||
let (thirdparty_tmpdir, thirdparty_path, thirdparty_time) =
|
||||
sq_key_generate(Some(&["bob <bob@example.org>"]))?;
|
||||
let thirdparty_path = thirdparty_path.display().to_string();
|
||||
let thirdparty_cert = Cert::from_file(&thirdparty_path)?;
|
||||
let thirdparty_valid_cert = thirdparty_cert
|
||||
.with_policy(STANDARD_POLICY, Some(thirdparty_time.into()))?;
|
||||
@ -261,99 +310,157 @@ fn sq_key_revoke_thirdparty() -> Result<()> {
|
||||
None,
|
||||
),
|
||||
] {
|
||||
let revocation = &path.parent().unwrap().join(format!(
|
||||
"revocation_{}_{}_{}.rev",
|
||||
reason_str,
|
||||
if notations.is_some() {
|
||||
"notations"
|
||||
} else {
|
||||
"no_notations"
|
||||
},
|
||||
if revocation_time.is_some() {
|
||||
"time"
|
||||
} else {
|
||||
"no_time"
|
||||
}
|
||||
));
|
||||
for keystore in [false, true].into_iter() {
|
||||
let home = TempDir::new().unwrap();
|
||||
let home = home.path().display().to_string();
|
||||
|
||||
let mut cmd = Command::cargo_bin("sq")?;
|
||||
cmd.args([
|
||||
"--no-cert-store",
|
||||
"--no-key-store",
|
||||
"key",
|
||||
"revoke",
|
||||
"--output",
|
||||
&revocation.to_string_lossy(),
|
||||
"--cert-file",
|
||||
&path.to_string_lossy(),
|
||||
"--revoker-file",
|
||||
&thirdparty_path.to_string_lossy(),
|
||||
reason_str,
|
||||
message,
|
||||
]);
|
||||
if let Some(notations) = notations {
|
||||
for (k, v) in notations {
|
||||
cmd.args(["--notation", k, v]);
|
||||
let revocation = &tmpdir.path().join(format!(
|
||||
"revocation_{}_{}_{}.rev",
|
||||
reason_str,
|
||||
if notations.is_some() {
|
||||
"notations"
|
||||
} else {
|
||||
"no_notations"
|
||||
},
|
||||
if revocation_time.is_some() {
|
||||
"time"
|
||||
} else {
|
||||
"no_time"
|
||||
}
|
||||
));
|
||||
|
||||
if keystore {
|
||||
// When using the keystore, we need to import the key.
|
||||
|
||||
for path in &[ &cert_path, &thirdparty_path ] {
|
||||
let mut cmd = Command::cargo_bin("sq")?;
|
||||
cmd.args([
|
||||
"--home", &home,
|
||||
"key",
|
||||
"import",
|
||||
&path,
|
||||
]);
|
||||
let output = cmd.output()?;
|
||||
if !output.status.success() {
|
||||
panic!(
|
||||
"sq exited with non-zero status code: {}",
|
||||
String::from_utf8(output.stderr)?
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(time) = revocation_time {
|
||||
|
||||
let mut cmd = Command::cargo_bin("sq")?;
|
||||
cmd.args([
|
||||
"--time",
|
||||
&time.format("%Y-%m-%dT%H:%M:%SZ").to_string(),
|
||||
"--home", &home,
|
||||
"key",
|
||||
"revoke",
|
||||
reason_str,
|
||||
message,
|
||||
]);
|
||||
}
|
||||
let output = cmd.output()?;
|
||||
if !output.status.success() {
|
||||
panic!(
|
||||
"sq exited with non-zero status code: {}",
|
||||
String::from_utf8(output.stderr)?
|
||||
);
|
||||
}
|
||||
|
||||
// read revocation cert
|
||||
let revocation_cert = Cert::from_file(&revocation)?;
|
||||
assert!(! revocation_cert.is_tsk());
|
||||
|
||||
// evaluate revocation status
|
||||
let status = revocation_cert.revocation_status(
|
||||
STANDARD_POLICY, revocation_time.map(Into::into));
|
||||
if let RevocationStatus::CouldBe(sigs) = status {
|
||||
// there is only one signature packet
|
||||
assert_eq!(sigs.len(), 1);
|
||||
let sig = sigs.into_iter().next().unwrap();
|
||||
|
||||
// it is a key revocation
|
||||
assert_eq!(sig.typ(), SignatureType::KeyRevocation);
|
||||
|
||||
// the issuer is a thirdparty revoker
|
||||
assert_eq!(
|
||||
sig.get_issuers().into_iter().next().as_ref(),
|
||||
Some(&thirdparty_fingerprint.clone().into())
|
||||
);
|
||||
|
||||
// the revocation can be verified
|
||||
if sig
|
||||
.clone()
|
||||
.verify_primary_key_revocation(
|
||||
&thirdparty_cert.primary_key(),
|
||||
&cert.primary_key(),
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
panic!("revocation is not valid")
|
||||
if keystore {
|
||||
cmd.args([
|
||||
"--cert", &cert.fingerprint().to_string(),
|
||||
"--revoker", &thirdparty_cert.fingerprint().to_string(),
|
||||
]);
|
||||
} else {
|
||||
cmd.args([
|
||||
"--output",
|
||||
&revocation.to_string_lossy(),
|
||||
"--cert-file",
|
||||
&cert_path,
|
||||
"--revoker-file",
|
||||
&thirdparty_path,
|
||||
]);
|
||||
}
|
||||
|
||||
// our reason for revocation and message matches
|
||||
assert_eq!(
|
||||
sig.reason_for_revocation(),
|
||||
Some((reason, message.as_bytes()))
|
||||
);
|
||||
if let Some(notations) = notations {
|
||||
for (k, v) in notations {
|
||||
cmd.args(["--notation", k, v]);
|
||||
}
|
||||
}
|
||||
if let Some(time) = revocation_time {
|
||||
cmd.args([
|
||||
"--time",
|
||||
&time.format("%Y-%m-%dT%H:%M:%SZ").to_string(),
|
||||
]);
|
||||
}
|
||||
let output = cmd.output()?;
|
||||
if !output.status.success() {
|
||||
panic!(
|
||||
"sq exited with non-zero status code: {}",
|
||||
String::from_utf8(output.stderr)?
|
||||
);
|
||||
}
|
||||
|
||||
// the notations of the revocation match the ones
|
||||
// we passed in
|
||||
compare_notations(sig, notations)?;
|
||||
} else {
|
||||
panic!("there are no signatures in {:?}", status);
|
||||
if keystore {
|
||||
// When using the keystore, we need to export the
|
||||
// revoked certificate.
|
||||
|
||||
let mut cmd = Command::cargo_bin("sq")?;
|
||||
cmd.args([
|
||||
"--home", &home,
|
||||
"cert",
|
||||
"export",
|
||||
"--cert", &cert.fingerprint().to_string(),
|
||||
]);
|
||||
let output = cmd.output()?;
|
||||
if !output.status.success() {
|
||||
panic!(
|
||||
"sq exited with non-zero status code: {}",
|
||||
String::from_utf8(output.stderr)?
|
||||
);
|
||||
}
|
||||
std::fs::write(&revocation, &output.stdout)
|
||||
.expect(&format!("Writing {}", &revocation.display()));
|
||||
}
|
||||
|
||||
// read revocation cert
|
||||
let revocation_cert = Cert::from_file(&revocation)?;
|
||||
assert!(! revocation_cert.is_tsk());
|
||||
|
||||
// evaluate revocation status
|
||||
let status = revocation_cert.revocation_status(
|
||||
STANDARD_POLICY, revocation_time.map(Into::into));
|
||||
if let RevocationStatus::CouldBe(sigs) = status {
|
||||
// there is only one signature packet
|
||||
assert_eq!(sigs.len(), 1);
|
||||
let sig = sigs.into_iter().next().unwrap();
|
||||
|
||||
// it is a key revocation
|
||||
assert_eq!(sig.typ(), SignatureType::KeyRevocation);
|
||||
|
||||
// the issuer is a thirdparty revoker
|
||||
assert_eq!(
|
||||
sig.get_issuers().into_iter().next().as_ref(),
|
||||
Some(&thirdparty_fingerprint.clone().into())
|
||||
);
|
||||
|
||||
// the revocation can be verified
|
||||
if sig
|
||||
.clone()
|
||||
.verify_primary_key_revocation(
|
||||
&thirdparty_cert.primary_key(),
|
||||
&cert.primary_key(),
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
panic!("revocation is not valid")
|
||||
}
|
||||
|
||||
// our reason for revocation and message matches
|
||||
assert_eq!(
|
||||
sig.reason_for_revocation(),
|
||||
Some((reason, message.as_bytes()))
|
||||
);
|
||||
|
||||
// the notations of the revocation match the ones
|
||||
// we passed in
|
||||
compare_notations(sig, notations)?;
|
||||
} else {
|
||||
panic!("there are no signatures in {:?}", status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user