diff options
| -rw-r--r-- | nitrocli/README.md | 3 | ||||
| -rw-r--r-- | nitrocli/doc/nitrocli.1 | 8 | ||||
| -rw-r--r-- | nitrocli/src/args.rs | 23 | ||||
| -rw-r--r-- | nitrocli/src/commands.rs | 14 | ||||
| -rw-r--r-- | nitrocli/src/pinentry.rs | 27 | 
5 files changed, 72 insertions, 3 deletions
| diff --git a/nitrocli/README.md b/nitrocli/README.md index ed5e4e4..ac6710f 100644 --- a/nitrocli/README.md +++ b/nitrocli/README.md @@ -15,8 +15,6 @@ The following commands are currently supported:  - config: Access the Nitrokey's configuration    - get: Read the current configuration.    - set: Change the configuration. -- pin: Change the Nitrokey’s PINs -  - unblock: Unblock and reset the user PIN.  - storage: Work with the Nitrokey's storage.    - open: Open the encrypted volume. The user PIN needs to be entered.    - close: Close the encrypted volume. @@ -28,6 +26,7 @@ The following commands are currently supported:    - clear: Delete an OTP slot.  - pin: Manage the Nitrokey's PINs.    - clear: Remove the user and admin PIN from gpg-agent's cache. +  - set: Change the admin or the user PIN.    - unblock: Unblock and reset the user PIN.  ### *Note:* diff --git a/nitrocli/doc/nitrocli.1 b/nitrocli/doc/nitrocli.1 index bec9a15..4e59352 100644 --- a/nitrocli/doc/nitrocli.1 +++ b/nitrocli/doc/nitrocli.1 @@ -139,6 +139,14 @@ Use the \fBstatus\fR command to check the retry counters.  Clear the PINs cached by the other commands.  .TP +\fBnitrocli pin set \fItype\fR +Change a PIN. +\fItype\fR is the type of the PIN that will be changed:  \fBadmin\fR to change +the admin PIN or \fBuser\fR to change the user PIN. +This command only works if the retry counter for the PIN type is at least one. +(Use the \fBstatus\fR command to check the retry counters.) + +.TP  .B nitrocli pin unblock  Unblock and reset the user PIN.  This command requires the admin PIN. diff --git a/nitrocli/src/args.rs b/nitrocli/src/args.rs index 4341235..f00ac2a 100644 --- a/nitrocli/src/args.rs +++ b/nitrocli/src/args.rs @@ -24,6 +24,7 @@ use std::str;  use crate::commands;  use crate::error::Error; +use crate::pinentry;  type Result<T> = result::Result<T, Error>; @@ -278,6 +279,7 @@ impl From<OtpMode> for nitrokey::OtpMode {  #[derive(Debug)]  enum PinCommand {    Clear, +  Set,    Unblock,  } @@ -285,6 +287,7 @@ impl PinCommand {    fn execute(&self, args: Vec<String>) -> Result<()> {      match *self {        PinCommand::Clear => pin_clear(args), +      PinCommand::Set => pin_set(args),        PinCommand::Unblock => pin_unblock(args),      }    } @@ -297,6 +300,7 @@ impl fmt::Display for PinCommand {        "{}",        match *self {          PinCommand::Clear => "clear", +        PinCommand::Set => "set",          PinCommand::Unblock => "unblock",        }      ) @@ -309,6 +313,7 @@ impl str::FromStr for PinCommand {    fn from_str(s: &str) -> result::Result<Self, Self::Err> {      match s {        "clear" => Ok(PinCommand::Clear), +      "set" => Ok(PinCommand::Set),        "unblock" => Ok(PinCommand::Unblock),        _ => Err(()),      } @@ -693,7 +698,7 @@ fn pin(args: Vec<String>) -> Result<()> {    let _ = parser.refer(&mut subcommand).required().add_argument(      "subcommand",      argparse::Store, -    "The subcommand to execute (clear|unblock)", +    "The subcommand to execute (clear|set|unblock)",    );    let _ = parser.refer(&mut subargs).add_argument(      "arguments", @@ -717,6 +722,22 @@ fn pin_clear(args: Vec<String>) -> Result<()> {    commands::pin_clear()  } +/// Change a PIN. +fn pin_set(args: Vec<String>) -> Result<()> { +  let mut pintype = pinentry::PinType::User; +  let mut parser = argparse::ArgumentParser::new(); +  parser.set_description("Changes a PIN"); +  let _ = parser.refer(&mut pintype).required().add_argument( +    "type", +    argparse::Store, +    "The PIN type to change (admin|user)", +  ); +  parse(&parser, args)?; +  drop(parser); + +  commands::pin_set(pintype) +} +  /// Unblock and reset the user PIN.  fn pin_unblock(args: Vec<String>) -> Result<()> {    let mut parser = argparse::ArgumentParser::new(); diff --git a/nitrocli/src/commands.rs b/nitrocli/src/commands.rs index e3e2a14..b018a58 100644 --- a/nitrocli/src/commands.rs +++ b/nitrocli/src/commands.rs @@ -507,6 +507,20 @@ fn choose_pin(pintype: pinentry::PinType) -> Result<String> {    }  } +/// Change a PIN. +pub fn pin_set(pintype: pinentry::PinType) -> Result<()> { +  let device = get_device()?; +  let new_pin = choose_pin(pintype)?; +  try_with_passphrase( +    pintype, +    "Could not change the PIN", +    |current_pin| match pintype { +      pinentry::PinType::Admin => device.change_admin_pin(¤t_pin, &new_pin), +      pinentry::PinType::User => device.change_user_pin(¤t_pin, &new_pin), +    }, +  ) +} +  /// Unblock and reset the user PIN.  pub fn pin_unblock() -> Result<()> {    let device = get_device()?; diff --git a/nitrocli/src/pinentry.rs b/nitrocli/src/pinentry.rs index 33b5266..0d9fc5f 100644 --- a/nitrocli/src/pinentry.rs +++ b/nitrocli/src/pinentry.rs @@ -17,7 +17,9 @@  // * along with this program.  If not, see <http://www.gnu.org/licenses/>. *  // ************************************************************************* +use std::fmt;  use std::process; +use std::str;  use crate::error::Error; @@ -33,6 +35,31 @@ pub enum PinType {    User,  } +impl fmt::Display for PinType { +  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +    write!( +      f, +      "{}", +      match *self { +        PinType::Admin => "admin", +        PinType::User => "user", +      } +    ) +  } +} + +impl str::FromStr for PinType { +  type Err = (); + +  fn from_str(s: &str) -> Result<Self, Self::Err> { +    match s { +      "admin" => Ok(PinType::Admin), +      "user" => Ok(PinType::User), +      _ => Err(()), +    } +  } +} +  impl PinType {    fn cache_id(self) -> &'static str {      match self { | 
