Move sq key userid strip to sq toolbox strip-userid.

- Fixes #306.
This commit is contained in:
Justus Winter 2024-09-16 16:16:10 +02:00
parent 9cfc2e9a16
commit e2d5bc1de4
No known key found for this signature in database
GPG Key ID: 686F55B4AB2B3386
9 changed files with 143 additions and 129 deletions

View File

@ -494,7 +494,7 @@ _Requirement: We must be able to generate a key with a User ID, and then strip t
~~~scenario
given an installed sq
when I run sq --no-cert-store --no-key-store key generate --without-password --userid "<juliet@example.org>" --output key.pgp
when I run sq --no-cert-store --no-key-store key userid strip --cert-file key.pgp --userid "<juliet@example.org>" --output new.pgp
when I run sq --no-cert-store --no-key-store toolbox strip-userid --cert-file key.pgp --userid "<juliet@example.org>" --output new.pgp
when I run sq --no-cert-store --no-key-store inspect new.pgp
then stdout doesn't contain "UserID:"
~~~
@ -1103,11 +1103,11 @@ when I run sq --no-cert-store --no-key-store toolbox extract-cert bob.pgp --outp
when I run sq --no-cert-store --no-key-store pki certify --certifier-file alice.pgp bob-cert.pgp --email bob@example.org --output cert.pgp
when I run sq --no-cert-store --no-key-store key userid strip --cert-file cert.pgp --userid "<bob@example.org>" --output cert.0.pgp
when I run sq --no-cert-store --no-key-store toolbox strip-userid --cert-file cert.pgp --userid "<bob@example.org>" --output cert.0.pgp
when I run sq --no-cert-store --no-key-store inspect cert.0.pgp
then stdout contains "Certifications: 1,"
when I run sq --no-cert-store --no-key-store key userid strip --cert-file cert.pgp --userid "Bob <bob@example.org>" --output cert.1.pgp
when I run sq --no-cert-store --no-key-store toolbox strip-userid --cert-file cert.pgp --userid "Bob <bob@example.org>" --output cert.1.pgp
when I run sq --no-cert-store --no-key-store inspect cert.1.pgp
then stdout contains "Certifications: 1,"
~~~

View File

@ -31,7 +31,6 @@ Add User IDs to a key, or revoke them.
pub enum Command {
Add(AddCommand),
Revoke(RevokeCommand),
Strip(StripCommand),
}
const USERID_ADD_EXAMPLES: Actions = Actions {
@ -372,120 +371,3 @@ impl From<UserIDReasonForRevocation> for ReasonForRevocation {
}
}
}
const USERID_STRIP_EXAMPLES: Actions = Actions {
actions: &[
Action::Example(Example {
comment: "Strip a User ID from a cert in the cert store.",
command: &[
"sq", "key", "userid", "strip",
"--cert", "EB28F26E2739A4870ECC47726F0073F60FD0CBF0",
"--userid", "Alice <alice@example.org>",
],
}),
]
};
test_examples!(sq_key_userid_strip, USERID_STRIP_EXAMPLES);
#[derive(Debug, Args)]
#[clap(
about = "Strip a user ID",
long_about =
"Strip a user ID.
Note that this operation does not reliably remove User IDs from a \
certificate that has already been disseminated! (OpenPGP software \
typically appends new information it receives about a certificate to \
its local copy of that certificate. Systems that have obtained a copy \
of your certificate with the User ID that you are trying to strip will \
not drop that User ID from their copy.)
In most cases, you will want to use the 'sq key userid revoke' operation \
instead. That issues a revocation for a User ID, which can be used to mark \
the User ID as invalidated.
However, this operation can be useful in very specific cases, in particular: \
to remove a mistakenly added User ID before it has been uploaded to key \
servers or otherwise shared.
Stripping a User ID may change how a certificate is interpreted. This \
is because information about the certificate like algorithm preferences, \
the primary key's key flags, etc. is stored in the User ID's binding \
signature.
",
after_help = USERID_STRIP_EXAMPLES,
)]
#[clap(group(ArgGroup::new("cert_input").args(&["cert_file", "cert"]).required(true)))]
#[clap(group(ArgGroup::new("cert-userid").args(&["names", "emails", "userid"]).required(true).multiple(true)))]
pub struct StripCommand {
#[clap(
long,
value_name = "FINGERPRINT|KEYID",
help = "Strip the user ID on the specified certificate",
)]
pub cert: Option<KeyHandle>,
#[clap(
long,
help = FileOrStdin::HELP_OPTIONAL,
value_name = "CERT_FILE",
conflicts_with = "cert",
help = "Strip the user ID on the specified certificate",
long_help = "\
Strip the user ID on the specified certificate.
Read the certificate whose user ID should be stripped from FILE or \
stdin, if `-`. It is an error for the file to contain more than one \
certificate.",
)]
pub cert_file: Option<FileOrStdin>,
#[clap(
long,
value_name = FileOrCertStore::VALUE_NAME,
help = "Write to the specified FILE",
long_help = "\
Write to the specified FILE.
If not specified, and the certificate was read from the certificate
store, imports the modified certificate into the cert store. If not
specified, and the certificate was read from a file, writes the
modified certificate to stdout.",
)]
pub output: Option<FileOrStdout>,
#[clap(
long = "name",
value_name = "NAME",
help = "Strip the given name user ID",
long_help = "\
Strip the given name user ID. Must match a user ID exactly. To strip
a user ID that contains more than just a name, use `--userid`.",
)]
pub names: Vec<String>,
#[clap(
long = "email",
value_name = "ADDRESS",
help = "Strip the given email address user ID",
long_help = "\
Strip the given email address user ID. Must match a user ID exactly.
To strip a user ID that contains more than just an email address name,
use `--userid`.",
)]
pub emails: Vec<String>,
#[clap(
value_name = "USERID",
long,
help = "Strip the given user IDs",
long_help = "\
Strip the given user IDs from the key. Must match a user ID exactly.",
)]
pub userid: Vec<UserID>,
#[clap(
long,
help = "Emit binary data",
)]
pub binary: bool,
}

View File

@ -7,6 +7,7 @@ pub mod dearmor;
pub mod extract_cert;
pub mod keyring;
pub mod packet;
pub mod strip_userid;
#[derive(Parser, Debug)]
#[clap(
@ -30,6 +31,7 @@ pub struct Command {
pub enum Subcommands {
Keyring(keyring::Command),
ExtractCert(extract_cert::Command),
StripUserid(strip_userid::Command),
Packet(packet::Command),
Armor(armor::Command),
Dearmor(dearmor::Command),

View File

@ -0,0 +1,129 @@
use clap::{ArgGroup, Args};
use sequoia_openpgp as openpgp;
use openpgp::KeyHandle;
use openpgp::packet::UserID;
use crate::cli::types::ClapData;
use crate::cli::types::FileOrCertStore;
use crate::cli::types::FileOrStdin;
use crate::cli::types::FileOrStdout;
use crate::cli::examples::*;
#[derive(Debug, Args)]
#[clap(
about = "Strip a user ID",
long_about =
"Strip a user ID.
Note that this operation does not reliably remove User IDs from a \
certificate that has already been disseminated! (OpenPGP software \
typically appends new information it receives about a certificate to \
its local copy of that certificate. Systems that have obtained a copy \
of your certificate with the User ID that you are trying to strip will \
not drop that User ID from their copy.)
In most cases, you will want to use the 'sq key userid revoke' operation \
instead. That issues a revocation for a User ID, which can be used to mark \
the User ID as invalidated.
However, this operation can be useful in very specific cases, in particular: \
to remove a mistakenly added User ID before it has been uploaded to key \
servers or otherwise shared.
Stripping a User ID may change how a certificate is interpreted. This \
is because information about the certificate like algorithm preferences, \
the primary key's key flags, etc. is stored in the User ID's binding \
signature.
",
after_help = USERID_STRIP_EXAMPLES,
)]
#[clap(group(ArgGroup::new("cert_input").args(&["cert_file", "cert"]).required(true)))]
#[clap(group(ArgGroup::new("cert-userid").args(&["names", "emails", "userid"]).required(true).multiple(true)))]
pub struct Command {
#[clap(
long,
value_name = "FINGERPRINT|KEYID",
help = "Strip the user ID on the specified certificate",
)]
pub cert: Option<KeyHandle>,
#[clap(
long,
help = FileOrStdin::HELP_OPTIONAL,
value_name = "CERT_FILE",
conflicts_with = "cert",
help = "Strip the user ID on the specified certificate",
long_help = "\
Strip the user ID on the specified certificate.
Read the certificate whose user ID should be stripped from FILE or \
stdin, if `-`. It is an error for the file to contain more than one \
certificate.",
)]
pub cert_file: Option<FileOrStdin>,
#[clap(
long,
value_name = FileOrCertStore::VALUE_NAME,
help = "Write to the specified FILE",
long_help = "\
Write to the specified FILE.
If not specified, and the certificate was read from the certificate
store, imports the modified certificate into the cert store. If not
specified, and the certificate was read from a file, writes the
modified certificate to stdout.",
)]
pub output: Option<FileOrStdout>,
#[clap(
long = "name",
value_name = "NAME",
help = "Strip the given name user ID",
long_help = "\
Strip the given name user ID. Must match a user ID exactly. To strip
a user ID that contains more than just a name, use `--userid`.",
)]
pub names: Vec<String>,
#[clap(
long = "email",
value_name = "ADDRESS",
help = "Strip the given email address user ID",
long_help = "\
Strip the given email address user ID. Must match a user ID exactly.
To strip a user ID that contains more than just an email address name,
use `--userid`.",
)]
pub emails: Vec<String>,
#[clap(
value_name = "USERID",
long,
help = "Strip the given user IDs",
long_help = "\
Strip the given user IDs from the key. Must match a user ID exactly.",
)]
pub userid: Vec<UserID>,
#[clap(
long,
help = "Emit binary data",
)]
pub binary: bool,
}
const USERID_STRIP_EXAMPLES: Actions = Actions {
actions: &[
Action::Example(Example {
comment: "Strip a User ID from a cert in the cert store.",
command: &[
"sq", "toolbox", "strip-userid",
"--cert", "EB28F26E2739A4870ECC47726F0073F60FD0CBF0",
"--userid", "Alice <alice@example.org>",
],
}),
]
};
test_examples!(sq_key_userid_strip, USERID_STRIP_EXAMPLES);

View File

@ -20,7 +20,7 @@ mod password;
mod revoke;
use revoke::certificate_revoke;
mod subkey;
mod userid;
pub mod userid;
pub fn dispatch(sq: Sq, command: cli::key::Command) -> Result<()>
{

View File

@ -168,7 +168,6 @@ pub fn dispatch(
match command {
cli::key::userid::Command::Add(c) => userid_add(sq, c)?,
cli::key::userid::Command::Revoke(c) => userid_revoke(sq, c)?,
cli::key::userid::Command::Strip(c) => userid_strip(sq, c)?,
}
Ok(())
@ -356,9 +355,9 @@ fn cert_checksum(cert: &Cert) -> Result<Vec<u8>> {
sum.into_digest()
}
fn userid_strip(
pub fn userid_strip(
sq: Sq,
mut command: cli::key::userid::StripCommand,
mut command: cli::toolbox::strip_userid::Command,
) -> Result<()> {
let cert = if let Some(file) = &command.cert_file {
if command.output.is_none() {
@ -390,7 +389,7 @@ fn userid_strip(
fn userid_strip_internal(
sq: Sq,
command: cli::key::userid::StripCommand,
command: cli::toolbox::strip_userid::Command,
key: Cert,
tries: usize,
) -> Result<()> {

View File

@ -25,5 +25,7 @@ pub fn dispatch(sq: Sq, command: Command) -> Result<()>
armor::dispatch(sq, command),
Subcommands::Dearmor(command) =>
dearmor::dispatch(sq, command),
Subcommands::StripUserid(command) =>
crate::commands::key::userid::userid_strip(sq, command),
}
}

View File

@ -945,9 +945,9 @@ impl Sq {
}
/// Strips user IDs to the given key.
pub fn key_userid_strip(&self, key: Cert, args: &[&str]) -> Result<Cert> {
pub fn toolbox_strip_userid(&self, key: Cert, args: &[&str]) -> Result<Cert> {
let mut cmd = self.command();
cmd.args(["key", "userid", "strip"]);
cmd.args(["toolbox", "strip-userid"]);
for arg in args {
cmd.arg(arg);
}

View File

@ -401,7 +401,7 @@ fn sq_key_userid_strip() -> Result<()> {
assert_eq!(key.userids().count(), 3);
// Whoops, that's a secret.
let key = sq.key_userid_strip(key, &[
let key = sq.toolbox_strip_userid(key, &[
"--userid", "<joan@hut8.bletchley.park>",
])?;