Merge branch 'nora/texts' into 'main'

Add and improve help texts

See merge request openpgp-card/openpgp-card!19
This commit is contained in:
Heiko 2022-10-27 16:22:01 +00:00
commit d689ceb1dc
13 changed files with 350 additions and 90 deletions

View file

@ -20,7 +20,7 @@ sshkeys = "0.3.2"
pem = "1" pem = "1"
rpassword = "6" rpassword = "6"
anyhow = "1" anyhow = "1"
clap = { version = "3", features = ["derive"] } clap = { version = "3", features = ["derive", "wrap_help"] }
env_logger = "0.9" env_logger = "0.9"
log = "0.4" log = "0.4"
serde_json = "1.0.86" serde_json = "1.0.86"
@ -28,6 +28,7 @@ serde = { version = "1.0.145", features = ["derive"] }
semver = "1.0.14" semver = "1.0.14"
serde_yaml = "0.9.13" serde_yaml = "0.9.13"
thiserror = "1.0.37" thiserror = "1.0.37"
indoc = "1"
[build-dependencies] [build-dependencies]
subplot-build = "0.5.0" subplot-build = "0.5.0"

View file

@ -33,10 +33,6 @@ pub struct Cli {
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
pub enum Command { pub enum Command {
/// Show all output versions that are supported. Mark the
/// currently chosen one with a star.
OutputVersions {},
/// Enumerate available OpenPGP cards /// Enumerate available OpenPGP cards
List {}, List {},
@ -46,7 +42,7 @@ pub enum Command {
/// Show technical details about a card /// Show technical details about a card
Info(commands::info::InfoCommand), Info(commands::info::InfoCommand),
/// Display a card's authentication key as an SSH public key /// Show a card's authentication key as an SSH public key
Ssh(commands::ssh::SshCommand), Ssh(commands::ssh::SshCommand),
/// Export the key data on a card as an OpenPGP public key /// Export the key data on a card as an OpenPGP public key
@ -56,20 +52,60 @@ pub enum Command {
Admin(commands::admin::AdminCommand), Admin(commands::admin::AdminCommand),
/// PIN management (change PINs, reset blocked PINs) /// PIN management (change PINs, reset blocked PINs)
#[clap(
long_about = indoc::indoc! { "
PIN management (change PINs, reset blocked PINs)
OpenPGP cards use PINs (numerical passwords) to verify that a user is allowed to \
perform an operation. There are two PINs for regular operation, User PIN and Admin \
PIN, and one optional Resetting Code.
The User PIN is required to use cryptographic operations on a card (such as \
decryption or signing).
The Admin PIN is needed to configure a card (for example to import an OpenPGP key \
into the card) or to unblock the User PIN.
The Resetting Code only allows unblocking the User PIN. This is useful if the user \
doesn't have access to the Admin PIN.
By default, on unconfigured (or factory reset) cards, the User PIN is typically set to
123456, and the Admin PIN is set to 12345678."
},
)]
Pin(commands::pin::PinCommand), Pin(commands::pin::PinCommand),
/// Decrypt data using a card /// Decrypt data using a card
Decrypt(commands::decrypt::DecryptCommand), Decrypt(commands::decrypt::DecryptCommand),
/// Sign data using a card /// Sign data using a card
///
/// Currently, only detached signatures are supported.
Sign(commands::sign::SignCommand), Sign(commands::sign::SignCommand),
/// Attestation management (Yubico) /// Attestation management (Yubico only)
///
/// Yubico implements a proprietary extension to the OpenPGP card standard to
/// cryptographically certify that a certain asymmetric key has been generated on device, and
/// not imported.
///
/// This feature is available on YubiKey 5 devices with firmware version 5.2 or newer.
Attestation(commands::attestation::AttestationCommand), Attestation(commands::attestation::AttestationCommand),
/// Completely reset a card (deletes all data, including the keys on the card!) /// Completely reset a card (deletes all data including keys!)
FactoryReset(commands::factory_reset::FactoryResetCommand), FactoryReset(commands::factory_reset::FactoryResetCommand),
/// Change identity (applies only to Nitrokey Start) /// Change identity (Nitrokey Start only)
///
/// A Nitrokey Start device contains three distinct virtual OpenPGP cards, select the identity
/// of the virtual card to activate.
SetIdentity(commands::set_identity::SetIdentityCommand), SetIdentity(commands::set_identity::SetIdentityCommand),
/// Show supported output format versions
#[clap(
long_about = indoc::indoc! { "
Show supported output format versions for JSON and YAML output.
Mark the currently chosen one with a star."
}
)]
OutputVersions {},
} }

View file

@ -30,10 +30,20 @@ use crate::{output, util, ENTER_ADMIN_PIN, ENTER_USER_PIN};
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
pub struct AdminCommand { pub struct AdminCommand {
#[clap(name = "card ident", short = 'c', long = "card")] #[clap(
name = "card ident",
short = 'c',
long = "card",
help = "Identifier of the card to use"
)]
pub ident: String, pub ident: String,
#[clap(name = "Admin PIN file", short = 'P', long = "admin-pin")] #[clap(
name = "Admin PIN file",
short = 'P',
long = "admin-pin",
help = "Optionally, get Admin PIN from a file"
)]
pub admin_pin: Option<PathBuf>, pub admin_pin: Option<PathBuf>,
#[clap(subcommand)] #[clap(subcommand)]
@ -43,66 +53,118 @@ pub struct AdminCommand {
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
pub enum AdminSubCommand { pub enum AdminSubCommand {
/// Set cardholder name /// Set cardholder name
Name { name: String }, Name {
#[clap(help = "cardholder name to set on the card")]
/// Set cardholder URL name: String,
Url { url: String },
/// Import a Key.
///
/// If no fingerprint is provided, the key will only be imported if
/// there are zero or one (sub)keys for each key slot on the card.
Import {
keyfile: PathBuf,
#[clap(name = "Signature key fingerprint", short = 's', long = "sig-fp")]
sig_fp: Option<String>,
#[clap(name = "Decryption key fingerprint", short = 'd', long = "dec-fp")]
dec_fp: Option<String>,
#[clap(name = "Authentication key fingerprint", short = 'a', long = "auth-fp")]
auth_fp: Option<String>,
}, },
/// Generate a Key. /// Set certificate URL
Url {
#[clap(help = "URL that provides the certificate for the key material on this card")]
url: String,
},
/// Import a Key onto the card.
///
/// Most keys can be imported without specifying subkey fingerprints. However, if the key
/// contins more than one signing, decryption or authentication capable subkey, subkeys must be
/// explicitly selected.
///
/// If any of the options is given, only the selected subkeys are imported into the selected
/// slots.
///
/// Subkey capabilities must match the slot the key is imported into. The DEC slot can
/// only be used for encryption capable subkeys. The SIG and AUT slots can be used for signing,
/// certification and authentication capable subkeys.
Import {
#[clap(help = "File that contains the PGP private key")]
keyfile: PathBuf,
/// Optionally, select the subkey to import in the SIG slot
#[clap(name = "SIG subkey fingerprint", short = 's', long = "sig-fp")]
sig_fp: Option<String>,
/// Optionally, select the subkey to import in the DEC slot
#[clap(name = "DEC subkey fingerprint", short = 'd', long = "dec-fp")]
dec_fp: Option<String>,
/// Optionally, select the subkey to import in the AUT slot
#[clap(name = "AUT subkey fingerprint", short = 'a', long = "aut-fp")]
aut_fp: Option<String>,
},
/// Generate a Key on the card.
/// ///
/// A signing key is always created, decryption and authentication keys /// A signing key is always created, decryption and authentication keys
/// are optional. /// are optional.
Generate(AdminGenerateCommand), Generate(AdminGenerateCommand),
/// Set touch policy /// Set the card's touch policy (if supported)
///
/// A touch policy defines if cryptographic operations on the card require user interaction
/// with the card, for example by touching a button on the card.
///
/// Only some cards support this feature at all, not all cards support all policies.
///
/// Caution: Setting the ATT slot to Fixed or Cached-Fixed is permanent. Even a factory reset does
/// not undo this setting.
Touch { Touch {
/// Key slot to set the touch policy for
#[clap(name = "Key slot", short = 'k', long = "key", value_enum)] #[clap(name = "Key slot", short = 'k', long = "key", value_enum)]
key: BasePlusAttKeySlot, key: BasePlusAttKeySlot,
#[clap(name = "Policy", short = 'p', long = "policy", value_enum)] /// Touch policy to set on this key slot
#[clap(
name = "Policy",
short = 'p',
long = "policy",
value_enum,
long_help = "Touch policy to set on this key slot
Off: No touch confirmation required.
On: Touch confirmation required for each operation.
Fixed: Like 'On', but the policy can only be changed by a reset.
Cached: Like 'On', but touch confirmation is valid for 15 seconds.
Cached-Fixed: Combines 'Cached' and 'Fixed'."
)]
policy: TouchPolicy, policy: TouchPolicy,
}, },
} }
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
pub struct AdminGenerateCommand { pub struct AdminGenerateCommand {
#[clap(name = "User PIN file", short = 'p', long = "user-pin")]
user_pin: Option<PathBuf>,
/// Output file /// Output file
#[clap(name = "output", long = "output", short = 'o')] #[clap(name = "output", long = "output", short = 'o')]
output_file: PathBuf, output_file: PathBuf,
#[clap(long = "no-decrypt", action = clap::ArgAction::SetFalse)] /// Do not create a key in the DEC slot
#[clap(long = "no-dec", action = clap::ArgAction::SetFalse)]
decrypt: bool, decrypt: bool,
#[clap(long = "no-auth", action = clap::ArgAction::SetFalse)] /// Do not create a key in the AUT slot
#[clap(long = "no-aut", action = clap::ArgAction::SetFalse)]
auth: bool, auth: bool,
/// Algorithm /// Choose the algorithm for the key material to generate on the card.
#[clap(value_enum)] ///
/// If the parameter is not given, use the algorithm currently set on the card.
///
/// Specific cards support a set of algorithms that can differ between models. On modern cards,
/// use 'opgpcard info' to see the list of supported algorithms.
#[clap(name = "algorithm", value_enum)]
algo: Option<Algo>, algo: Option<Algo>,
/// User ID to add to the exported certificate representation /// User ID to add to the exported certificate representation
#[clap(name = "User ID", short = 'u', long = "userid")] #[clap(name = "User ID", short = 'u', long = "userid")]
user_ids: Vec<String>, user_ids: Vec<String>,
#[clap(
name = "User PIN file",
short = 'p',
long = "user-pin",
help = "Optionally, get User PIN from a file"
)]
user_pin: Option<PathBuf>,
} }
#[derive(ValueEnum, Debug, Clone)] #[derive(ValueEnum, Debug, Clone)]
@ -161,7 +223,7 @@ pub enum Algo {
Nistp256, Nistp256,
Nistp384, Nistp384,
Nistp521, Nistp521,
Curve25519, Cv25519,
} }
impl From<Algo> for AlgoSimple { impl From<Algo> for AlgoSimple {
@ -173,7 +235,7 @@ impl From<Algo> for AlgoSimple {
Algo::Nistp256 => AlgoSimple::NIST256, Algo::Nistp256 => AlgoSimple::NIST256,
Algo::Nistp384 => AlgoSimple::NIST384, Algo::Nistp384 => AlgoSimple::NIST384,
Algo::Nistp521 => AlgoSimple::NIST521, Algo::Nistp521 => AlgoSimple::NIST521,
Algo::Curve25519 => AlgoSimple::Curve25519, Algo::Cv25519 => AlgoSimple::Curve25519,
} }
} }
} }
@ -200,9 +262,9 @@ pub fn admin(
keyfile, keyfile,
sig_fp, sig_fp,
dec_fp, dec_fp,
auth_fp, aut_fp,
} => { } => {
import_command(keyfile, sig_fp, dec_fp, auth_fp, card, admin_pin.as_deref())?; import_command(keyfile, sig_fp, dec_fp, aut_fp, card, admin_pin.as_deref())?;
} }
AdminSubCommand::Generate(cmd) => { AdminSubCommand::Generate(cmd) => {
generate_command( generate_command(
@ -238,14 +300,14 @@ fn keys_pick_explicit<'a>(
policy: &'a dyn Policy, policy: &'a dyn Policy,
sig_fp: Option<String>, sig_fp: Option<String>,
dec_fp: Option<String>, dec_fp: Option<String>,
auth_fp: Option<String>, aut_fp: Option<String>,
) -> Result<[Option<ValidErasedKeyAmalgamation<'a, SecretParts>>; 3]> { ) -> Result<[Option<ValidErasedKeyAmalgamation<'a, SecretParts>>; 3]> {
let key_by_fp = |fp: Option<String>| match fp { let key_by_fp = |fp: Option<String>| match fp {
Some(fp) => sq_util::private_subkey_by_fingerprint(key, policy, &fp), Some(fp) => sq_util::private_subkey_by_fingerprint(key, policy, &fp),
None => Ok(None), None => Ok(None),
}; };
Ok([key_by_fp(sig_fp)?, key_by_fp(dec_fp)?, key_by_fp(auth_fp)?]) Ok([key_by_fp(sig_fp)?, key_by_fp(dec_fp)?, key_by_fp(aut_fp)?])
} }
fn gen_subkeys( fn gen_subkeys(
@ -321,7 +383,7 @@ fn import_command(
keyfile: PathBuf, keyfile: PathBuf,
sig_fp: Option<String>, sig_fp: Option<String>,
dec_fp: Option<String>, dec_fp: Option<String>,
auth_fp: Option<String>, aut_fp: Option<String>,
mut card: Card<Transaction>, mut card: Card<Transaction>,
admin_pin: Option<&[u8]>, admin_pin: Option<&[u8]>,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
@ -330,12 +392,12 @@ fn import_command(
let p = StandardPolicy::new(); let p = StandardPolicy::new();
// select the (sub)keys to upload // select the (sub)keys to upload
let [sig, dec, auth] = match (&sig_fp, &dec_fp, &auth_fp) { let [sig, dec, auth] = match (&sig_fp, &dec_fp, &aut_fp) {
// No fingerprint has been provided, try to autoselect keys // No fingerprint has been provided, try to autoselect keys
// (this fails if there is more than one (sub)key for any keytype). // (this fails if there is more than one (sub)key for any keytype).
(&None, &None, &None) => keys_pick_yolo(&key, &p)?, (&None, &None, &None) => keys_pick_yolo(&key, &p)?,
_ => keys_pick_explicit(&key, &p, sig_fp, dec_fp, auth_fp)?, _ => keys_pick_explicit(&key, &p, sig_fp, dec_fp, aut_fp)?,
}; };
let mut pws: Vec<String> = vec![]; let mut pws: Vec<String> = vec![];

View file

@ -23,30 +23,59 @@ pub struct AttestationCommand {
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
pub enum AttSubCommand { pub enum AttSubCommand {
/// Print the card's "Attestation Certificate" /// Print the card's attestation certificate
///
/// New YubiKeys are preloaded with an attestation certificate issued by the Yubico CA.
Cert { Cert {
#[clap(name = "card ident", short = 'c', long = "card")] #[clap(
name = "card ident",
short = 'c',
long = "card",
help = "Identifier of the card to use"
)]
ident: Option<String>, ident: Option<String>,
}, },
/// Generate "Attestation Statement" for one of the key slots on the card /// Generate attestation statement for one of the key slots on the card
///
/// An attestation statement can only be generated for key slots that contain keys that were
/// generated by the card. See 'opgpcard admin generate' and 'opgpcard status -v'.
Generate { Generate {
#[clap(name = "card ident", short = 'c', long = "card")] #[clap(
name = "card ident",
short = 'c',
long = "card",
help = "Identifier of the card to use"
)]
ident: String, ident: String,
/// Key slot to use
#[clap(name = "Key slot", short = 'k', long = "key", value_enum)] #[clap(name = "Key slot", short = 'k', long = "key", value_enum)]
key: BaseKeySlot, key: BaseKeySlot,
#[clap(name = "User PIN file", short = 'p', long = "user-pin")] #[clap(
name = "User PIN file",
short = 'p',
long = "user-pin",
help = "Optionally, get User PIN from a file"
)]
user_pin: Option<PathBuf>, user_pin: Option<PathBuf>,
}, },
/// Print a "cardholder certificate" from the card. /// Print the attestation statement for one of the key slots on the card
/// This shows the "Attestation Statement", if one has been generated. ///
/// An attestation statement can only be printed after generating it. See 'opgpcard attestation
/// generate'.
Statement { Statement {
#[clap(name = "card ident", short = 'c', long = "card")] #[clap(
name = "card ident",
short = 'c',
long = "card",
help = "Identifier of the card to reset"
)]
ident: Option<String>, ident: Option<String>,
/// Key slot to use
#[clap(name = "Key slot", short = 'k', long = "key", value_enum)] #[clap(name = "Key slot", short = 'k', long = "key", value_enum)]
key: BaseKeySlot, key: BaseKeySlot,
}, },

View file

@ -17,10 +17,20 @@ use crate::util;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
pub struct DecryptCommand { pub struct DecryptCommand {
#[clap(name = "card ident", short = 'c', long = "card")] #[clap(
name = "card ident",
short = 'c',
long = "card",
help = "Identifier of the card to use"
)]
ident: String, ident: String,
#[clap(name = "User PIN file", short = 'p', long = "user-pin")] #[clap(
name = "User PIN file",
short = 'p',
long = "user-pin",
help = "Optionally, get User PIN from a file"
)]
pin_file: Option<PathBuf>, pin_file: Option<PathBuf>,
/// Input file (stdin if unset) /// Input file (stdin if unset)

View file

@ -10,7 +10,12 @@ use crate::util;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
pub struct FactoryResetCommand { pub struct FactoryResetCommand {
#[clap(name = "card ident", short = 'c', long = "card")] #[clap(
name = "card ident",
short = 'c',
long = "card",
help = "Identifier of the card to use"
)]
ident: String, ident: String,
} }

View file

@ -13,7 +13,12 @@ use crate::versioned_output::{OutputBuilder, OutputFormat, OutputVersion};
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
pub struct InfoCommand { pub struct InfoCommand {
#[clap(name = "card ident", short = 'c', long = "card")] #[clap(
name = "card ident",
short = 'c',
long = "card",
help = "Identifier of the card to use"
)]
pub ident: Option<String>, pub ident: Option<String>,
} }

View file

@ -15,7 +15,12 @@ use crate::{ENTER_ADMIN_PIN, ENTER_USER_PIN};
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
pub struct PinCommand { pub struct PinCommand {
#[clap(name = "card ident", short = 'c', long = "card")] #[clap(
name = "card ident",
short = 'c',
long = "card",
help = "Identifier of the card to use"
)]
pub ident: String, pub ident: String,
#[clap(subcommand)] #[clap(subcommand)]
@ -25,49 +30,111 @@ pub struct PinCommand {
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
pub enum PinSubCommand { pub enum PinSubCommand {
/// Set User PIN /// Set User PIN
///
/// Set a new User PIN by providing the current User PIN.
SetUser { SetUser {
#[clap(name = "User PIN file old", short = 'p', long = "user-pin-old")] #[clap(
name = "User PIN file old",
short = 'p',
long = "user-pin-old",
help = "Optionally, get old User PIN from a file"
)]
user_pin_old: Option<PathBuf>, user_pin_old: Option<PathBuf>,
#[clap(name = "User PIN file new", short = 'q', long = "user-pin-new")] #[clap(
name = "User PIN file new",
short = 'q',
long = "user-pin-new",
help = "Optionally, get new User PIN from a file"
)]
user_pin_new: Option<PathBuf>, user_pin_new: Option<PathBuf>,
}, },
/// Set Admin PIN /// Set Admin PIN
///
/// Set a new Admin PIN by providing the current Admin PIN.
SetAdmin { SetAdmin {
#[clap(name = "Admin PIN file old", short = 'P', long = "admin-pin-old")] #[clap(
name = "Admin PIN file old",
short = 'P',
long = "admin-pin-old",
help = "Optionally, get old Admin PIN from a file"
)]
admin_pin_old: Option<PathBuf>, admin_pin_old: Option<PathBuf>,
#[clap(name = "Admin PIN file new", short = 'Q', long = "admin-pin-new")] #[clap(
name = "Admin PIN file new",
short = 'Q',
long = "admin-pin-new",
help = "Optionally, get new Admin PIN from a file"
)]
admin_pin_new: Option<PathBuf>, admin_pin_new: Option<PathBuf>,
}, },
/// Reset User PIN with Admin PIN /// Reset User PIN with Admin PIN
///
/// Set a new User PIN by providing the Admin PIN. This can also be used if the User PIN has
/// been blocked.
ResetUser { ResetUser {
#[clap(name = "Admin PIN file", short = 'P', long = "admin-pin")] #[clap(
name = "Admin PIN file",
short = 'P',
long = "admin-pin",
help = "Optionally, get Admin PIN from a file"
)]
admin_pin: Option<PathBuf>, admin_pin: Option<PathBuf>,
#[clap(name = "User PIN file new", short = 'p', long = "user-pin-new")] #[clap(
name = "User PIN file new",
short = 'p',
long = "user-pin-new",
help = "Optionally, get new User PIN from a file"
)]
user_pin_new: Option<PathBuf>,
},
/// Reset User PIN with Resetting Code
///
/// Set a new User PIN by providing the Resetting Code. This can also be used if the User PIN
/// has been blocked.
ResetUserRc {
#[clap(
name = "Resetting Code file",
short = 'r',
long = "reset-code",
help = "Optionally, get the Resetting Code from a file"
)]
reset_code: Option<PathBuf>,
#[clap(
name = "User PIN file new",
short = 'p',
long = "user-pin-new",
help = "Optionally, get new User PIN from a file"
)]
user_pin_new: Option<PathBuf>, user_pin_new: Option<PathBuf>,
}, },
/// Set Resetting Code /// Set Resetting Code
///
/// Set a Resetting Code by providing the Admin PIN.
SetReset { SetReset {
#[clap(name = "Admin PIN file", short = 'P', long = "admin-pin")] #[clap(
name = "Admin PIN file",
short = 'P',
long = "admin-pin",
help = "Optionally, get Admin PIN from a file"
)]
admin_pin: Option<PathBuf>, admin_pin: Option<PathBuf>,
#[clap(name = "Resetting code file", short = 'r', long = "reset-code")] #[clap(
name = "Resetting Code file",
short = 'r',
long = "reset-code",
help = "Optionally, get the Resetting Code from a file"
)]
reset_code: Option<PathBuf>, reset_code: Option<PathBuf>,
}, },
/// Reset User PIN with 'Resetting Code'
ResetUserRc {
#[clap(name = "Resetting Code file", short = 'r', long = "reset-code")]
reset_code: Option<PathBuf>,
#[clap(name = "User PIN file new", short = 'p', long = "user-pin-new")]
user_pin_new: Option<PathBuf>,
},
} }
pub fn pin(ident: &str, cmd: PinSubCommand) -> Result<()> { pub fn pin(ident: &str, cmd: PinSubCommand) -> Result<()> {

View file

@ -21,10 +21,20 @@ use crate::versioned_output::{OutputBuilder, OutputFormat, OutputVersion};
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
pub struct PubkeyCommand { pub struct PubkeyCommand {
#[clap(name = "card ident", short = 'c', long = "card")] #[clap(
name = "card ident",
short = 'c',
long = "card",
help = "Identifier of the card to use"
)]
ident: Option<String>, ident: Option<String>,
#[clap(name = "User PIN file", short = 'p', long = "user-pin")] #[clap(
name = "User PIN file",
short = 'p',
long = "user-pin",
help = "Optionally, get User PIN from a file"
)]
user_pin: Option<PathBuf>, user_pin: Option<PathBuf>,
/// User ID to add to the exported certificate representation /// User ID to add to the exported certificate representation

View file

@ -10,9 +10,15 @@ use crate::util;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
pub struct SetIdentityCommand { pub struct SetIdentityCommand {
#[clap(name = "card ident", short = 'c', long = "card")] #[clap(
name = "card ident",
short = 'c',
long = "card",
help = "Identifier of the card to use"
)]
ident: String, ident: String,
/// Identity of the virtual card to activate
#[clap(name = "identity", value_enum)] #[clap(name = "identity", value_enum)]
id: SetIdentityId, id: SetIdentityId,
} }

View file

@ -14,14 +14,28 @@ use crate::util;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
pub struct SignCommand { pub struct SignCommand {
#[clap(name = "card ident", short = 'c', long = "card")] #[clap(
name = "card ident",
short = 'c',
long = "card",
help = "Identifier of the card to use"
)]
pub ident: String, pub ident: String,
/// User PIN file #[clap(
#[clap(short = 'p', long = "user-pin")] name = "User PIN file",
short = 'p',
long = "user-pin",
help = "Optionally, get User PIN from a file"
)]
pub user_pin: Option<PathBuf>, pub user_pin: Option<PathBuf>,
#[clap(name = "detached", short = 'd', long = "detached")] #[clap(
name = "detached",
short = 'd',
long = "detached",
help = "Create a detached signature"
)]
pub detached: bool, pub detached: bool,
/// Input file (stdin if unset) /// Input file (stdin if unset)

View file

@ -16,7 +16,12 @@ use crate::versioned_output::{OutputBuilder, OutputFormat, OutputVersion};
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
pub struct SshCommand { pub struct SshCommand {
#[clap(name = "card ident", short = 'c', long = "card")] #[clap(
name = "card ident",
short = 'c',
long = "card",
help = "Identifier of the card to use"
)]
pub ident: Option<String>, pub ident: Option<String>,
} }

View file

@ -15,10 +15,20 @@ use crate::versioned_output::{OutputBuilder, OutputFormat, OutputVersion};
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
pub struct StatusCommand { pub struct StatusCommand {
#[clap(name = "card ident", short = 'c', long = "card")] #[clap(
name = "card ident",
short = 'c',
long = "card",
help = "Identifier of the card to use"
)]
pub ident: Option<String>, pub ident: Option<String>,
#[clap(name = "verbose", short = 'v', long = "verbose")] #[clap(
name = "verbose",
short = 'v',
long = "verbose",
help = "Use verbose output"
)]
pub verbose: bool, pub verbose: bool,
/// Print public key material for each key slot /// Print public key material for each key slot