Move cli parser for sq key subkey add to its own module.

This commit is contained in:
Neal H. Walfield 2024-11-05 13:54:58 +01:00
parent 26c14e1a54
commit 0dffab8d50
No known key found for this signature in database
GPG Key ID: 6863C9AD5B4D22D3
3 changed files with 189 additions and 173 deletions

View File

@ -12,7 +12,6 @@ use crate::cli::types::FileOrStdout;
use crate::cli::types::Time;
use crate::cli::key::{
CipherSuite,
EncryptPurpose,
KeyReasonForRevocation,
};
@ -26,6 +25,8 @@ use crate::cli::types::CertDesignators;
use crate::cli::types::KeyDesignators;
use crate::cli::types::cert_designator;
pub mod add;
#[derive(Debug, Subcommand)]
#[clap(
name = "subkey",
@ -40,7 +41,7 @@ and revoke them.",
)]
#[non_exhaustive]
pub enum Command {
Add(AddCommand),
Add(add::Command),
Export(ExportCommand),
Delete(DeleteCommand),
Password(PasswordCommand),
@ -49,176 +50,6 @@ pub enum Command {
Bind(BindCommand),
}
const SUBKEY_ADD_EXAMPLES: Actions = Actions {
actions: &[
Action::Setup(Setup {
command: &[
"sq", "key", "import",
"alice-secret.pgp",
],
}),
Action::Example(Example {
comment: "\
Add a new signing-capable subkey to Alice's key.",
command: &[
"sq", "key", "subkey", "add",
"--without-password",
"--can-sign",
"--cert=EB28F26E2739A4870ECC47726F0073F60FD0CBF0",
],
}),
]
};
test_examples!(sq_key_subkey_add, SUBKEY_ADD_EXAMPLES);
#[derive(Debug, Args)]
#[clap(
about = "Add a new subkey to a certificate",
long_about = "\
Add a new subkey to a certificate.
A subkey has one or more capabilities.
`--can-sign` sets the signing capability, and means that the key may \
be used for signing. `--can-authenticate` sets the authentication \
capability, and means that the key may be used for authentication \
(e.g., as an SSH key). `--can-certify` sets the certificate \
capability, and means that the key may be used to make third-party \
certifications. These capabilities may be combined.
`--can-encrypt=storage` sets the storage encryption capability, and \
means that the key may be used for storage \
encryption. `--can-encrypt=transport` sets the transport encryption \
capability, and means that the key may be used for transport \
encryption. `--can-encrypt=universal` sets both the storage and the \
transport encryption capability, and means that the key may be used \
for both storage and transport encryption. The encryption \
capabilities must not be combined with the signing or authentication \
capability.
Normally, `sq` prompts the user for a password to use to encrypt the \
secret key material. The password for the new subkey may be different \
from the other keys. When using `--without-password`, `sq` doesn't \
prompt for a password, and doesn't password-protect the subkey.
By default a new subkey doesn't expire on its own. However, its \
validity period is limited by that of the certificate. Using the \
`--expiration` argument allows setting a different expiration time.
`sq key subkey add` respects the reference time set by the top-level \
`--time` argument. It sets the creation time of the subkey to the specified \
time.
",
after_help = SUBKEY_ADD_EXAMPLES,
)]
#[clap(group(ArgGroup::new("authentication-group").args(&["can_authenticate", "can_encrypt"])))]
#[clap(group(ArgGroup::new("sign-group").args(&["can_sign", "can_encrypt"])))]
#[clap(group(ArgGroup::new("required-group").args(&["can_authenticate", "can_sign", "can_encrypt"]).required(true)))]
pub struct AddCommand {
#[command(flatten)]
pub cert: CertDesignators<
cert_designator::CertUserIDEmailFileArgs,
cert_designator::NoPrefix,
cert_designator::OneValueAndFileRequiresOutput,
SubkeyAddDoc>,
#[clap(
long,
value_name = "CIPHER-SUITE",
default_value_t = CipherSuite::Cv25519,
help = "Select the cryptographic algorithms for the subkey",
value_enum,
)]
pub cipher_suite: CipherSuite,
#[command(flatten)]
pub expiration: ExpirationArg,
#[clap(
long,
help = "Add a signing-capable subkey",
)]
pub can_sign: bool,
#[clap(
long,
help = "Add an authentication-capable subkey",
)]
pub can_authenticate: bool,
#[clap(
long,
value_name = "PURPOSE",
help = "Add an encryption-capable subkey [default: universal]",
long_help = "\
Add an encryption-capable subkey.
Encryption-capable subkeys can be marked as suitable for transport \
encryption, storage encryption, or both, i.e., universal. [default: \
universal]",
value_enum,
)]
pub can_encrypt: Option<EncryptPurpose>,
#[clap(
long = "new-password-file",
value_name = "PASSWORD_FILE",
help = "\
File containing password to encrypt the secret key material",
long_help = "\
File containing password to encrypt the secret key material.
Note that the entire key file will be used as the password including \
any surrounding whitespace like a trailing newline.",
conflicts_with = "without_password",
)]
pub new_password_file: Option<PathBuf>,
#[clap(
long,
help = "Don't protect the subkey's secret key material with a password",
)]
pub without_password: bool,
#[clap(
long,
value_name = FileOrStdout::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 key store. If not \
specified, and the certificate was read from a file, writes the \
modified certificate to stdout.",
)]
pub output: Option<FileOrStdout>,
#[clap(
long,
help = "Emit binary data",
)]
pub binary: bool,
}
/// Documentation for the cert designators for the key subkey add
/// command.
pub struct SubkeyAddDoc {}
impl cert_designator::AdditionalDocs for SubkeyAddDoc {
fn help(arg: &'static str, help: &'static str) -> clap::builder::StyledStr {
match arg {
"file" =>
"Add a subkey to the key read from PATH"
.into(),
_ => {
debug_assert!(help.starts_with("Use certificates"));
help.replace("Use certificates",
"Add a subkey to the key")
.into()
},
}
}
}
const SUBKEY_EXPORT_EXAMPLES: Actions = Actions {
actions: &[
Action::Setup(Setup {

185
src/cli/key/subkey/add.rs Normal file
View File

@ -0,0 +1,185 @@
use std::path::PathBuf;
use clap::Args;
use clap::ArgGroup;
use crate::cli::examples;
use examples::Action;
use examples::Actions;
use examples::Example;
use examples::Setup;
use crate::cli::key::CipherSuite;
use crate::cli::types::CertDesignators;
use crate::cli::types::ClapData;
use crate::cli::types::EncryptPurpose;
use crate::cli::types::ExpirationArg;
use crate::cli::types::FileOrStdout;
use crate::cli::types::cert_designator;
pub struct AdditionalDocs {}
impl cert_designator::AdditionalDocs for AdditionalDocs {
fn help(arg: &'static str, help: &'static str) -> clap::builder::StyledStr {
match arg {
"file" =>
"Add a subkey to the key read from PATH"
.into(),
_ => {
debug_assert!(help.starts_with("Use certificates"));
help.replace("Use certificates",
"Add a subkey to the key")
.into()
},
}
}
}
#[derive(Debug, Args)]
#[clap(
about = "Add a new subkey to a certificate",
long_about = "\
Add a new subkey to a certificate.
A subkey has one or more capabilities.
`--can-sign` sets the signing capability, and means that the key may \
be used for signing. `--can-authenticate` sets the authentication \
capability, and means that the key may be used for authentication \
(e.g., as an SSH key). `--can-certify` sets the certificate \
capability, and means that the key may be used to make third-party \
certifications. These capabilities may be combined.
`--can-encrypt=storage` sets the storage encryption capability, and \
means that the key may be used for storage \
encryption. `--can-encrypt=transport` sets the transport encryption \
capability, and means that the key may be used for transport \
encryption. `--can-encrypt=universal` sets both the storage and the \
transport encryption capability, and means that the key may be used \
for both storage and transport encryption. The encryption \
capabilities must not be combined with the signing or authentication \
capability.
Normally, `sq` prompts the user for a password to use to encrypt the \
secret key material. The password for the new subkey may be different \
from the other keys. When using `--without-password`, `sq` doesn't \
prompt for a password, and doesn't password-protect the subkey.
By default a new subkey doesn't expire on its own. However, its \
validity period is limited by that of the certificate. Using the \
`--expiration` argument allows setting a different expiration time.
`sq key subkey add` respects the reference time set by the top-level \
`--time` argument. It sets the creation time of the subkey to the specified \
time.
",
after_help = EXAMPLES,
)]
#[clap(group(ArgGroup::new("authentication-group").args(&["can_authenticate", "can_encrypt"])))]
#[clap(group(ArgGroup::new("sign-group").args(&["can_sign", "can_encrypt"])))]
#[clap(group(ArgGroup::new("required-group").args(&["can_authenticate", "can_sign", "can_encrypt"]).required(true)))]
pub struct Command {
#[command(flatten)]
pub cert: CertDesignators<
cert_designator::CertUserIDEmailFileArgs,
cert_designator::NoPrefix,
cert_designator::OneValueAndFileRequiresOutput,
AdditionalDocs>,
#[clap(
long,
value_name = "CIPHER-SUITE",
default_value_t = CipherSuite::Cv25519,
help = "Select the cryptographic algorithms for the subkey",
value_enum,
)]
pub cipher_suite: CipherSuite,
#[command(flatten)]
pub expiration: ExpirationArg,
#[clap(
long,
help = "Add a signing-capable subkey",
)]
pub can_sign: bool,
#[clap(
long,
help = "Add an authentication-capable subkey",
)]
pub can_authenticate: bool,
#[clap(
long,
value_name = "PURPOSE",
help = "Add an encryption-capable subkey [default: universal]",
long_help = "\
Add an encryption-capable subkey.
Encryption-capable subkeys can be marked as suitable for transport \
encryption, storage encryption, or both, i.e., universal. [default: \
universal]",
value_enum,
)]
pub can_encrypt: Option<EncryptPurpose>,
#[clap(
long = "new-password-file",
value_name = "PASSWORD_FILE",
help = "\
File containing password to encrypt the secret key material",
long_help = "\
File containing password to encrypt the secret key material.
Note that the entire key file will be used as the password including \
any surrounding whitespace like a trailing newline.",
conflicts_with = "without_password",
)]
pub new_password_file: Option<PathBuf>,
#[clap(
long,
help = "Don't protect the subkey's secret key material with a password",
)]
pub without_password: bool,
#[clap(
long,
value_name = FileOrStdout::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 key store. If not \
specified, and the certificate was read from a file, writes the \
modified certificate to stdout.",
)]
pub output: Option<FileOrStdout>,
#[clap(
long,
help = "Emit binary data",
)]
pub binary: bool,
}
const EXAMPLES: Actions = Actions {
actions: &[
Action::Setup(Setup {
command: &[
"sq", "key", "import",
"alice-secret.pgp",
],
}),
Action::Example(Example {
comment: "\
Add a new signing-capable subkey to Alice's key.",
command: &[
"sq", "key", "subkey", "add",
"--without-password",
"--can-sign",
"--cert=EB28F26E2739A4870ECC47726F0073F60FD0CBF0",
],
}),
]
};
test_examples!(sq_key_subkey_add, EXAMPLES);

View File

@ -19,8 +19,8 @@ use openpgp::Packet;
use openpgp::Result;
use crate::Sq;
use crate::cli::key::subkey::AddCommand;
use crate::cli::key::subkey::Command;
use crate::cli::key::subkey::add::Command as AddCommand;
use crate::cli::key::subkey::DeleteCommand;
use crate::cli::key::subkey::ExpireCommand;
use crate::cli::key::subkey::ExportCommand;