aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Krahl <robin.krahl@ireas.org>2018-12-23 02:21:21 +0100
committerDaniel Mueller <deso@posteo.net>2018-12-24 18:15:28 -0800
commitac52b71b0c23c415eede3b8fef50831ae2fe01bc (patch)
tree41581dafc149481e81e4718114d8f280233ec8b8
parentb7b9c6f3591e1ef28371d42229053b5368e79b7d (diff)
downloadnitrocli-ac52b71b0c23c415eede3b8fef50831ae2fe01bc.tar.gz
nitrocli-ac52b71b0c23c415eede3b8fef50831ae2fe01bc.tar.bz2
Implement the otp status subcommand
This patch introduces the `otp status` subcommand that lists all OTP slots and their current status. To avoid hardcoding the number of slots per type, we iterate all slots until we get an `InvalidSlot` error (assuming that the set of valid slots is {0, ..., n} for some n). The `status` command is quite slow as we have to query each slot separately.
-rw-r--r--nitrocli/src/args.rs22
-rw-r--r--nitrocli/src/commands.rs44
2 files changed, 65 insertions, 1 deletions
diff --git a/nitrocli/src/args.rs b/nitrocli/src/args.rs
index 5c2cbb8..ab80f3d 100644
--- a/nitrocli/src/args.rs
+++ b/nitrocli/src/args.rs
@@ -86,6 +86,7 @@ enum OtpCommand {
Clear,
Get,
Set,
+ Status,
}
impl OtpCommand {
@@ -94,6 +95,7 @@ impl OtpCommand {
OtpCommand::Clear => otp_clear(args),
OtpCommand::Get => otp_get(args),
OtpCommand::Set => otp_set(args),
+ OtpCommand::Status => otp_status(args),
}
}
}
@@ -107,6 +109,7 @@ impl fmt::Display for OtpCommand {
OtpCommand::Clear => "clear",
OtpCommand::Get => "get",
OtpCommand::Set => "set",
+ OtpCommand::Status => "status",
}
)
}
@@ -120,6 +123,7 @@ impl str::FromStr for OtpCommand {
"clear" => Ok(OtpCommand::Clear),
"get" => Ok(OtpCommand::Get),
"set" => Ok(OtpCommand::Set),
+ "status" => Ok(OtpCommand::Status),
_ => Err(()),
}
}
@@ -249,7 +253,7 @@ fn otp(args: Vec<String>) -> Result<()> {
let _ = parser.refer(&mut subcommand).required().add_argument(
"subcommand",
argparse::Store,
- "The subcommand to execute (clear|get|set)",
+ "The subcommand to execute (clear|get|set|status)",
);
let _ = parser.refer(&mut subargs).add_argument(
"arguments",
@@ -374,6 +378,22 @@ fn otp_clear(args: Vec<String>) -> Result<()> {
commands::otp_clear(slot, algorithm)
}
+/// Print the status of the OTP slots.
+fn otp_status(args: Vec<String>) -> Result<()> {
+ let mut all = false;
+ let mut parser = argparse::ArgumentParser::new();
+ parser.set_description("Prints the status of the OTP slots");
+ let _ = parser.refer(&mut all).add_option(
+ &["-a", "--all"],
+ argparse::StoreTrue,
+ "Show slots that are not programmed",
+ );
+ parse(&parser, args)?;
+ drop(parser);
+
+ commands::otp_status(all)
+}
+
/// 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>)> {
diff --git a/nitrocli/src/commands.rs b/nitrocli/src/commands.rs
index 93e9bd3..3546e2e 100644
--- a/nitrocli/src/commands.rs
+++ b/nitrocli/src/commands.rs
@@ -333,6 +333,50 @@ pub fn otp_clear(slot: u8, algorithm: args::OtpAlgorithm) -> Result<()> {
Ok(())
}
+fn print_otp_status(
+ algorithm: args::OtpAlgorithm,
+ device: &nitrokey::DeviceWrapper,
+ all: bool,
+) -> Result<()> {
+ let mut slot: u8 = 0;
+ loop {
+ let result = match algorithm {
+ args::OtpAlgorithm::Hotp => device.get_hotp_slot_name(slot),
+ args::OtpAlgorithm::Totp => device.get_totp_slot_name(slot),
+ };
+ slot = match slot.checked_add(1) {
+ Some(slot) => slot,
+ None => {
+ return Err(Error::Error(
+ "Integer overflow when iterating OTP slots".to_string(),
+ ))
+ }
+ };
+ let name = match result {
+ Ok(name) => name,
+ Err(nitrokey::CommandError::InvalidSlot) => return Ok(()),
+ Err(nitrokey::CommandError::SlotNotProgrammed) => {
+ if all {
+ "[not programmed]".to_string()
+ } else {
+ continue;
+ }
+ }
+ Err(err) => return Err(get_error("Could not check OTP slot", &err)),
+ };
+ println!("{}\t{}\t{}", algorithm, slot - 1, name);
+ }
+}
+
+/// Print the status of the OTP slots.
+pub fn otp_status(all: bool) -> Result<()> {
+ let device = get_device()?;
+ println!("alg\tslot\tname");
+ print_otp_status(args::OtpAlgorithm::Hotp, &device, all)?;
+ print_otp_status(args::OtpAlgorithm::Totp, &device, all)?;
+ Ok(())
+}
+
#[cfg(test)]
mod tests {
use super::*;