From cc3aa7f14eaec746e6718ef155a59e10c67a03fb Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 30 Dec 2018 18:32:45 +0100 Subject: Implement the pin set command This change implements the pin set command which can be used to change a Nitrokey's user or admin PIN. --- nitrocli/README.md | 3 +-- nitrocli/doc/nitrocli.1 | 8 ++++++++ nitrocli/src/args.rs | 23 ++++++++++++++++++++++- nitrocli/src/commands.rs | 14 ++++++++++++++ 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 @@ -138,6 +138,14 @@ Use the \fBstatus\fR command to check the retry counters. .B nitrocli pin clear 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. 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 = result::Result; @@ -278,6 +279,7 @@ impl From for nitrokey::OtpMode { #[derive(Debug)] enum PinCommand { Clear, + Set, Unblock, } @@ -285,6 +287,7 @@ impl PinCommand { fn execute(&self, args: Vec) -> 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 { match s { "clear" => Ok(PinCommand::Clear), + "set" => Ok(PinCommand::Set), "unblock" => Ok(PinCommand::Unblock), _ => Err(()), } @@ -693,7 +698,7 @@ fn pin(args: Vec) -> 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) -> Result<()> { commands::pin_clear() } +/// Change a PIN. +fn pin_set(args: Vec) -> 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) -> 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 { } } +/// 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 . * // ************************************************************************* +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 { + match s { + "admin" => Ok(PinType::Admin), + "user" => Ok(PinType::User), + _ => Err(()), + } + } +} + impl PinType { fn cache_id(self) -> &'static str { match self { -- cgit v1.2.3