aboutsummaryrefslogtreecommitdiff
path: root/src/device.rs
diff options
context:
space:
mode:
authorRobin Krahl <robin.krahl@ireas.org>2018-05-22 00:32:12 +0000
committerRobin Krahl <robin.krahl@ireas.org>2018-05-22 02:35:28 +0200
commita9657c48c8c92f6c82834a2abd90e27904e2cf6b (patch)
tree6991987c5fd21a169a233c30b17706330ae085c6 /src/device.rs
parent2a736067581dd44288b43a01bf47e0d28801e0a8 (diff)
downloadnitrokey-rs-a9657c48c8c92f6c82834a2abd90e27904e2cf6b.tar.gz
nitrokey-rs-a9657c48c8c92f6c82834a2abd90e27904e2cf6b.tar.bz2
Restructure code by functionality
In future versions, we want to support not only the Nitrokey Pro, but also the Nitrokey Storage. This requires a better code layout. This patch introduces two main changes: First, the OTP-specific methods are moved from the Device trait and the AdminAuthenticatedDevice struct to the functionality-based traits ConfigureOtp and GenerateOtp. This will hopefully make it easier to integrate the Nitrokey Storage. Secondly, the code is split into separate modules. These modules are currently all private and re-exported in the lib module, but we can consider making them public in the future.
Diffstat (limited to 'src/device.rs')
-rw-r--r--src/device.rs711
1 files changed, 711 insertions, 0 deletions
diff --git a/src/device.rs b/src/device.rs
new file mode 100644
index 0000000..6c1a957
--- /dev/null
+++ b/src/device.rs
@@ -0,0 +1,711 @@
+use config::{Config, RawConfig};
+use libc;
+use nitrokey_sys;
+use std::ffi::CString;
+use std::os::raw::c_int;
+use otp::{ConfigureOtp, GenerateOtp, OtpMode, OtpSlotData, RawOtpSlotData};
+use util::{generate_password, get_last_error, result_from_string, CommandError, CommandStatus};
+
+static TEMPORARY_PASSWORD_LENGTH: usize = 25;
+
+/// Available Nitrokey models.
+#[derive(Debug, PartialEq)]
+pub enum Model {
+ /// The Nitrokey Storage.
+ Storage,
+ /// The Nitrokey Pro.
+ Pro,
+}
+
+/// A Nitrokey device without user or admin authentication.
+///
+/// Use [`connect`][] or [`connect_model`][] to obtain an instance. If you
+/// want to execute a command that requires user or admin authentication,
+/// use [`authenticate_admin`][] or [`authenticate_user`][].
+///
+/// # Examples
+///
+/// Authentication with error handling:
+///
+/// ```no_run
+/// use nitrokey::{UnauthenticatedDevice, UserAuthenticatedDevice};
+/// # use nitrokey::CommandError;
+///
+/// fn perform_user_task(device: &UserAuthenticatedDevice) {}
+/// fn perform_other_task(device: &UnauthenticatedDevice) {}
+///
+/// # fn try_main() -> Result<(), CommandError> {
+/// let device = nitrokey::connect()?;
+/// let device = match device.authenticate_user("123456") {
+/// Ok(user) => {
+/// perform_user_task(&user);
+/// user.device()
+/// },
+/// Err((device, err)) => {
+/// println!("Could not authenticate as user: {:?}", err);
+/// device
+/// },
+/// };
+/// perform_other_task(&device);
+/// # Ok(())
+/// # }
+/// ```
+///
+/// [`authenticate_admin`]: #method.authenticate_admin
+/// [`authenticate_user`]: #method.authenticate_user
+/// [`connect`]: fn.connect.html
+/// [`connect_model`]: fn.connect_model.html
+#[derive(Debug)]
+pub struct UnauthenticatedDevice {}
+
+/// A Nitrokey device with user authentication.
+///
+/// To obtain an instance of this struct, use the [`authenticate_user`][]
+/// method on an [`UnauthenticatedDevice`][]. To get back to an
+/// unauthenticated device, use the [`device`][] method.
+///
+/// [`authenticate_admin`]: struct.UnauthenticatedDevice#method.authenticate_admin
+/// [`device`]: #method.device
+/// [`UnauthenticatedDevice`]: struct.UnauthenticatedDevice.html
+#[derive(Debug)]
+pub struct UserAuthenticatedDevice {
+ device: UnauthenticatedDevice,
+ temp_password: Vec<u8>,
+}
+
+/// A Nitrokey device with admin authentication.
+///
+/// To obtain an instance of this struct, use the [`authenticate_admin`][]
+/// method on an [`UnauthenticatedDevice`][]. To get back to an
+/// unauthenticated device, use the [`device`][] method.
+///
+/// [`authenticate_admin`]: struct.UnauthenticatedDevice#method.authenticate_admin
+/// [`device`]: #method.device
+/// [`UnauthenticatedDevice`]: struct.UnauthenticatedDevice.html
+#[derive(Debug)]
+pub struct AdminAuthenticatedDevice {
+ device: UnauthenticatedDevice,
+ temp_password: Vec<u8>,
+}
+
+/// A Nitrokey device.
+///
+/// This trait provides the commands that can be executed without
+/// authentication.
+pub trait Device {
+ /// Sets the time on the Nitrokey. This command may set the time to
+ /// arbitrary values. `time` is the number of seconds since January 1st,
+ /// 1970 (Unix timestamp).
+ ///
+ /// The time is used for TOTP generation (see [`get_totp_code`][]).
+ ///
+ /// # Errors
+ ///
+ /// - [`Timestamp`][] if the time could not be set
+ ///
+ /// [`get_totp_code`]: trait.ProvideOtp.html#method.get_totp_code
+ /// [`Timestamp`]: enum.CommandError.html#variant.Timestamp
+ // TODO: example
+ fn set_time(&self, time: u64) -> CommandStatus {
+ unsafe { CommandStatus::from(nitrokey_sys::NK_totp_set_time(time)) }
+ }
+
+ /// Returns the serial number of the Nitrokey device. The serial number
+ /// is the string representation of a hex number.
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::Device;
+ /// # use nitrokey::CommandError;
+ ///
+ /// # fn try_main() -> Result<(), CommandError> {
+ /// let device = nitrokey::connect()?;
+ /// match device.get_serial_number() {
+ /// Ok(number) => println!("serial no: {:?}", number),
+ /// Err(err) => println!("Could not get serial number: {:?}", err),
+ /// };
+ /// # Ok(())
+ /// # }
+ /// ```
+ fn get_serial_number(&self) -> Result<String, CommandError> {
+ unsafe { result_from_string(nitrokey_sys::NK_device_serial_number()) }
+ }
+
+ /// Returns the number of remaining authentication attempts for the user. The
+ /// total number of available attempts is three.
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::Device;
+ /// # use nitrokey::CommandError;
+ ///
+ /// # fn try_main() -> Result<(), CommandError> {
+ /// let device = nitrokey::connect()?;
+ /// let count = device.get_user_retry_count();
+ /// println!("{} remaining authentication attempts (user)", count);
+ /// # Ok(())
+ /// # }
+ /// ```
+ fn get_user_retry_count(&self) -> u8 {
+ unsafe { nitrokey_sys::NK_get_user_retry_count() }
+ }
+
+ /// Returns the number of remaining authentication attempts for the admin. The
+ /// total number of available attempts is three.
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::Device;
+ /// # use nitrokey::CommandError;
+ ///
+ /// # fn try_main() -> Result<(), CommandError> {
+ /// let device = nitrokey::connect()?;
+ /// let count = device.get_admin_retry_count();
+ /// println!("{} remaining authentication attempts (admin)", count);
+ /// # Ok(())
+ /// # }
+ /// ```
+ fn get_admin_retry_count(&self) -> u8 {
+ unsafe { nitrokey_sys::NK_get_admin_retry_count() }
+ }
+
+ /// Returns the major part of the firmware version (should be zero).
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::Device;
+ /// # use nitrokey::CommandError;
+ ///
+ /// # fn try_main() -> Result<(), CommandError> {
+ /// let device = nitrokey::connect()?;
+ /// println!(
+ /// "Firmware version: {}.{}",
+ /// device.get_major_firmware_version(),
+ /// device.get_minor_firmware_version(),
+ /// );
+ /// # Ok(())
+ /// # }
+ /// ```
+ fn get_major_firmware_version(&self) -> i32 {
+ unsafe { nitrokey_sys::NK_get_major_firmware_version() }
+ }
+
+ /// Returns the minor part of the firmware version (for example 8 for
+ /// version 0.8).
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::Device;
+ /// # use nitrokey::CommandError;
+ ///
+ /// # fn try_main() -> Result<(), CommandError> {
+ /// let device = nitrokey::connect()?;
+ /// println!(
+ /// "Firmware version: {}.{}",
+ /// device.get_major_firmware_version(),
+ /// device.get_minor_firmware_version(),
+ /// );
+ /// # Ok(())
+ /// # }
+ fn get_minor_firmware_version(&self) -> i32 {
+ unsafe { nitrokey_sys::NK_get_minor_firmware_version() }
+ }
+
+ /// Returns the current configuration of the Nitrokey device.
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::Device;
+ /// # use nitrokey::CommandError;
+ ///
+ /// # fn try_main() -> Result<(), CommandError> {
+ /// let device = nitrokey::connect()?;
+ /// let config = device.get_config()?;
+ /// println!("numlock binding: {:?}", config.numlock);
+ /// println!("capslock binding: {:?}", config.capslock);
+ /// println!("scrollock binding: {:?}", config.scrollock);
+ /// println!("require password for OTP: {:?}", config.user_password);
+ /// # Ok(())
+ /// # }
+ /// ```
+ fn get_config(&self) -> Result<Config, CommandError> {
+ unsafe {
+ let config_ptr = nitrokey_sys::NK_read_config();
+ if config_ptr.is_null() {
+ return Err(get_last_error());
+ }
+ let config_array_ptr = config_ptr as *const [u8; 5];
+ let raw_config = RawConfig::from(*config_array_ptr);
+ libc::free(config_ptr as *mut libc::c_void);
+ return Ok(raw_config.into());
+ }
+ }
+
+ /// Changes the administrator PIN.
+ ///
+ /// # Errors
+ ///
+ /// - [`InvalidString`][] if one of the provided passwords contains a null byte
+ /// - [`WrongPassword`][] if the current admin password is wrong
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::{CommandStatus, Device};
+ /// # use nitrokey::CommandError;
+ ///
+ /// # fn try_main() -> Result<(), CommandError> {
+ /// let device = nitrokey::connect()?;
+ /// match device.change_admin_pin("12345678", "12345679") {
+ /// CommandStatus::Success => println!("Updated admin PIN."),
+ /// CommandStatus::Error(err) => println!("Failed to update admin PIN: {:?}", err),
+ /// };
+ /// # Ok(())
+ /// # }
+ /// ```
+ ///
+ /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString
+ /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword
+ fn change_admin_pin(&self, current: &str, new: &str) -> CommandStatus {
+ let current_string = CString::new(current);
+ let new_string = CString::new(new);
+ if current_string.is_err() || new_string.is_err() {
+ return CommandStatus::Error(CommandError::InvalidString);
+ }
+ let current_string = current_string.unwrap();
+ let new_string = new_string.unwrap();
+ unsafe {
+ CommandStatus::from(nitrokey_sys::NK_change_admin_PIN(
+ current_string.as_ptr(),
+ new_string.as_ptr(),
+ ))
+ }
+ }
+
+ /// Changes the user PIN.
+ ///
+ /// # Errors
+ ///
+ /// - [`InvalidString`][] if one of the provided passwords contains a null byte
+ /// - [`WrongPassword`][] if the current user password is wrong
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::{CommandStatus, Device};
+ /// # use nitrokey::CommandError;
+ ///
+ /// # fn try_main() -> Result<(), CommandError> {
+ /// let device = nitrokey::connect()?;
+ /// match device.change_user_pin("123456", "123457") {
+ /// CommandStatus::Success => println!("Updated admin PIN."),
+ /// CommandStatus::Error(err) => println!("Failed to update admin PIN: {:?}", err),
+ /// };
+ /// # Ok(())
+ /// # }
+ /// ```
+ ///
+ /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString
+ /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword
+ fn change_user_pin(&self, current: &str, new: &str) -> CommandStatus {
+ let current_string = CString::new(current);
+ let new_string = CString::new(new);
+ if current_string.is_err() || new_string.is_err() {
+ return CommandStatus::Error(CommandError::InvalidString);
+ }
+ let current_string = current_string.unwrap();
+ let new_string = new_string.unwrap();
+ unsafe {
+ CommandStatus::from(nitrokey_sys::NK_change_user_PIN(
+ current_string.as_ptr(),
+ new_string.as_ptr(),
+ ))
+ }
+ }
+}
+
+trait AuthenticatedDevice {
+ fn new(device: UnauthenticatedDevice, temp_password: Vec<u8>) -> Self;
+}
+
+impl UnauthenticatedDevice {
+ fn authenticate<D, T>(
+ self,
+ password: &str,
+ callback: T,
+ ) -> Result<D, (UnauthenticatedDevice, CommandError)>
+ where
+ D: AuthenticatedDevice,
+ T: Fn(*const i8, *const i8) -> c_int,
+ {
+ let temp_password = match generate_password(TEMPORARY_PASSWORD_LENGTH) {
+ Ok(pw) => pw,
+ Err(_) => return Err((self, CommandError::RngError)),
+ };
+ let password = CString::new(password);
+ if password.is_err() {
+ return Err((self, CommandError::InvalidString));
+ }
+
+ let pw = password.unwrap();
+ let password_ptr = pw.as_ptr();
+ let temp_password_ptr = temp_password.as_ptr() as *const i8;
+ return match callback(password_ptr, temp_password_ptr) {
+ 0 => Ok(D::new(self, temp_password)),
+ rv => Err((self, CommandError::from(rv))),
+ };
+ }
+
+ /// Performs user authentication. This method consumes the device. If
+ /// successful, an authenticated device is returned. Otherwise, the
+ /// current unauthenticated device and the error are returned.
+ ///
+ /// This method generates a random temporary password that is used for all
+ /// operations that require user access.
+ ///
+ /// # Errors
+ ///
+ /// - [`InvalidString`][] if the provided user password contains a null byte
+ /// - [`RngError`][] if the generation of the temporary password failed
+ /// - [`WrongPassword`][] if the provided user password is wrong
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::{UnauthenticatedDevice, UserAuthenticatedDevice};
+ /// # use nitrokey::CommandError;
+ ///
+ /// fn perform_user_task(device: &UserAuthenticatedDevice) {}
+ /// fn perform_other_task(device: &UnauthenticatedDevice) {}
+ ///
+ /// # fn try_main() -> Result<(), CommandError> {
+ /// let device = nitrokey::connect()?;
+ /// let device = match device.authenticate_user("123456") {
+ /// Ok(user) => {
+ /// perform_user_task(&user);
+ /// user.device()
+ /// },
+ /// Err((device, err)) => {
+ /// println!("Could not authenticate as user: {:?}", err);
+ /// device
+ /// },
+ /// };
+ /// perform_other_task(&device);
+ /// # Ok(())
+ /// # }
+ /// ```
+ ///
+ /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString
+ /// [`RngError`]: enum.CommandError.html#variant.RngError
+ /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword
+ pub fn authenticate_user(
+ self,
+ password: &str,
+ ) -> Result<UserAuthenticatedDevice, (UnauthenticatedDevice, CommandError)> {
+ return self.authenticate(password, |password_ptr, temp_password_ptr| unsafe {
+ nitrokey_sys::NK_user_authenticate(password_ptr, temp_password_ptr)
+ });
+ }
+
+ /// Performs admin authentication. This method consumes the device. If
+ /// successful, an authenticated device is returned. Otherwise, the
+ /// current unauthenticated device and the error are returned.
+ ///
+ /// This method generates a random temporary password that is used for all
+ /// operations that require admin access.
+ ///
+ /// # Errors
+ ///
+ /// - [`InvalidString`][] if the provided admin password contains a null byte
+ /// - [`RngError`][] if the generation of the temporary password failed
+ /// - [`WrongPassword`][] if the provided admin password is wrong
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::{AdminAuthenticatedDevice, UnauthenticatedDevice};
+ /// # use nitrokey::CommandError;
+ ///
+ /// fn perform_admin_task(device: &AdminAuthenticatedDevice) {}
+ /// fn perform_other_task(device: &UnauthenticatedDevice) {}
+ ///
+ /// # fn try_main() -> Result<(), CommandError> {
+ /// let device = nitrokey::connect()?;
+ /// let device = match device.authenticate_admin("123456") {
+ /// Ok(admin) => {
+ /// perform_admin_task(&admin);
+ /// admin.device()
+ /// },
+ /// Err((device, err)) => {
+ /// println!("Could not authenticate as admin: {:?}", err);
+ /// device
+ /// },
+ /// };
+ /// perform_other_task(&device);
+ /// # Ok(())
+ /// # }
+ /// ```
+ ///
+ /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString
+ /// [`RngError`]: enum.CommandError.html#variant.RngError
+ /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword
+ pub fn authenticate_admin(
+ self,
+ password: &str,
+ ) -> Result<AdminAuthenticatedDevice, (UnauthenticatedDevice, CommandError)> {
+ return self.authenticate(password, |password_ptr, temp_password_ptr| unsafe {
+ nitrokey_sys::NK_first_authenticate(password_ptr, temp_password_ptr)
+ });
+ }
+}
+
+impl Drop for UnauthenticatedDevice {
+ fn drop(&mut self) {
+ unsafe {
+ nitrokey_sys::NK_logout();
+ }
+ }
+}
+
+impl Device for UnauthenticatedDevice {}
+
+impl GenerateOtp for UnauthenticatedDevice {}
+
+impl UserAuthenticatedDevice {
+ /// Forgets the user authentication and returns an unauthenticated
+ /// device. This method consumes the authenticated device. It does not
+ /// perform any actual commands on the Nitrokey.
+ pub fn device(self) -> UnauthenticatedDevice {
+ self.device
+ }
+}
+
+impl Device for UserAuthenticatedDevice {}
+
+impl GenerateOtp for UserAuthenticatedDevice {
+ /// Generates an HOTP code on the given slot. This operation may not
+ /// require user authorization, depending on the device configuration (see
+ /// [`get_config`][]).
+ ///
+ /// # Errors
+ ///
+ /// - [`SlotNotProgrammed`][] if the given slot is not configured
+ /// - [`WrongSlot`][] if there is no slot with the given number
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::{Device, GenerateOtp};
+ /// # use nitrokey::CommandError;
+ ///
+ /// # fn try_main() -> Result<(), CommandError> {
+ /// let device = nitrokey::connect()?;
+ /// match device.authenticate_user("123456") {
+ /// Ok(user) => {
+ /// let code = user.get_hotp_code(1)?;
+ /// println!("Generated HOTP code on slot 1: {:?}", code);
+ /// },
+ /// Err(err) => println!("Could not authenticate: {:?}", err),
+ /// };
+ /// # Ok(())
+ /// # }
+ /// ```
+ ///
+ /// [`get_config`]: #method.get_config
+ /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed
+ /// [`WrongSlot`]: enum.CommandError.html#variant.WrongSlot
+ fn get_hotp_code(&self, slot: u8) -> Result<String, CommandError> {
+ unsafe {
+ let temp_password_ptr = self.temp_password.as_ptr() as *const i8;
+ return result_from_string(nitrokey_sys::NK_get_hotp_code_PIN(slot, temp_password_ptr));
+ }
+ }
+
+ /// Generates a TOTP code on the given slot. This operation may not
+ /// require user authorization, depending on the device configuration (see
+ /// [`get_config`][]).
+ ///
+ /// To make sure that the Nitrokey’s time is in sync, consider calling
+ /// [`set_time`][] before calling this method.
+ ///
+ /// # Errors
+ ///
+ /// - [`SlotNotProgrammed`][] if the given slot is not configured
+ /// - [`WrongSlot`][] if there is no slot with the given number
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::{Device, GenerateOtp};
+ /// # use nitrokey::CommandError;
+ ///
+ /// # fn try_main() -> Result<(), CommandError> {
+ /// let device = nitrokey::connect()?;
+ /// match device.authenticate_user("123456") {
+ /// Ok(user) => {
+ /// let code = user.get_totp_code(1)?;
+ /// println!("Generated TOTP code on slot 1: {:?}", code);
+ /// },
+ /// Err(err) => println!("Could not authenticate: {:?}", err),
+ /// };
+ /// # Ok(())
+ /// # }
+ /// ```
+ ///
+ /// [`get_config`]: #method.get_config
+ /// [`set_time`]: trait.Device.html#method.set_time
+ /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed
+ /// [`WrongSlot`]: enum.CommandError.html#variant.WrongSlot
+ fn get_totp_code(&self, slot: u8) -> Result<String, CommandError> {
+ unsafe {
+ let temp_password_ptr = self.temp_password.as_ptr() as *const i8;
+ return result_from_string(nitrokey_sys::NK_get_totp_code_PIN(
+ slot,
+ 0,
+ 0,
+ 0,
+ temp_password_ptr,
+ ));
+ }
+ }
+}
+
+impl AuthenticatedDevice for UserAuthenticatedDevice {
+ fn new(device: UnauthenticatedDevice, temp_password: Vec<u8>) -> Self {
+ UserAuthenticatedDevice {
+ device,
+ temp_password,
+ }
+ }
+}
+
+impl AdminAuthenticatedDevice {
+ /// Forgets the user authentication and returns an unauthenticated
+ /// device. This method consumes the authenticated device. It does not
+ /// perform any actual commands on the Nitrokey.
+ pub fn device(self) -> UnauthenticatedDevice {
+ self.device
+ }
+
+ /// Writes the given configuration to the Nitrokey device.
+ ///
+ /// # Errors
+ ///
+ /// - [`InvalidSlot`][] if the provided numlock, capslock or scrolllock
+ /// slot is larger than two
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::Config;
+ /// # use nitrokey::CommandError;
+ ///
+ /// # fn try_main() -> Result<(), CommandError> {
+ /// let device = nitrokey::connect()?;
+ /// let config = Config::new(None, None, None, false);
+ /// match device.authenticate_admin("12345678") {
+ /// Ok(admin) => {
+ /// admin.write_config(config);
+ /// ()
+ /// },
+ /// Err((_, err)) => println!("Could not authenticate as admin: {:?}", err),
+ /// };
+ /// # Ok(())
+ /// # }
+ /// ```
+ ///
+ /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot
+ pub fn write_config(&self, config: Config) -> CommandStatus {
+ let raw_config = match RawConfig::try_from(config) {
+ Ok(raw_config) => raw_config,
+ Err(err) => return CommandStatus::Error(err),
+ };
+ unsafe {
+ let rv = nitrokey_sys::NK_write_config(
+ raw_config.numlock,
+ raw_config.capslock,
+ raw_config.scrollock,
+ raw_config.user_password,
+ false,
+ self.temp_password.as_ptr() as *const i8,
+ );
+ return CommandStatus::from(rv);
+ }
+ }
+
+ fn write_otp_slot<T>(&self, data: OtpSlotData, callback: T) -> CommandStatus
+ where
+ T: Fn(RawOtpSlotData, *const i8) -> c_int,
+ {
+ let raw_data = match RawOtpSlotData::new(data) {
+ Ok(raw_data) => raw_data,
+ Err(err) => return CommandStatus::Error(err),
+ };
+ let temp_password_ptr = self.temp_password.as_ptr() as *const i8;
+ let rv = callback(raw_data, temp_password_ptr);
+ return CommandStatus::from(rv);
+ }
+}
+
+impl Device for AdminAuthenticatedDevice {}
+
+impl ConfigureOtp for AdminAuthenticatedDevice {
+ fn write_hotp_slot(&self, data: OtpSlotData, counter: u64) -> CommandStatus {
+ return self.write_otp_slot(data, |raw_data: RawOtpSlotData, temp_password_ptr| unsafe {
+ nitrokey_sys::NK_write_hotp_slot(
+ raw_data.number,
+ raw_data.name.as_ptr(),
+ raw_data.secret.as_ptr(),
+ counter,
+ raw_data.mode == OtpMode::EightDigits,
+ raw_data.use_enter,
+ raw_data.use_token_id,
+ raw_data.token_id.as_ptr(),
+ temp_password_ptr,
+ )
+ });
+ }
+
+ fn write_totp_slot(&self, data: OtpSlotData, time_window: u16) -> CommandStatus {
+ return self.write_otp_slot(data, |raw_data: RawOtpSlotData, temp_password_ptr| unsafe {
+ nitrokey_sys::NK_write_totp_slot(
+ raw_data.number,
+ raw_data.name.as_ptr(),
+ raw_data.secret.as_ptr(),
+ time_window,
+ raw_data.mode == OtpMode::EightDigits,
+ raw_data.use_enter,
+ raw_data.use_token_id,
+ raw_data.token_id.as_ptr(),
+ temp_password_ptr,
+ )
+ });
+ }
+
+ fn erase_hotp_slot(&self, slot: u8) -> CommandStatus {
+ let temp_password_ptr = self.temp_password.as_ptr() as *const i8;
+ unsafe { CommandStatus::from(nitrokey_sys::NK_erase_hotp_slot(slot, temp_password_ptr)) }
+ }
+
+ fn erase_totp_slot(&self, slot: u8) -> CommandStatus {
+ let temp_password_ptr = self.temp_password.as_ptr() as *const i8;
+ unsafe { CommandStatus::from(nitrokey_sys::NK_erase_totp_slot(slot, temp_password_ptr)) }
+ }
+}
+
+impl GenerateOtp for AdminAuthenticatedDevice {}
+
+impl AuthenticatedDevice for AdminAuthenticatedDevice {
+ fn new(device: UnauthenticatedDevice, temp_password: Vec<u8>) -> Self {
+ AdminAuthenticatedDevice {
+ device,
+ temp_password,
+ }
+ }
+}