aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--nitrocli/CHANGELOG.md2
-rw-r--r--nitrocli/README.md3
-rw-r--r--nitrocli/doc/nitrocli.113
-rw-r--r--nitrocli/src/args.rs88
-rw-r--r--nitrocli/src/commands.rs14
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::*;