aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Krahl <robin.krahl@ireas.org>2018-12-23 02:05:32 +0100
committerDaniel Mueller <deso@posteo.net>2018-12-24 18:15:27 -0800
commit86b3170b1dd4c1955e540f5c914a317302754be1 (patch)
tree6dc7b2c760af1e9f84db164de2734306b3a0bb57
parentb3a0db4508319b10a34a3b58c73369f2fdcd2e5d (diff)
downloadnitrocli-86b3170b1dd4c1955e540f5c914a317302754be1.tar.gz
nitrocli-86b3170b1dd4c1955e540f5c914a317302754be1.tar.bz2
Implement the otp get subcommand
This patch implements the `otp get` subcommand that allows the user to generate a one-time password on the Nitrokey device. Before generating the password, the device configuration is checked so that the user only has to enter a PIN if it is required for the OTP generation.
-rw-r--r--nitrocli/src/args.rs57
-rw-r--r--nitrocli/src/commands.rs32
2 files changed, 86 insertions, 3 deletions
diff --git a/nitrocli/src/args.rs b/nitrocli/src/args.rs
index 2112243..f4035e6 100644
--- a/nitrocli/src/args.rs
+++ b/nitrocli/src/args.rs
@@ -87,9 +87,9 @@ enum OtpCommand {
}
impl OtpCommand {
- fn execute(&self, _args: Vec<String>) -> Result<()> {
+ fn execute(&self, args: Vec<String>) -> Result<()> {
match *self {
- OtpCommand::Get => Err(Error::Error("Not implementend".to_string())),
+ OtpCommand::Get => otp_get(args),
}
}
}
@@ -117,6 +117,37 @@ impl str::FromStr for OtpCommand {
}
}
+#[derive(Clone, Copy, Debug)]
+pub enum OtpAlgorithm {
+ Hotp,
+ Totp,
+}
+
+impl fmt::Display for OtpAlgorithm {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(
+ f,
+ "{}",
+ match *self {
+ OtpAlgorithm::Hotp => "hotp",
+ OtpAlgorithm::Totp => "totp",
+ }
+ )
+ }
+}
+
+impl str::FromStr for OtpAlgorithm {
+ type Err = ();
+
+ fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
+ match s {
+ "hotp" => Ok(OtpAlgorithm::Hotp),
+ "totp" => Ok(OtpAlgorithm::Totp),
+ _ => 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))
@@ -185,6 +216,28 @@ fn otp(args: Vec<String>) -> Result<()> {
subcommand.execute(subargs)
}
+/// Generate a one-time password on the Nitrokey device.
+fn otp_get(args: Vec<String>) -> Result<()> {
+ let mut slot: u8 = 0;
+ let mut algorithm = OtpAlgorithm::Totp;
+ let mut parser = argparse::ArgumentParser::new();
+ parser.set_description("Generates a one-time password");
+ let _ =
+ parser
+ .refer(&mut slot)
+ .required()
+ .add_argument("slot", argparse::Store, "The OTP slot to use");
+ let _ = parser.refer(&mut algorithm).add_option(
+ &["-a", "--algorithm"],
+ argparse::Store,
+ "The OTP algorithm to use (hotp|totp)",
+ );
+ parse(&parser, args)?;
+ drop(parser);
+
+ commands::otp_get(slot, algorithm)
+}
+
/// 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 9761728..a82734e 100644
--- a/nitrocli/src/commands.rs
+++ b/nitrocli/src/commands.rs
@@ -20,7 +20,9 @@
use std::result;
use nitrokey::Device;
+use nitrokey::GenerateOtp;
+use crate::args;
use crate::error::Error;
use crate::pinentry;
use crate::Result;
@@ -30,6 +32,11 @@ fn get_error(msg: &str, err: &nitrokey::CommandError) -> Error {
Error::Error(format!("{}: {:?}", msg, err))
}
+/// Connect to any Nitrokey device and return it.
+fn get_device() -> Result<nitrokey::DeviceWrapper> {
+ nitrokey::connect().map_err(|_| Error::Error("Nitrokey device not found".to_string()))
+}
+
/// Connect to a Nitrokey Storage device and return it.
fn get_storage_device() -> Result<nitrokey::Storage> {
nitrokey::Storage::connect()
@@ -53,7 +60,6 @@ where
}
/// Authenticate the given device with the user PIN.
-#[allow(unused)]
fn authenticate_user<T>(device: T) -> Result<nitrokey::User<T>>
where
T: Device,
@@ -246,3 +252,27 @@ pub fn clear() -> Result<()> {
pinentry::clear_passphrase(pinentry::PinType::User)?;
Ok(())
}
+
+fn get_otp<T: GenerateOtp>(slot: u8, algorithm: args::OtpAlgorithm, device: &T) -> Result<String> {
+ match algorithm {
+ args::OtpAlgorithm::Hotp => device.get_hotp_code(slot),
+ args::OtpAlgorithm::Totp => device.get_totp_code(slot),
+ }
+ .map_err(|err| get_error("Could not generate OTP", &err))
+}
+
+/// Generate a one-time password on the Nitrokey device.
+pub fn otp_get(slot: u8, algorithm: args::OtpAlgorithm) -> Result<()> {
+ let device = get_device()?;
+ let config = device
+ .get_config()
+ .map_err(|err| get_error("Could not get device configuration", &err))?;
+ let otp = if config.user_password {
+ let user = authenticate_user(device)?;
+ get_otp(slot, algorithm, &user)
+ } else {
+ get_otp(slot, algorithm, &device)
+ }?;
+ println!("{}", otp);
+ Ok(())
+}