Port the sq key revoke tests to the common test framework.
- Add `Sq::key_revoke`, and change the `sq key revoke` integration tests to use it.
This commit is contained in:
@ -376,6 +376,79 @@ impl Sq {
|
||||
String::from_utf8_lossy(&output.stdout).to_string()
|
||||
}
|
||||
|
||||
/// Run `sq key revoked` and return the revocation certificate.
|
||||
pub fn key_revoke<'a, H, I, Q>(&self,
|
||||
cert_handle: H,
|
||||
revoker_handle: I,
|
||||
reason: &str,
|
||||
message: &str,
|
||||
revocation_time: Option<DateTime<Utc>>,
|
||||
notations: &[(&str, &str)],
|
||||
output_file: Q)
|
||||
-> Cert
|
||||
where H: Into<FileOrKeyHandle>,
|
||||
I: Into<Option<FileOrKeyHandle>>,
|
||||
Q: Into<Option<&'a Path>>,
|
||||
{
|
||||
let cert_handle = cert_handle.into();
|
||||
let revoker_handle = revoker_handle.into();
|
||||
let output_file = output_file.into();
|
||||
|
||||
let mut cmd = self.command();
|
||||
cmd.arg("key").arg("revoke")
|
||||
.arg(reason)
|
||||
.arg(message);
|
||||
|
||||
match &cert_handle {
|
||||
FileOrKeyHandle::FileOrStdin(path) => {
|
||||
cmd.arg("--cert-file").arg(path);
|
||||
}
|
||||
FileOrKeyHandle::KeyHandle((_kh, s)) => {
|
||||
cmd.arg("--cert").arg(&s);
|
||||
}
|
||||
};
|
||||
match revoker_handle.as_ref() {
|
||||
Some(FileOrKeyHandle::FileOrStdin(path)) => {
|
||||
cmd.arg("--revoker-file").arg(path);
|
||||
}
|
||||
Some(FileOrKeyHandle::KeyHandle((_kh, s))) => {
|
||||
cmd.arg("--revoker").arg(&s);
|
||||
}
|
||||
None => (),
|
||||
};
|
||||
|
||||
if let Some(output_file) = output_file {
|
||||
cmd.arg("--output").arg(output_file);
|
||||
}
|
||||
|
||||
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 = self.run(cmd, Some(true));
|
||||
assert!(output.status.success());
|
||||
|
||||
if let Some(output_file) = output_file {
|
||||
if output_file != &PathBuf::from("-") {
|
||||
return Cert::from_file(output_file)
|
||||
.expect("can parse certificate");
|
||||
}
|
||||
} else if output_file.is_none() {
|
||||
if let FileOrKeyHandle::KeyHandle((kh, _s)) = cert_handle {
|
||||
return self.cert_export(kh);
|
||||
}
|
||||
}
|
||||
Cert::from_bytes(&output.stdout)
|
||||
.expect("can parse certificate")
|
||||
}
|
||||
|
||||
/// Imports the specified key into the keystore.
|
||||
pub fn key_import<P>(&self, path: P)
|
||||
where P: AsRef<Path>
|
||||
|
@ -1,8 +1,5 @@
|
||||
use assert_cmd::Command;
|
||||
use std::time::Duration;
|
||||
|
||||
use tempfile::TempDir;
|
||||
|
||||
use chrono::Duration;
|
||||
use openpgp::parse::Parse;
|
||||
use openpgp::types::ReasonForRevocation;
|
||||
use openpgp::types::RevocationStatus;
|
||||
@ -13,23 +10,24 @@ use sequoia_openpgp as openpgp;
|
||||
|
||||
mod common;
|
||||
use common::compare_notations;
|
||||
use common::sq_key_generate;
|
||||
use common::FileOrKeyHandle;
|
||||
use common::Sq;
|
||||
use common::STANDARD_POLICY;
|
||||
|
||||
#[test]
|
||||
fn sq_key_revoke() -> Result<()> {
|
||||
let (tmpdir, cert_path, time) = sq_key_generate(None)?;
|
||||
let cert_path = cert_path.display().to_string();
|
||||
let sq = Sq::new();
|
||||
|
||||
let cert = Cert::from_file(&cert_path)?;
|
||||
let valid_cert = cert.with_policy(STANDARD_POLICY, Some(time.into()))?;
|
||||
let fingerprint = &valid_cert.clone().fingerprint();
|
||||
let time = sq.now();
|
||||
|
||||
let (cert, cert_path, _cert_rev)
|
||||
= sq.key_generate(&[], &["alice"]);
|
||||
|
||||
let message = "message";
|
||||
|
||||
// revoke for various reasons, with or without notations added, or with
|
||||
// a revocation whose reference time is one hour after the creation of the
|
||||
// certificate
|
||||
// revoke for various reasons, with or without notations added, or
|
||||
// with a revocation whose reference time is one hour after the
|
||||
// creation of the certificate
|
||||
for (reason, reason_str, notations, revocation_time) in [
|
||||
(
|
||||
ReasonForRevocation::KeyCompromised,
|
||||
@ -41,7 +39,7 @@ fn sq_key_revoke() -> Result<()> {
|
||||
ReasonForRevocation::KeyCompromised,
|
||||
"compromised",
|
||||
&[][..],
|
||||
Some(time + Duration::hours(1)),
|
||||
Some(time + Duration::new(60 * 60, 0)),
|
||||
),
|
||||
(
|
||||
ReasonForRevocation::KeyCompromised,
|
||||
@ -68,7 +66,7 @@ fn sq_key_revoke() -> Result<()> {
|
||||
ReasonForRevocation::Unspecified,
|
||||
"unspecified",
|
||||
&[][..],
|
||||
Some(time + Duration::hours(1)),
|
||||
Some(time + Duration::new(60 * 60, 0)),
|
||||
),
|
||||
(
|
||||
ReasonForRevocation::Unspecified,
|
||||
@ -85,13 +83,10 @@ fn sq_key_revoke() -> Result<()> {
|
||||
eprintln!("--------------------------");
|
||||
eprintln!("keystore: {}", keystore);
|
||||
|
||||
let home = TempDir::new().unwrap();
|
||||
let home = home.path().display().to_string();
|
||||
|
||||
let revocation = &tmpdir.path().join(format!(
|
||||
let revocation = sq.scratch_file(Some(&format!(
|
||||
"revocation_{}_{}_{}.rev",
|
||||
reason_str,
|
||||
if ! notations.is_empty() {
|
||||
if notations.is_empty() {
|
||||
"no_notations"
|
||||
} else {
|
||||
"notations"
|
||||
@ -101,88 +96,25 @@ fn sq_key_revoke() -> Result<()> {
|
||||
} 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)?
|
||||
);
|
||||
}
|
||||
sq.key_import(&cert_path);
|
||||
}
|
||||
|
||||
let mut cmd = Command::cargo_bin("sq")?;
|
||||
cmd.args([
|
||||
"--home", &home,
|
||||
"key",
|
||||
"revoke",
|
||||
let updated = sq.key_revoke(
|
||||
if keystore {
|
||||
FileOrKeyHandle::from(cert.key_handle())
|
||||
} else {
|
||||
FileOrKeyHandle::from(&cert_path)
|
||||
},
|
||||
None,
|
||||
reason_str,
|
||||
message,
|
||||
]);
|
||||
|
||||
if keystore {
|
||||
cmd.args([
|
||||
"--cert", &cert.fingerprint().to_string(),
|
||||
]);
|
||||
} else {
|
||||
cmd.args([
|
||||
"--output",
|
||||
&revocation.to_string_lossy(),
|
||||
"--cert-file",
|
||||
&cert_path,
|
||||
]);
|
||||
}
|
||||
|
||||
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 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()));
|
||||
}
|
||||
|
||||
let updated = Cert::from_file(&revocation).expect("valid cert");
|
||||
None,
|
||||
notations,
|
||||
Some(revocation.as_path()));
|
||||
|
||||
if let RevocationStatus::Revoked(sigs)
|
||||
= updated.revocation_status(STANDARD_POLICY, None)
|
||||
@ -193,14 +125,14 @@ fn sq_key_revoke() -> Result<()> {
|
||||
// the issuer is the certificate owner
|
||||
assert_eq!(
|
||||
sig.get_issuers().into_iter().next(),
|
||||
Some(fingerprint.into())
|
||||
Some(cert.key_handle())
|
||||
);
|
||||
|
||||
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();
|
||||
.revocation_status(
|
||||
STANDARD_POLICY,
|
||||
revocation_time.map(Into::into));
|
||||
|
||||
println!("{:?}", sig);
|
||||
println!("{:?}", status);
|
||||
@ -225,23 +157,23 @@ fn sq_key_revoke() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
tmpdir.close()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sq_key_revoke_thirdparty() -> Result<()> {
|
||||
let (tmpdir, cert_path, _) = sq_key_generate(None)?;
|
||||
let cert_path = cert_path.display().to_string();
|
||||
let cert = Cert::from_file(&cert_path)?;
|
||||
let sq = Sq::new();
|
||||
|
||||
let time = sq.now();
|
||||
|
||||
let (cert, cert_path, _cert_rev)
|
||||
= sq.key_generate(&[], &["alice"]);
|
||||
|
||||
let (thirdparty_cert, thirdparty_path, _cert_rev)
|
||||
= sq.key_generate(&[], &["bob <bob@example.org>"]);
|
||||
|
||||
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()))?;
|
||||
.with_policy(STANDARD_POLICY, Some(time.into()))?;
|
||||
let thirdparty_fingerprint = &thirdparty_valid_cert.clone().fingerprint();
|
||||
|
||||
let message = "message";
|
||||
@ -260,7 +192,7 @@ fn sq_key_revoke_thirdparty() -> Result<()> {
|
||||
ReasonForRevocation::KeyCompromised,
|
||||
"compromised",
|
||||
&[][..],
|
||||
Some(thirdparty_time + Duration::hours(1)),
|
||||
Some(time + Duration::new(60 * 60, 0)),
|
||||
),
|
||||
(
|
||||
ReasonForRevocation::KeyCompromised,
|
||||
@ -273,7 +205,7 @@ fn sq_key_revoke_thirdparty() -> Result<()> {
|
||||
ReasonForRevocation::KeyRetired,
|
||||
"retired",
|
||||
&[][..],
|
||||
Some(thirdparty_time + Duration::hours(1)),
|
||||
Some(time + Duration::new(60 * 60, 0)),
|
||||
),
|
||||
(
|
||||
ReasonForRevocation::KeyRetired,
|
||||
@ -286,7 +218,7 @@ fn sq_key_revoke_thirdparty() -> Result<()> {
|
||||
ReasonForRevocation::KeySuperseded,
|
||||
"superseded",
|
||||
&[][..],
|
||||
Some(thirdparty_time + Duration::hours(1)),
|
||||
Some(time + Duration::new(60 * 60, 0)),
|
||||
),
|
||||
(
|
||||
ReasonForRevocation::KeySuperseded,
|
||||
@ -299,7 +231,7 @@ fn sq_key_revoke_thirdparty() -> Result<()> {
|
||||
ReasonForRevocation::Unspecified,
|
||||
"unspecified",
|
||||
&[][..],
|
||||
Some(thirdparty_time + Duration::hours(1)),
|
||||
Some(time + Duration::new(60 * 60, 0)),
|
||||
),
|
||||
(
|
||||
ReasonForRevocation::Unspecified,
|
||||
@ -309,10 +241,7 @@ fn sq_key_revoke_thirdparty() -> Result<()> {
|
||||
),
|
||||
] {
|
||||
for keystore in [false, true].into_iter() {
|
||||
let home = TempDir::new().unwrap();
|
||||
let home = home.path().display().to_string();
|
||||
|
||||
let revocation = &tmpdir.path().join(format!(
|
||||
let revocation = sq.scratch_file(Some(&format!(
|
||||
"revocation_{}_{}_{}.rev",
|
||||
reason_str,
|
||||
if ! notations.is_empty() {
|
||||
@ -325,94 +254,32 @@ fn sq_key_revoke_thirdparty() -> Result<()> {
|
||||
} 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)?
|
||||
);
|
||||
}
|
||||
}
|
||||
sq.cert_import(&cert_path);
|
||||
sq.key_import(&thirdparty_path);
|
||||
}
|
||||
|
||||
let mut cmd = Command::cargo_bin("sq")?;
|
||||
cmd.args([
|
||||
"--home", &home,
|
||||
"key",
|
||||
"revoke",
|
||||
let revocation_cert = sq.key_revoke(
|
||||
if keystore {
|
||||
FileOrKeyHandle::from(cert.key_handle())
|
||||
} else {
|
||||
FileOrKeyHandle::from(&cert_path)
|
||||
},
|
||||
if keystore {
|
||||
FileOrKeyHandle::from(thirdparty_cert.key_handle())
|
||||
} else {
|
||||
FileOrKeyHandle::from(&thirdparty_path)
|
||||
},
|
||||
reason_str,
|
||||
message,
|
||||
]);
|
||||
None,
|
||||
notations,
|
||||
Some(revocation.as_path()));
|
||||
|
||||
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,
|
||||
]);
|
||||
}
|
||||
|
||||
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 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());
|
||||
|
||||
@ -460,8 +327,5 @@ fn sq_key_revoke_thirdparty() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
tmpdir.close()?;
|
||||
thirdparty_tmpdir.close()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
Reference in New Issue
Block a user