diff options
| -rw-r--r-- | nitrocli/src/args.rs | 57 | ||||
| -rw-r--r-- | nitrocli/src/commands.rs | 32 | 
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(()) +} | 
