diff options
| -rw-r--r-- | nitrocli/CHANGELOG.md | 2 | ||||
| -rw-r--r-- | nitrocli/README.md | 3 | ||||
| -rw-r--r-- | nitrocli/doc/nitrocli.1 | 13 | ||||
| -rw-r--r-- | nitrocli/src/args.rs | 88 | ||||
| -rw-r--r-- | nitrocli/src/commands.rs | 14 | 
5 files changed, 96 insertions, 24 deletions
| diff --git a/nitrocli/CHANGELOG.md b/nitrocli/CHANGELOG.md index ae6b422..f36a859 100644 --- a/nitrocli/CHANGELOG.md +++ b/nitrocli/CHANGELOG.md @@ -8,6 +8,8 @@ Unreleased    - Removed the `hid`, `hidapi-sys` and `pkg-config` dependencies  - Added the `otp` command for working with one-time passwords  - Added the `config` command for reading and writing the device configuration +- Added the `pin` command for managing PINs +  - Renamed the `clear` command to `pin clear`  - Moved `open` and `close` commands as subcommands into newly introduced    `storage` command    - Moved printing of storage related information from `status` command diff --git a/nitrocli/README.md b/nitrocli/README.md index c36caff..68e4da6 100644 --- a/nitrocli/README.md +++ b/nitrocli/README.md @@ -12,7 +12,6 @@ certain commands on the [Nitrokey Storage][nitrokey-storage] device.  The following commands are currently supported:  - status: Report status information about the Nitrokey. -- clear: Remove the user and admin PIN from gpg-agent's cache.  - config: Access the Nitrokey's configuration    - get: Read the current configuration.    - set: Change the configuration. @@ -25,6 +24,8 @@ The following commands are currently supported:    - set: Set an OTP slot.    - status: List all OTP slots.    - clear: Delete an OTP slot. +- pin: Manage the Nitrokey's PINs. +  - clear: Remove the user and admin PIN from gpg-agent's cache.  ### *Note:*  ---------------------------------------------------------------------- diff --git a/nitrocli/doc/nitrocli.1 b/nitrocli/doc/nitrocli.1 index 21aab03..ef56b22 100644 --- a/nitrocli/doc/nitrocli.1 +++ b/nitrocli/doc/nitrocli.1 @@ -16,8 +16,6 @@ It can be used to access the encrypted volume and the one-time password generato  Print the status of the connected Nitrokey device, including the stick serial  number, the firmware version, and the PIN retry count.  .TP -.B nitrocli clear -Clear the passphrases cached by the other commands.  .SS Storage  .TP @@ -120,6 +118,17 @@ passwords using the \fBotp get\fR command.  If \fB\-\-no\-otp\-pin\fR is set, OTP generation can be performed without PIN.  These two options are mutually exclusive. +.SS PINs +Nitrokey devices have two PINs: the user PIN and the admin PIN. The user +PIN must have at least six, the admin PIN at least eight characters. The +user PIN is required for commands such as \fBotp get\fR (depending on +the configuration) and for all \fBpws\fR commands. +The admin PIN is usually required to change the device configuration. + +.TP +.B nitrocli pin clear +Clear the PINs cached by the other commands. +  .SH EXAMPLES  .SS One-time passwords  Configure a one-time password slot with a hexadecimal secret representation: diff --git a/nitrocli/src/args.rs b/nitrocli/src/args.rs index b4733f6..edfd811 100644 --- a/nitrocli/src/args.rs +++ b/nitrocli/src/args.rs @@ -30,9 +30,9 @@ type Result<T> = result::Result<T, Error>;  /// A top-level command for nitrocli.  #[derive(Debug)]  pub enum Command { -  Clear,    Config,    Otp, +  Pin,    Status,    Storage,  } @@ -41,9 +41,9 @@ impl Command {    /// Execute this command with the given arguments.    pub fn execute(&self, args: Vec<String>) -> Result<()> {      match *self { -      Command::Clear => clear(args),        Command::Config => config(args),        Command::Otp => otp(args), +      Command::Pin => pin(args),        Command::Status => status(args),        Command::Storage => storage(args),      } @@ -56,9 +56,9 @@ impl fmt::Display for Command {        f,        "{}",        match *self { -        Command::Clear => "clear",          Command::Config => "config",          Command::Otp => "otp", +        Command::Pin => "pin",          Command::Status => "status",          Command::Storage => "storage",        } @@ -71,9 +71,9 @@ impl str::FromStr for Command {    fn from_str(s: &str) -> result::Result<Self, Self::Err> {      match s { -      "clear" => Ok(Command::Clear),        "config" => Ok(Command::Config),        "otp" => Ok(Command::Otp), +      "pin" => Ok(Command::Pin),        "status" => Ok(Command::Status),        "storage" => Ok(Command::Storage),        _ => Err(()), @@ -275,6 +275,42 @@ impl From<OtpMode> for nitrokey::OtpMode {    }  } +#[derive(Debug)] +enum PinCommand { +  Clear, +} + +impl PinCommand { +  fn execute(&self, args: Vec<String>) -> Result<()> { +    match *self { +      PinCommand::Clear => pin_clear(args), +    } +  } +} + +impl fmt::Display for PinCommand { +  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +    write!( +      f, +      "{}", +      match *self { +        PinCommand::Clear => "clear", +      } +    ) +  } +} + +impl str::FromStr for PinCommand { +  type Err = (); + +  fn from_str(s: &str) -> result::Result<Self, Self::Err> { +    match s { +      "clear" => Ok(PinCommand::Clear), +      _ => Err(()), +    } +  } +} +  fn parse(parser: &argparse::ArgumentParser<'_>, args: Vec<String>) -> Result<()> {    if let Err(err) = parser.parse(args, &mut io::stdout(), &mut io::stderr()) {      Err(Error::ArgparseError(err)) @@ -387,15 +423,6 @@ fn storage_status(args: Vec<String>) -> Result<()> {    commands::storage_status()  } -/// Clear the PIN as cached by various other commands. -fn clear(args: Vec<String>) -> Result<()> { -  let mut parser = argparse::ArgumentParser::new(); -  parser.set_description("Clears the cached passphrases"); -  parse(&parser, args)?; - -  commands::clear() -} -  /// Execute a config subcommand.  fn config(args: Vec<String>) -> Result<()> {    let mut subcommand = ConfigCommand::Get; @@ -653,6 +680,39 @@ fn otp_status(args: Vec<String>) -> Result<()> {    commands::otp_status(all)  } +/// Execute a PIN subcommand. +fn pin(args: Vec<String>) -> Result<()> { +  let mut subcommand = PinCommand::Clear; +  let mut subargs = vec![]; +  let mut parser = argparse::ArgumentParser::new(); +  parser.set_description("Manages the Nitrokey PINs"); +  let _ = parser.refer(&mut subcommand).required().add_argument( +    "subcommand", +    argparse::Store, +    "The subcommand to execute (clear)", +  ); +  let _ = parser.refer(&mut subargs).add_argument( +    "arguments", +    argparse::List, +    "The arguments for the subcommand", +  ); +  parser.stop_on_first_argument(true); +  parse(&parser, args)?; +  drop(parser); + +  subargs.insert(0, format!("nitrocli pin {}", subcommand)); +  subcommand.execute(subargs) +} + +/// Clear the PIN as cached by various other commands. +fn pin_clear(args: Vec<String>) -> Result<()> { +  let mut parser = argparse::ArgumentParser::new(); +  parser.set_description("Clears the cached PINs"); +  parse(&parser, args)?; + +  commands::pin_clear() +} +  /// Parse the command-line arguments and return the selected command and  /// the remaining arguments for the command.  fn parse_arguments(args: Vec<String>) -> Result<(Command, Vec<String>)> { @@ -663,7 +723,7 @@ fn parse_arguments(args: Vec<String>) -> Result<(Command, Vec<String>)> {    let _ = parser.refer(&mut command).required().add_argument(      "command",      argparse::Store, -    "The command to execute (clear|config|otp|status|storage)", +    "The command to execute (config|otp|pin|status|storage)",    );    let _ = parser.refer(&mut subargs).add_argument(      "arguments", diff --git a/nitrocli/src/commands.rs b/nitrocli/src/commands.rs index 17c8c8c..7f25415 100644 --- a/nitrocli/src/commands.rs +++ b/nitrocli/src/commands.rs @@ -275,13 +275,6 @@ pub fn storage_status() -> Result<()> {    Ok(())  } -/// Clear the PIN stored when opening the nitrokey's encrypted volume. -pub fn clear() -> Result<()> { -  pinentry::clear_passphrase(pinentry::PinType::Admin)?; -  pinentry::clear_passphrase(pinentry::PinType::User)?; -  Ok(()) -} -  /// Return a String representation of the given Option.  fn format_option<T: fmt::Display>(option: Option<T>) -> String {    match option { @@ -474,6 +467,13 @@ pub fn otp_status(all: bool) -> Result<()> {    Ok(())  } +/// Clear the PIN stored by various operations. +pub fn pin_clear() -> Result<()> { +  pinentry::clear_passphrase(pinentry::PinType::Admin)?; +  pinentry::clear_passphrase(pinentry::PinType::User)?; +  Ok(()) +} +  #[cfg(test)]  mod tests {    use super::*; | 
