diff options
author | Robin Krahl <robin.krahl@ireas.org> | 2018-12-23 02:08:47 +0100 |
---|---|---|
committer | Daniel Mueller <deso@posteo.net> | 2018-12-24 18:15:28 -0800 |
commit | 1630c7872631f5f7e5bab599121df1fed26e47da (patch) | |
tree | d831a9029f970731534cfdef9c05bb9196fe096a /nitrocli/src/commands.rs | |
parent | 86b3170b1dd4c1955e540f5c914a317302754be1 (diff) | |
download | nitrocli-1630c7872631f5f7e5bab599121df1fed26e47da.tar.gz nitrocli-1630c7872631f5f7e5bab599121df1fed26e47da.tar.bz2 |
Implement the otp set subcommand
This patch implements the `otp set` subcommand that configures an OTP
slot. There are two ways to specify an OTP secret: as a hexadecimal
string (that means that every two characters are interpreted as a
hexadecimal representation of one byte of the secret) or as an ASCII
string (that means that the ASCII code of every character is interpreted
as one byte of the secret). As the HOTP RFC mentions both
representations, this implementation supports both.
Diffstat (limited to 'nitrocli/src/commands.rs')
-rw-r--r-- | nitrocli/src/commands.rs | 67 |
1 files changed, 66 insertions, 1 deletions
diff --git a/nitrocli/src/commands.rs b/nitrocli/src/commands.rs index a82734e..8dc8c42 100644 --- a/nitrocli/src/commands.rs +++ b/nitrocli/src/commands.rs @@ -19,6 +19,7 @@ use std::result; +use nitrokey::ConfigureOtp; use nitrokey::Device; use nitrokey::GenerateOtp; @@ -73,7 +74,6 @@ where } /// Authenticate the given device with the admin PIN. -#[allow(unused)] fn authenticate_admin<T>(device: T) -> Result<nitrokey::Admin<T>> where T: Device, @@ -276,3 +276,68 @@ pub fn otp_get(slot: u8, algorithm: args::OtpAlgorithm) -> Result<()> { println!("{}", otp); Ok(()) } + +/// Prepare an ASCII secret string for libnitrokey. +/// +/// libnitrokey expects secrets as hexadecimal strings. This function transforms an ASCII string +/// into a hexadecimal string or returns an error if the given string contains non-ASCII +/// characters. +fn prepare_secret(secret: &str) -> Result<String> { + if secret.is_ascii() { + Ok( + secret + .as_bytes() + .iter() + .map(|c| format!("{:x}", c)) + .collect::<Vec<String>>() + .join(""), + ) + } else { + Err(Error::Error( + "The given secret is not an ASCII string despite --ascii being set".to_string(), + )) + } +} + +/// Configure a one-time password slot on the Nitrokey device. +pub fn otp_set( + data: nitrokey::OtpSlotData, + algorithm: args::OtpAlgorithm, + counter: u64, + time_window: u16, + ascii: bool, +) -> Result<()> { + let secret = if ascii { + prepare_secret(&data.secret)? + } else { + data.secret + }; + let data = nitrokey::OtpSlotData { secret, ..data }; + let device = authenticate_admin(get_device()?)?; + match algorithm { + args::OtpAlgorithm::Hotp => device.write_hotp_slot(data, counter), + args::OtpAlgorithm::Totp => device.write_totp_slot(data, time_window), + } + .map_err(|err| get_error("Could not write OTP slot", &err))?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn prepare_secret_ascii() { + let result = prepare_secret("12345678901234567890"); + assert_eq!( + "3132333435363738393031323334353637383930".to_string(), + result.unwrap() + ); + } + + #[test] + fn prepare_secret_non_ascii() { + let result = prepare_secret("Österreich"); + assert!(result.is_err()); + } +} |