Move cli parser for sq key subkey bind to its own module.
This commit is contained in:
parent
909016e3ae
commit
cbe7eb1402
@ -8,8 +8,6 @@ use sequoia_openpgp as openpgp;
|
||||
use openpgp::cert::CipherSuite as SqCipherSuite;
|
||||
use openpgp::types::ReasonForRevocation;
|
||||
|
||||
use crate::cli::types::EncryptPurpose;
|
||||
|
||||
use crate::cli::examples;
|
||||
use examples::Action;
|
||||
use examples::Actions;
|
||||
|
@ -1,24 +1,7 @@
|
||||
use clap::{ArgGroup, Args, Subcommand};
|
||||
|
||||
use sequoia_openpgp as openpgp;
|
||||
use openpgp::KeyHandle;
|
||||
|
||||
use crate::cli::types::ClapData;
|
||||
use crate::cli::types::ExpirationArg;
|
||||
use crate::cli::types::FileOrStdout;
|
||||
use crate::cli::types::Time;
|
||||
|
||||
use crate::cli::key::EncryptPurpose;
|
||||
|
||||
use crate::cli::examples;
|
||||
use examples::Action;
|
||||
use examples::Actions;
|
||||
use examples::Example;
|
||||
use examples::Setup;
|
||||
use crate::cli::types::CertDesignators;
|
||||
use crate::cli::types::cert_designator;
|
||||
use clap::Subcommand;
|
||||
|
||||
pub mod add;
|
||||
pub mod bind;
|
||||
pub mod delete;
|
||||
pub mod expire;
|
||||
pub mod export;
|
||||
@ -45,183 +28,5 @@ pub enum Command {
|
||||
Password(password::Command),
|
||||
Expire(expire::Command),
|
||||
Revoke(revoke::Command),
|
||||
Bind(BindCommand),
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
#[clap(
|
||||
name = "bind",
|
||||
about = "Bind keys from one certificate to another",
|
||||
long_about = "\
|
||||
Bind keys from one certificate to another.
|
||||
|
||||
This command allows the user to attach a primary key or a subkey \
|
||||
attached to one certificate to another certificate. Say you want to \
|
||||
transition to a new certificate, but have an authentication subkey on \
|
||||
your current certificate that you want to keep because it allows access \
|
||||
a server and updating its configuration is not feasible. This command \
|
||||
makes it easy to attach the subkey to the new certificate.
|
||||
|
||||
After the operation, the key is bound both to the old certificate and to \
|
||||
the new one. To remove secret key material from the old certificate, use \
|
||||
`sq key subkey delete` or `sq key delete`, as appropriate. To revoke the \
|
||||
old subkey or key, use `sq key subkey revoke` or `sq key revoke`, \
|
||||
respectively.
|
||||
",
|
||||
after_help = BIND_EXAMPLES,
|
||||
)]
|
||||
#[clap(group(ArgGroup::new("cap-sign").args(&["can_sign", "cannot_sign"])))]
|
||||
#[clap(group(ArgGroup::new("cap-authenticate").args(&["can_authenticate", "cannot_authenticate"])))]
|
||||
#[clap(group(ArgGroup::new("cap-encrypt").args(&["can_encrypt", "cannot_encrypt"])))]
|
||||
pub struct BindCommand {
|
||||
#[command(flatten)]
|
||||
pub cert: CertDesignators<
|
||||
cert_designator::CertUserIDEmailFileArgs,
|
||||
cert_designator::NoPrefix,
|
||||
cert_designator::OneValueAndFileRequiresOutput,
|
||||
SubkeyBindDoc>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
value_name = "KEY",
|
||||
required(true),
|
||||
help = "Add the key or subkey KEY to the certificate",
|
||||
)]
|
||||
pub key: Vec<KeyHandle>,
|
||||
#[clap(
|
||||
long,
|
||||
allow_hyphen_values = true,
|
||||
value_name = "CREATION_TIME",
|
||||
help = "Make bound subkeys have the specified creation time",
|
||||
long_help = "\
|
||||
Make bound subkeys have the specified creation time.
|
||||
|
||||
Normally, the key's creation time is preserved. The exception is if \
|
||||
the key's creation time is the Unix epoch. In that case, the current \
|
||||
time is used.
|
||||
|
||||
This option allows setting the key's creation time to a specified value. \
|
||||
Note: changing a key's creation time also changes its fingerprint. \
|
||||
Changing the fingerprint will make it impossible to look up the key for \
|
||||
the purpose of signature verification, for example.",
|
||||
)]
|
||||
pub creation_time: Option<Time>,
|
||||
#[clap(flatten)]
|
||||
pub expiration: ExpirationArg,
|
||||
#[clap(
|
||||
long,
|
||||
help = "Allow adopting keys from certificates \
|
||||
using broken cryptography",
|
||||
)]
|
||||
pub allow_broken_crypto: bool,
|
||||
|
||||
#[clap(
|
||||
long = "can-sign",
|
||||
help ="Set the signing-capable flag",
|
||||
)]
|
||||
pub can_sign: bool,
|
||||
#[clap(
|
||||
long = "cannot-sign",
|
||||
help = "Don't set the signing-capable flag",
|
||||
)]
|
||||
pub cannot_sign: bool,
|
||||
#[clap(
|
||||
long = "can-authenticate",
|
||||
help = "Set the authentication-capable flag",
|
||||
)]
|
||||
pub can_authenticate: bool,
|
||||
#[clap(
|
||||
long = "cannot-authenticate",
|
||||
help = "Don't set the authentication-capable flag",
|
||||
)]
|
||||
pub cannot_authenticate: bool,
|
||||
#[clap(
|
||||
long = "can-encrypt",
|
||||
value_name = "PURPOSE",
|
||||
help = "Set the encryption-capable flag",
|
||||
long_help = "\
|
||||
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 = "cannot-encrypt",
|
||||
help = "Don't set the encryption-capable flag",
|
||||
)]
|
||||
pub cannot_encrypt: 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 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,
|
||||
help = "Emit binary data",
|
||||
)]
|
||||
pub binary: bool,
|
||||
}
|
||||
|
||||
const BIND_EXAMPLES: Actions = Actions {
|
||||
actions: &[
|
||||
Action::Setup(Setup {
|
||||
command: &[
|
||||
"sq", "key", "import",
|
||||
"alice-secret.pgp", "alice-new-secret.pgp",
|
||||
],
|
||||
}),
|
||||
Action::Example(Example {
|
||||
comment: "\
|
||||
Bind Alice's old authentication subkey to Alice's new certificate.",
|
||||
command: &[
|
||||
"sq", "key", "subkey", "bind",
|
||||
"--cert=C5999E8191BF7B503653BE958B1F7910D01F86E5",
|
||||
"--key=0D45C6A756A038670FDFD85CB1C82E8D27DB23A1",
|
||||
],
|
||||
}),
|
||||
Action::Example(Example {
|
||||
comment: "\
|
||||
Bind a bare key to Alice's certificate. A bare key is a public key \
|
||||
without any components or signatures. This simplifies working with raw \
|
||||
keys, e.g., keys generated on an OpenPGP card, a TPM device, etc.",
|
||||
command: &[
|
||||
"sq", "key", "subkey", "bind",
|
||||
"--keyring=bare.pgp",
|
||||
"--cert=C5999E8191BF7B503653BE958B1F7910D01F86E5",
|
||||
"--key=B321BA8F650CB16443E06826DBFA98A78CF6562F",
|
||||
"--can-encrypt=universal",
|
||||
],
|
||||
}),
|
||||
]
|
||||
};
|
||||
test_examples!(sq_key_bind, BIND_EXAMPLES);
|
||||
|
||||
/// Documentation for the cert argument of the key subkey bind
|
||||
/// command.
|
||||
pub struct SubkeyBindDoc {}
|
||||
|
||||
impl cert_designator::AdditionalDocs for SubkeyBindDoc {
|
||||
fn help(arg: &'static str, help: &'static str) -> clap::builder::StyledStr {
|
||||
match arg {
|
||||
"file" =>
|
||||
"Add the specified subkeys to the key read from PATH"
|
||||
.into(),
|
||||
_ => {
|
||||
debug_assert!(help.starts_with("Use certificates"));
|
||||
help.replace("Use certificates",
|
||||
"Add the specified subkeys on the key")
|
||||
.into()
|
||||
},
|
||||
}
|
||||
}
|
||||
Bind(bind::Command),
|
||||
}
|
||||
|
195
src/cli/key/subkey/bind.rs
Normal file
195
src/cli/key/subkey/bind.rs
Normal file
@ -0,0 +1,195 @@
|
||||
use clap::Args;
|
||||
use clap::ArgGroup;
|
||||
|
||||
use sequoia_openpgp as openpgp;
|
||||
use openpgp::KeyHandle;
|
||||
|
||||
use crate::cli::examples;
|
||||
use examples::Action;
|
||||
use examples::Actions;
|
||||
use examples::Example;
|
||||
use examples::Setup;
|
||||
|
||||
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::Time;
|
||||
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 the specified subkeys to the key read from PATH"
|
||||
.into(),
|
||||
_ => {
|
||||
debug_assert!(help.starts_with("Use certificates"));
|
||||
help.replace("Use certificates",
|
||||
"Add the specified subkeys on the key")
|
||||
.into()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
#[clap(
|
||||
name = "bind",
|
||||
about = "Bind keys from one certificate to another",
|
||||
long_about = "\
|
||||
Bind keys from one certificate to another.
|
||||
|
||||
This command allows the user to attach a primary key or a subkey \
|
||||
attached to one certificate to another certificate. Say you want to \
|
||||
transition to a new certificate, but have an authentication subkey on \
|
||||
your current certificate that you want to keep because it allows access \
|
||||
a server and updating its configuration is not feasible. This command \
|
||||
makes it easy to attach the subkey to the new certificate.
|
||||
|
||||
After the operation, the key is bound both to the old certificate and to \
|
||||
the new one. To remove secret key material from the old certificate, use \
|
||||
`sq key subkey delete` or `sq key delete`, as appropriate. To revoke the \
|
||||
old subkey or key, use `sq key subkey revoke` or `sq key revoke`, \
|
||||
respectively.
|
||||
",
|
||||
after_help = EXAMPLES,
|
||||
)]
|
||||
#[clap(group(ArgGroup::new("cap-sign").args(&["can_sign", "cannot_sign"])))]
|
||||
#[clap(group(ArgGroup::new("cap-authenticate").args(&["can_authenticate", "cannot_authenticate"])))]
|
||||
#[clap(group(ArgGroup::new("cap-encrypt").args(&["can_encrypt", "cannot_encrypt"])))]
|
||||
pub struct Command {
|
||||
#[command(flatten)]
|
||||
pub cert: CertDesignators<
|
||||
cert_designator::CertUserIDEmailFileArgs,
|
||||
cert_designator::NoPrefix,
|
||||
cert_designator::OneValueAndFileRequiresOutput,
|
||||
AdditionalDocs>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
value_name = "KEY",
|
||||
required(true),
|
||||
help = "Add the key or subkey KEY to the certificate",
|
||||
)]
|
||||
pub key: Vec<KeyHandle>,
|
||||
#[clap(
|
||||
long,
|
||||
allow_hyphen_values = true,
|
||||
value_name = "CREATION_TIME",
|
||||
help = "Make bound subkeys have the specified creation time",
|
||||
long_help = "\
|
||||
Make bound subkeys have the specified creation time.
|
||||
|
||||
Normally, the key's creation time is preserved. The exception is if \
|
||||
the key's creation time is the Unix epoch. In that case, the current \
|
||||
time is used.
|
||||
|
||||
This option allows setting the key's creation time to a specified value. \
|
||||
Note: changing a key's creation time also changes its fingerprint. \
|
||||
Changing the fingerprint will make it impossible to look up the key for \
|
||||
the purpose of signature verification, for example.",
|
||||
)]
|
||||
pub creation_time: Option<Time>,
|
||||
#[clap(flatten)]
|
||||
pub expiration: ExpirationArg,
|
||||
#[clap(
|
||||
long,
|
||||
help = "Allow adopting keys from certificates \
|
||||
using broken cryptography",
|
||||
)]
|
||||
pub allow_broken_crypto: bool,
|
||||
|
||||
#[clap(
|
||||
long = "can-sign",
|
||||
help ="Set the signing-capable flag",
|
||||
)]
|
||||
pub can_sign: bool,
|
||||
#[clap(
|
||||
long = "cannot-sign",
|
||||
help = "Don't set the signing-capable flag",
|
||||
)]
|
||||
pub cannot_sign: bool,
|
||||
#[clap(
|
||||
long = "can-authenticate",
|
||||
help = "Set the authentication-capable flag",
|
||||
)]
|
||||
pub can_authenticate: bool,
|
||||
#[clap(
|
||||
long = "cannot-authenticate",
|
||||
help = "Don't set the authentication-capable flag",
|
||||
)]
|
||||
pub cannot_authenticate: bool,
|
||||
#[clap(
|
||||
long = "can-encrypt",
|
||||
value_name = "PURPOSE",
|
||||
help = "Set the encryption-capable flag",
|
||||
long_help = "\
|
||||
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 = "cannot-encrypt",
|
||||
help = "Don't set the encryption-capable flag",
|
||||
)]
|
||||
pub cannot_encrypt: 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 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,
|
||||
help = "Emit binary data",
|
||||
)]
|
||||
pub binary: bool,
|
||||
}
|
||||
|
||||
const EXAMPLES: Actions = Actions {
|
||||
actions: &[
|
||||
Action::Setup(Setup {
|
||||
command: &[
|
||||
"sq", "key", "import",
|
||||
"alice-secret.pgp", "alice-new-secret.pgp",
|
||||
],
|
||||
}),
|
||||
Action::Example(Example {
|
||||
comment: "\
|
||||
Bind Alice's old authentication subkey to Alice's new certificate.",
|
||||
command: &[
|
||||
"sq", "key", "subkey", "bind",
|
||||
"--cert=C5999E8191BF7B503653BE958B1F7910D01F86E5",
|
||||
"--key=0D45C6A756A038670FDFD85CB1C82E8D27DB23A1",
|
||||
],
|
||||
}),
|
||||
Action::Example(Example {
|
||||
comment: "\
|
||||
Bind a bare key to Alice's certificate. A bare key is a public key \
|
||||
without any components or signatures. This simplifies working with raw \
|
||||
keys, e.g., keys generated on an OpenPGP card, a TPM device, etc.",
|
||||
command: &[
|
||||
"sq", "key", "subkey", "bind",
|
||||
"--keyring=bare.pgp",
|
||||
"--cert=C5999E8191BF7B503653BE958B1F7910D01F86E5",
|
||||
"--key=B321BA8F650CB16443E06826DBFA98A78CF6562F",
|
||||
"--can-encrypt=universal",
|
||||
],
|
||||
}),
|
||||
]
|
||||
};
|
||||
test_examples!(sq_key_bind, EXAMPLES);
|
@ -20,7 +20,7 @@ use crate::cli;
|
||||
use cli::types::EncryptPurpose;
|
||||
use crate::common::password;
|
||||
|
||||
pub fn bind(sq: Sq, command: cli::key::subkey::BindCommand) -> Result<()>
|
||||
pub fn bind(sq: Sq, command: cli::key::subkey::bind::Command) -> Result<()>
|
||||
{
|
||||
let handle =
|
||||
sq.resolve_cert(&command.cert, sequoia_wot::FULLY_TRUSTED)?.1;
|
||||
|
Loading…
Reference in New Issue
Block a user