opgpcard: Add and improve help texts

This commit is contained in:
Nora Widdecke 2022-10-27 09:23:15 +02:00
parent e656946335
commit f0c9fcc842
No known key found for this signature in database
GPG key ID: 2D4111B31DBB99B6
13 changed files with 329 additions and 69 deletions

View file

@ -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")]
name: String,
},
/// Set cardholder URL /// Set certificate URL
Url { url: String }, Url {
#[clap(help = "URL that provides the certificate for the key material on this card")]
url: String,
},
/// Import a Key. /// Import a Key onto the card.
/// ///
/// If no fingerprint is provided, the key will only be imported if /// Most keys can be imported without specifying subkey fingerprints. However, if the key
/// there are zero or one (sub)keys for each key slot on the card. /// 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 { Import {
#[clap(help = "File that contains the PGP private key")]
keyfile: PathBuf, keyfile: PathBuf,
#[clap(name = "Signature key fingerprint", short = 's', long = "sig-fp")] /// Optionally, select the subkey to import in the SIG slot
#[clap(name = "SIG subkey fingerprint", short = 's', long = "sig-fp")]
sig_fp: Option<String>, sig_fp: Option<String>,
#[clap(name = "Decryption key fingerprint", short = 'd', long = "dec-fp")] /// Optionally, select the subkey to import in the DEC slot
#[clap(name = "DEC subkey fingerprint", short = 'd', long = "dec-fp")]
dec_fp: Option<String>, dec_fp: Option<String>,
#[clap(name = "Authentication key fingerprint", short = 'a', long = "aut-fp")] /// Optionally, select the subkey to import in the AUT slot
#[clap(name = "AUT subkey fingerprint", short = 'a', long = "aut-fp")]
aut_fp: Option<String>, aut_fp: Option<String>,
}, },
/// Generate a Key. /// 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)]

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