diff options
-rw-r--r-- | nitrocli/CHANGELOG.md | 2 | ||||
-rw-r--r-- | nitrocli/Cargo.lock | 4 | ||||
-rw-r--r-- | nitrocli/Cargo.toml | 2 | ||||
-rw-r--r-- | nitrocli/src/commands.rs | 54 | ||||
-rw-r--r-- | nitrocli/src/tests/pin.rs | 4 | ||||
-rw-r--r-- | nitrocli/src/tests/reset.rs | 2 | ||||
-rw-r--r-- | nitrokey/.builds/archlinux-use-system-lib.yml (renamed from nitrokey/.builds/archlinux-use-system-lib.yaml) | 0 | ||||
-rw-r--r-- | nitrokey/CHANGELOG.md | 7 | ||||
-rw-r--r-- | nitrokey/Cargo.toml | 7 | ||||
-rw-r--r-- | nitrokey/README.md | 3 | ||||
-rw-r--r-- | nitrokey/src/auth.rs | 38 | ||||
-rw-r--r-- | nitrokey/src/device.rs | 242 | ||||
-rw-r--r-- | nitrokey/src/lib.rs | 10 | ||||
-rw-r--r-- | nitrokey/src/otp.rs | 54 | ||||
-rw-r--r-- | nitrokey/src/pws.rs | 39 | ||||
-rw-r--r-- | nitrokey/src/util.rs | 4 | ||||
-rw-r--r-- | nitrokey/tests/device.rs | 266 | ||||
-rw-r--r-- | nitrokey/tests/lib.rs | 4 | ||||
-rw-r--r-- | nitrokey/tests/otp.rs | 116 | ||||
-rw-r--r-- | nitrokey/tests/pws.rs | 54 | ||||
-rw-r--r-- | nitrokey/tests/util/mod.rs | 31 |
21 files changed, 560 insertions, 383 deletions
diff --git a/nitrocli/CHANGELOG.md b/nitrocli/CHANGELOG.md index b7cfa53..7ae33f1 100644 --- a/nitrocli/CHANGELOG.md +++ b/nitrocli/CHANGELOG.md @@ -7,7 +7,7 @@ Unreleased - Removed `storage status` subcommand - Moved its output into `status` command - Removed previously deprecated `--ascii` option from `otp set` command -- Bumped `nitrokey` dependency to `0.4.0-alpha.0` +- Bumped `nitrokey` dependency to `0.4.0-alpha.2` 0.2.4 diff --git a/nitrocli/Cargo.lock b/nitrocli/Cargo.lock index 4618ce1..1b34e54 100644 --- a/nitrocli/Cargo.lock +++ b/nitrocli/Cargo.lock @@ -77,7 +77,7 @@ dependencies = [ "argparse 0.2.2", "base32 0.4.0", "libc 0.2.57", - "nitrokey 0.4.0-alpha.0", + "nitrokey 0.4.0-alpha.2", "nitrokey-test 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "nitrokey-test-state 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -85,7 +85,7 @@ dependencies = [ [[package]] name = "nitrokey" -version = "0.4.0-alpha.0" +version = "0.4.0-alpha.2" dependencies = [ "libc 0.2.57", "nitrokey-sys 3.4.3", diff --git a/nitrocli/Cargo.toml b/nitrocli/Cargo.toml index 618f498..c648c81 100644 --- a/nitrocli/Cargo.toml +++ b/nitrocli/Cargo.toml @@ -53,7 +53,7 @@ path = "../base32" version = "0.2" [dependencies.nitrokey] -version = "0.4.0-alpha.0" +version = "0.4.0-alpha.2" [dev-dependencies.nitrokey-test] version = "0.2.1" diff --git a/nitrocli/src/commands.rs b/nitrocli/src/commands.rs index 37698f7..5d1a2ab 100644 --- a/nitrocli/src/commands.rs +++ b/nitrocli/src/commands.rs @@ -101,7 +101,7 @@ fn with_password_safe<F>(ctx: &mut args::ExecCtx<'_>, mut op: F) -> Result<()> where F: FnMut(&mut args::ExecCtx<'_>, nitrokey::PasswordSafe<'_>) -> Result<()>, { - with_device(ctx, |ctx, device| { + with_device(ctx, |ctx, mut device| { let pin_entry = pinentry::PinEntry::from(pinentry::PinType::User, &device)?; try_with_pin_and_data( ctx, @@ -333,15 +333,14 @@ fn print_status( r#"Status: model: {model} serial number: 0x{id} - firmware version: {fwv0}.{fwv1} + firmware version: {fwv} user retry count: {urc} admin retry count: {arc}"#, model = model, id = serial_number, - fwv0 = device.get_major_firmware_version(), - fwv1 = device.get_minor_firmware_version(), - urc = device.get_user_retry_count(), - arc = device.get_admin_retry_count(), + fwv = device.get_firmware_version()?, + urc = device.get_user_retry_count()?, + arc = device.get_admin_retry_count()?, )?; if let nitrokey::DeviceWrapper::Storage(device) = device { @@ -368,7 +367,7 @@ pub fn status(ctx: &mut args::ExecCtx<'_>) -> Result<()> { /// Perform a factory reset. pub fn reset(ctx: &mut args::ExecCtx<'_>) -> Result<()> { - with_device(ctx, |ctx, device| { + with_device(ctx, |ctx, mut device| { let pin_entry = pinentry::PinEntry::from(pinentry::PinType::Admin, &device)?; // To force the user to enter the admin PIN before performing a @@ -395,7 +394,7 @@ pub fn unencrypted_set( ctx: &mut args::ExecCtx<'_>, mode: args::UnencryptedVolumeMode, ) -> Result<()> { - with_storage_device(ctx, |ctx, device| { + with_storage_device(ctx, |ctx, mut device| { let pin_entry = pinentry::PinEntry::from(pinentry::PinType::Admin, &device)?; let mode = match mode { args::UnencryptedVolumeMode::ReadWrite => nitrokey::VolumeMode::ReadWrite, @@ -417,7 +416,7 @@ pub fn unencrypted_set( /// Open the encrypted volume on the Nitrokey. pub fn encrypted_open(ctx: &mut args::ExecCtx<'_>) -> Result<()> { - with_storage_device(ctx, |ctx, device| { + with_storage_device(ctx, |ctx, mut device| { let pin_entry = pinentry::PinEntry::from(pinentry::PinType::User, &device)?; // We may forcefully close a hidden volume, if active, so be sure to @@ -432,7 +431,7 @@ pub fn encrypted_open(ctx: &mut args::ExecCtx<'_>) -> Result<()> { /// Close the previously opened encrypted volume. pub fn encrypted_close(ctx: &mut args::ExecCtx<'_>) -> Result<()> { - with_storage_device(ctx, |_ctx, device| { + with_storage_device(ctx, |_ctx, mut device| { // Flush all filesystem caches to disk. We are mostly interested in // making sure that the encrypted volume on the Nitrokey we are // about to close is not closed while not all data was written to @@ -447,7 +446,7 @@ pub fn encrypted_close(ctx: &mut args::ExecCtx<'_>) -> Result<()> { /// Create a hidden volume. pub fn hidden_create(ctx: &mut args::ExecCtx<'_>, slot: u8, start: u8, end: u8) -> Result<()> { - with_storage_device(ctx, |ctx, device| { + with_storage_device(ctx, |ctx, mut device| { let pwd_entry = pinentry::PwdEntry::from(&device)?; let pwd = if let Some(pwd) = &ctx.password { pwd @@ -466,7 +465,7 @@ pub fn hidden_create(ctx: &mut args::ExecCtx<'_>, slot: u8, start: u8, end: u8) /// Open a hidden volume. pub fn hidden_open(ctx: &mut args::ExecCtx<'_>) -> Result<()> { - with_storage_device(ctx, |ctx, device| { + with_storage_device(ctx, |ctx, mut device| { let pwd_entry = pinentry::PwdEntry::from(&device)?; let pwd = if let Some(pwd) = &ctx.password { pwd @@ -489,7 +488,7 @@ pub fn hidden_open(ctx: &mut args::ExecCtx<'_>) -> Result<()> { /// Close a previously opened hidden volume. pub fn hidden_close(ctx: &mut args::ExecCtx<'_>) -> Result<()> { - with_storage_device(ctx, |_ctx, device| { + with_storage_device(ctx, |_ctx, mut device| { unsafe { sync() }; device @@ -537,7 +536,7 @@ pub fn config_set( user_password: Option<bool>, ) -> Result<()> { with_device(ctx, |ctx, device| { - let device = authenticate_admin(ctx, device)?; + let mut device = authenticate_admin(ctx, device)?; let config = device .get_config() .map_err(|err| get_error("Could not get configuration", err))?; @@ -555,14 +554,17 @@ pub fn config_set( /// Lock the Nitrokey device. pub fn lock(ctx: &mut args::ExecCtx<'_>) -> Result<()> { - with_device(ctx, |_ctx, device| { + with_device(ctx, |_ctx, mut device| { device .lock() .map_err(|err| get_error("Could not lock the device", err)) }) } -fn get_otp<T: GenerateOtp>(slot: u8, algorithm: args::OtpAlgorithm, device: &T) -> Result<String> { +fn get_otp<T>(slot: u8, algorithm: args::OtpAlgorithm, device: &mut T) -> Result<String> +where + T: GenerateOtp, +{ match algorithm { args::OtpAlgorithm::Hotp => device.get_hotp_code(slot), args::OtpAlgorithm::Totp => device.get_totp_code(slot), @@ -584,7 +586,7 @@ pub fn otp_get( algorithm: args::OtpAlgorithm, time: Option<u64>, ) -> Result<()> { - with_device(ctx, |ctx, device| { + with_device(ctx, |ctx, mut device| { if algorithm == args::OtpAlgorithm::Totp { device .set_time( @@ -600,10 +602,10 @@ pub fn otp_get( .get_config() .map_err(|err| get_error("Could not get device configuration", err))?; let otp = if config.user_password { - let user = authenticate_user(ctx, device)?; - get_otp(slot, algorithm, &user) + let mut user = authenticate_user(ctx, device)?; + get_otp(slot, algorithm, &mut user) } else { - get_otp(slot, algorithm, &device) + get_otp(slot, algorithm, &mut device) }?; println!(ctx, "{}", otp)?; Ok(()) @@ -657,7 +659,7 @@ pub fn otp_set( args::OtpSecretFormat::Hex => data.secret, }; let data = nitrokey::OtpSlotData { secret, ..data }; - let device = authenticate_admin(ctx, device)?; + let mut device = authenticate_admin(ctx, device)?; match algorithm { args::OtpAlgorithm::Hotp => device.write_hotp_slot(data, counter), args::OtpAlgorithm::Totp => device.write_totp_slot(data, time_window), @@ -674,7 +676,7 @@ pub fn otp_clear( algorithm: args::OtpAlgorithm, ) -> Result<()> { with_device(ctx, |ctx, device| { - let device = authenticate_admin(ctx, device)?; + let mut device = authenticate_admin(ctx, device)?; match algorithm { args::OtpAlgorithm::Hotp => device.erase_hotp_slot(slot), args::OtpAlgorithm::Totp => device.erase_totp_slot(slot), @@ -778,7 +780,7 @@ fn choose_pin( /// Change a PIN. pub fn pin_set(ctx: &mut args::ExecCtx<'_>, pin_type: pinentry::PinType) -> Result<()> { - with_device(ctx, |ctx, device| { + with_device(ctx, |ctx, mut device| { let pin_entry = pinentry::PinEntry::from(pin_type, &device)?; let new_pin = choose_pin(ctx, &pin_entry, true)?; @@ -801,7 +803,7 @@ pub fn pin_set(ctx: &mut args::ExecCtx<'_>, pin_type: pinentry::PinType) -> Resu /// Unblock and reset the user PIN. pub fn pin_unblock(ctx: &mut args::ExecCtx<'_>) -> Result<()> { - with_device(ctx, |ctx, device| { + with_device(ctx, |ctx, mut device| { let pin_entry = pinentry::PinEntry::from(pinentry::PinType::User, &device)?; let user_pin = choose_pin(ctx, &pin_entry, false)?; let pin_entry = pinentry::PinEntry::from(pinentry::PinType::Admin, &device)?; @@ -881,7 +883,7 @@ pub fn pws_set( login: &str, password: &str, ) -> Result<()> { - with_password_safe(ctx, |_ctx, pws| { + with_password_safe(ctx, |_ctx, mut pws| { pws .write_slot(slot, name, login, password) .map_err(|err| get_error("Could not write PWS slot", err)) @@ -890,7 +892,7 @@ pub fn pws_set( /// Clear a PWS slot. pub fn pws_clear(ctx: &mut args::ExecCtx<'_>, slot: u8) -> Result<()> { - with_password_safe(ctx, |_ctx, pws| { + with_password_safe(ctx, |_ctx, mut pws| { pws .erase_slot(slot) .map_err(|err| get_error("Could not clear PWS slot", err)) diff --git a/nitrocli/src/tests/pin.rs b/nitrocli/src/tests/pin.rs index 051129c..e4cd316 100644 --- a/nitrocli/src/tests/pin.rs +++ b/nitrocli/src/tests/pin.rs @@ -29,12 +29,12 @@ fn unblock(device: nitrokey::DeviceWrapper) -> crate::Result<()> { nitrokey::Error::CommandError(err) if err == nitrokey::CommandError::WrongPassword => (), _ => panic!("Unexpected error variant found: {:?}", err), } - assert!(device.get_user_retry_count() < 3); + assert!(device.get_user_retry_count()? < 3); let model = device.get_model(); let _ = Nitrocli::with_dev(device).handle(&["pin", "unblock"])?; let device = nitrokey::connect_model(model)?; - assert_eq!(device.get_user_retry_count(), 3); + assert_eq!(device.get_user_retry_count()?, 3); Ok(()) } diff --git a/nitrocli/src/tests/reset.rs b/nitrocli/src/tests/reset.rs index 12d7023..f9452ec 100644 --- a/nitrocli/src/tests/reset.rs +++ b/nitrocli/src/tests/reset.rs @@ -42,7 +42,7 @@ fn reset(device: nitrokey::DeviceWrapper) -> crate::Result<()> { // Check that the admin PIN has been reset. let device = nitrokey::connect_model(ncli.model().unwrap())?; - let device = device + let mut device = device .authenticate_admin(nitrokey::DEFAULT_ADMIN_PIN) .unwrap(); diff --git a/nitrokey/.builds/archlinux-use-system-lib.yaml b/nitrokey/.builds/archlinux-use-system-lib.yml index ac0fc0f..ac0fc0f 100644 --- a/nitrokey/.builds/archlinux-use-system-lib.yaml +++ b/nitrokey/.builds/archlinux-use-system-lib.yml diff --git a/nitrokey/CHANGELOG.md b/nitrokey/CHANGELOG.md index 24c79af..e98e857 100644 --- a/nitrokey/CHANGELOG.md +++ b/nitrokey/CHANGELOG.md @@ -31,6 +31,13 @@ SPDX-License-Identifier: MIT - Return `Error::Utf8Error` if libnitrokey returns an invalid UTF-8 string. - Implement `From<(T: Device, Error)>` for `Error`. - Fix timing issues with the `totp_no_pin` and `totp_pin` test cases. +- Always return a `Result` in functions that communicate with a device. +- Combine `get_{major,minor}_firmware_version` into `get_firmware_version`. +- Add `set_encrypted_volume_mode` to `Storage`. +- Use mutability to represent changes to the device status: + - Implement `DerefMut` for `User<T>` and `Admin<T>`. + - Add `device_mut` method to `DeviceWrapper`. + - Require a mutable `Device` reference if a method changes the device state. # v0.3.4 (2019-01-20) - Fix authentication methods that assumed that `char` is signed. diff --git a/nitrokey/Cargo.toml b/nitrokey/Cargo.toml index 084bcea..fd6fef7 100644 --- a/nitrokey/Cargo.toml +++ b/nitrokey/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "nitrokey" -version = "0.4.0-alpha.0" +version = "0.4.0-alpha.2" authors = ["Robin Krahl <robin.krahl@ireas.org>"] edition = "2018" homepage = "https://code.ireas.org/nitrokey-rs/" @@ -14,12 +14,13 @@ keywords = ["nitrokey", "otp"] categories = ["api-bindings"] readme = "README.md" license = "MIT" +exclude = [".builds/*"] [dependencies] libc = "0.2" -nitrokey-sys = "3.4" +nitrokey-sys = "~3.4" rand_core = {version = "0.3", default-features = false, features = ["std"] } rand_os = {version = "0.1"} [dev-dependencies] -nitrokey-test = {version = "0.2"} +nitrokey-test = {version = "=0.2.0"} diff --git a/nitrokey/README.md b/nitrokey/README.md index 069fed1..8c596eb 100644 --- a/nitrokey/README.md +++ b/nitrokey/README.md @@ -38,8 +38,7 @@ supported by `nitrokey-rs`: - `NK_is_AES_supported`. This method is no longer needed for Nitrokey devices with a recent firmware version. - `NK_set_unencrypted_volume_rorw_pin_type_user`, - `NK_set_unencrypted_read_only`, `NK_set_unencrypted_read_write`, - `NK_set_encrypted_read_only` and `NK_set_encrypted_read_write`. These + `NK_set_unencrypted_read_only`, `NK_set_unencrypted_read_write`. These methods are only relevant for older firmware versions (pre-v0.51). As the Nitrokey Storage firmware can be updated easily, we do not support these outdated versions. diff --git a/nitrokey/src/auth.rs b/nitrokey/src/auth.rs index 18b6572..f9f50fa 100644 --- a/nitrokey/src/auth.rs +++ b/nitrokey/src/auth.rs @@ -1,7 +1,7 @@ // Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org> // SPDX-License-Identifier: MIT -use std::ops::Deref; +use std::ops; use std::os::raw::c_char; use std::os::raw::c_int; @@ -49,7 +49,7 @@ pub trait Authenticate { /// user.device() /// }, /// Err((device, err)) => { - /// println!("Could not authenticate as user: {}", err); + /// eprintln!("Could not authenticate as user: {}", err); /// device /// }, /// }; @@ -95,7 +95,7 @@ pub trait Authenticate { /// admin.device() /// }, /// Err((device, err)) => { - /// println!("Could not authenticate as admin: {}", err); + /// eprintln!("Could not authenticate as admin: {}", err); /// device /// }, /// }; @@ -211,7 +211,7 @@ impl<T: Device> User<T> { } } -impl<T: Device> Deref for User<T> { +impl<T: Device> ops::Deref for User<T> { type Target = T; fn deref(&self) -> &Self::Target { @@ -219,8 +219,14 @@ impl<T: Device> Deref for User<T> { } } +impl<T: Device> ops::DerefMut for User<T> { + fn deref_mut(&mut self) -> &mut T { + &mut self.device + } +} + impl<T: Device> GenerateOtp for User<T> { - fn get_hotp_code(&self, slot: u8) -> Result<String, Error> { + fn get_hotp_code(&mut self, slot: u8) -> Result<String, Error> { result_from_string(unsafe { nitrokey_sys::NK_get_hotp_code_PIN(slot, self.temp_password_ptr()) }) @@ -246,7 +252,7 @@ impl<T: Device> AuthenticatedDevice<T> for User<T> { } } -impl<T: Device> Deref for Admin<T> { +impl<T: Device> ops::Deref for Admin<T> { type Target = T; fn deref(&self) -> &Self::Target { @@ -254,6 +260,12 @@ impl<T: Device> Deref for Admin<T> { } } +impl<T: Device> ops::DerefMut for Admin<T> { + fn deref_mut(&mut self) -> &mut T { + &mut self.device + } +} + impl<T: Device> Admin<T> { /// Forgets the user authentication and returns an unauthenticated device. This method /// consumes the authenticated device. It does not perform any actual commands on the @@ -278,18 +290,18 @@ impl<T: Device> Admin<T> { /// let device = nitrokey::connect()?; /// let config = Config::new(None, None, None, false); /// match device.authenticate_admin("12345678") { - /// Ok(admin) => { + /// Ok(mut admin) => { /// admin.write_config(config); /// () /// }, - /// Err((_, err)) => println!("Could not authenticate as admin: {}", err), + /// Err((_, err)) => eprintln!("Could not authenticate as admin: {}", err), /// }; /// # Ok(()) /// # } /// ``` /// /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot - pub fn write_config(&self, config: Config) -> Result<(), Error> { + pub fn write_config(&mut self, config: Config) -> Result<(), Error> { let raw_config = RawConfig::try_from(config)?; get_command_result(unsafe { nitrokey_sys::NK_write_config( @@ -305,7 +317,7 @@ impl<T: Device> Admin<T> { } impl<T: Device> ConfigureOtp for Admin<T> { - fn write_hotp_slot(&self, data: OtpSlotData, counter: u64) -> Result<(), Error> { + fn write_hotp_slot(&mut self, data: OtpSlotData, counter: u64) -> Result<(), Error> { let raw_data = RawOtpSlotData::new(data)?; get_command_result(unsafe { nitrokey_sys::NK_write_hotp_slot( @@ -322,7 +334,7 @@ impl<T: Device> ConfigureOtp for Admin<T> { }) } - fn write_totp_slot(&self, data: OtpSlotData, time_window: u16) -> Result<(), Error> { + fn write_totp_slot(&mut self, data: OtpSlotData, time_window: u16) -> Result<(), Error> { let raw_data = RawOtpSlotData::new(data)?; get_command_result(unsafe { nitrokey_sys::NK_write_totp_slot( @@ -339,13 +351,13 @@ impl<T: Device> ConfigureOtp for Admin<T> { }) } - fn erase_hotp_slot(&self, slot: u8) -> Result<(), Error> { + fn erase_hotp_slot(&mut self, slot: u8) -> Result<(), Error> { get_command_result(unsafe { nitrokey_sys::NK_erase_hotp_slot(slot, self.temp_password_ptr()) }) } - fn erase_totp_slot(&self, slot: u8) -> Result<(), Error> { + fn erase_totp_slot(&mut self, slot: u8) -> Result<(), Error> { get_command_result(unsafe { nitrokey_sys::NK_erase_totp_slot(slot, self.temp_password_ptr()) }) diff --git a/nitrokey/src/device.rs b/nitrokey/src/device.rs index 386ce94..f6492cd 100644 --- a/nitrokey/src/device.rs +++ b/nitrokey/src/device.rs @@ -12,7 +12,9 @@ use crate::config::{Config, RawConfig}; use crate::error::{CommunicationError, Error}; use crate::otp::GenerateOtp; use crate::pws::GetPasswordSafe; -use crate::util::{get_command_result, get_cstring, get_last_error, result_from_string}; +use crate::util::{ + get_command_result, get_cstring, get_last_error, result_from_string, result_or_error, +}; /// Available Nitrokey models. #[derive(Clone, Copy, Debug, PartialEq)] @@ -76,7 +78,7 @@ impl fmt::Display for VolumeMode { /// user.device() /// }, /// Err((device, err)) => { -/// println!("Could not authenticate as user: {}", err); +/// eprintln!("Could not authenticate as user: {}", err); /// device /// }, /// }; @@ -140,7 +142,7 @@ pub enum DeviceWrapper { /// user.device() /// }, /// Err((device, err)) => { -/// println!("Could not authenticate as user: {}", err); +/// eprintln!("Could not authenticate as user: {}", err); /// device /// }, /// }; @@ -186,7 +188,7 @@ pub struct Pro { /// user.device() /// }, /// Err((device, err)) => { -/// println!("Could not authenticate as user: {}", err); +/// eprintln!("Could not authenticate as user: {}", err); /// device /// }, /// }; @@ -323,7 +325,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// let device = nitrokey::connect()?; /// match device.get_serial_number() { /// Ok(number) => println!("serial no: {}", number), - /// Err(err) => println!("Could not get serial number: {}", err), + /// Err(err) => eprintln!("Could not get serial number: {}", err), /// }; /// # Ok(()) /// # } @@ -343,13 +345,15 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; - /// let count = device.get_user_retry_count(); - /// println!("{} remaining authentication attempts (user)", count); + /// match device.get_user_retry_count() { + /// Ok(count) => println!("{} remaining authentication attempts (user)", count), + /// Err(err) => eprintln!("Could not get user retry count: {}", err), + /// } /// # Ok(()) /// # } /// ``` - fn get_user_retry_count(&self) -> u8 { - unsafe { nitrokey_sys::NK_get_user_retry_count() } + fn get_user_retry_count(&self) -> Result<u8, Error> { + result_or_error(unsafe { nitrokey_sys::NK_get_user_retry_count() }) } /// Returns the number of remaining authentication attempts for the admin. The total number of @@ -364,15 +368,18 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// let count = device.get_admin_retry_count(); - /// println!("{} remaining authentication attempts (admin)", count); + /// match device.get_admin_retry_count() { + /// Ok(count) => println!("{} remaining authentication attempts (admin)", count), + /// Err(err) => eprintln!("Could not get admin retry count: {}", err), + /// } /// # Ok(()) /// # } /// ``` - fn get_admin_retry_count(&self) -> u8 { - unsafe { nitrokey_sys::NK_get_admin_retry_count() } + fn get_admin_retry_count(&self) -> Result<u8, Error> { + result_or_error(unsafe { nitrokey_sys::NK_get_admin_retry_count() }) } - /// Returns the major part of the firmware version (should be zero). + /// Returns the firmware version. /// /// # Example /// @@ -382,37 +389,24 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; - /// println!( - /// "Firmware version: {}.{}", - /// device.get_major_firmware_version(), - /// device.get_minor_firmware_version(), - /// ); + /// match device.get_firmware_version() { + /// Ok(version) => println!("Firmware version: {}", version), + /// Err(err) => eprintln!("Could not access firmware version: {}", err), + /// }; /// # 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::Error; - /// - /// # fn try_main() -> Result<(), Error> { - /// 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() } + fn get_firmware_version(&self) -> Result<FirmwareVersion, Error> { + let major = result_or_error(unsafe { nitrokey_sys::NK_get_major_firmware_version() })?; + let minor = result_or_error(unsafe { nitrokey_sys::NK_get_minor_firmware_version() })?; + let max = i32::from(u8::max_value()); + if major < 0 || minor < 0 || major > max || minor > max { + return Err(Error::UnexpectedError); + } + Ok(FirmwareVersion { + major: major as u8, + minor: minor as u8, + }) } /// Returns the current configuration of the Nitrokey device. @@ -458,10 +452,10 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// match device.change_admin_pin("12345678", "12345679") { /// Ok(()) => println!("Updated admin PIN."), - /// Err(err) => println!("Failed to update admin PIN: {}", err), + /// Err(err) => eprintln!("Failed to update admin PIN: {}", err), /// }; /// # Ok(()) /// # } @@ -469,7 +463,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn change_admin_pin(&self, current: &str, new: &str) -> Result<(), Error> { + fn change_admin_pin(&mut self, current: &str, new: &str) -> Result<(), Error> { let current_string = get_cstring(current)?; let new_string = get_cstring(new)?; get_command_result(unsafe { @@ -491,10 +485,10 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// match device.change_user_pin("123456", "123457") { /// Ok(()) => println!("Updated admin PIN."), - /// Err(err) => println!("Failed to update admin PIN: {}", err), + /// Err(err) => eprintln!("Failed to update admin PIN: {}", err), /// }; /// # Ok(()) /// # } @@ -502,7 +496,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn change_user_pin(&self, current: &str, new: &str) -> Result<(), Error> { + fn change_user_pin(&mut self, current: &str, new: &str) -> Result<(), Error> { let current_string = get_cstring(current)?; let new_string = get_cstring(new)?; get_command_result(unsafe { @@ -524,10 +518,10 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// match device.unlock_user_pin("12345678", "123456") { /// Ok(()) => println!("Unlocked user PIN."), - /// Err(err) => println!("Failed to unlock user PIN: {}", err), + /// Err(err) => eprintln!("Failed to unlock user PIN: {}", err), /// }; /// # Ok(()) /// # } @@ -535,7 +529,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn unlock_user_pin(&self, admin_pin: &str, user_pin: &str) -> Result<(), Error> { + fn unlock_user_pin(&mut self, admin_pin: &str, user_pin: &str) -> Result<(), Error> { let admin_pin_string = get_cstring(admin_pin)?; let user_pin_string = get_cstring(user_pin)?; get_command_result(unsafe { @@ -558,15 +552,15 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// match device.lock() { /// Ok(()) => println!("Locked the Nitrokey device."), - /// Err(err) => println!("Could not lock the Nitrokey device: {}", err), + /// Err(err) => eprintln!("Could not lock the Nitrokey device: {}", err), /// }; /// # Ok(()) /// # } /// ``` - fn lock(&self) -> Result<(), Error> { + fn lock(&mut self) -> Result<(), Error> { get_command_result(unsafe { nitrokey_sys::NK_lock_device() }) } @@ -589,17 +583,17 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// match device.factory_reset("12345678") { /// Ok(()) => println!("Performed a factory reset."), - /// Err(err) => println!("Could not perform a factory reset: {}", err), + /// Err(err) => eprintln!("Could not perform a factory reset: {}", err), /// }; /// # Ok(()) /// # } /// ``` /// /// [`build_aes_key`]: #method.build_aes_key - fn factory_reset(&self, admin_pin: &str) -> Result<(), Error> { + fn factory_reset(&mut self, admin_pin: &str) -> Result<(), Error> { let admin_pin_string = get_cstring(admin_pin)?; get_command_result(unsafe { nitrokey_sys::NK_factory_reset(admin_pin_string.as_ptr()) }) } @@ -623,17 +617,17 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// match device.build_aes_key("12345678") { /// Ok(()) => println!("New AES keys have been built."), - /// Err(err) => println!("Could not build new AES keys: {}", err), + /// Err(err) => eprintln!("Could not build new AES keys: {}", err), /// }; /// # Ok(()) /// # } /// ``` /// /// [`factory_reset`]: #method.factory_reset - fn build_aes_key(&self, admin_pin: &str) -> Result<(), Error> { + fn build_aes_key(&mut self, admin_pin: &str) -> Result<(), Error> { let admin_pin_string = get_cstring(admin_pin)?; get_command_result(unsafe { nitrokey_sys::NK_build_aes_key(admin_pin_string.as_ptr()) }) } @@ -655,7 +649,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// /// match nitrokey::connect() { /// Ok(device) => do_something(device), -/// Err(err) => println!("Could not connect to a Nitrokey: {}", err), +/// Err(err) => eprintln!("Could not connect to a Nitrokey: {}", err), /// } /// ``` /// @@ -687,7 +681,7 @@ pub fn connect() -> Result<DeviceWrapper, Error> { /// /// match nitrokey::connect_model(Model::Pro) { /// Ok(device) => do_something(device), -/// Err(err) => println!("Could not connect to a Nitrokey Pro: {}", err), +/// Err(err) => eprintln!("Could not connect to a Nitrokey Pro: {}", err), /// } /// ``` /// @@ -734,6 +728,13 @@ impl DeviceWrapper { DeviceWrapper::Pro(ref pro) => pro, } } + + fn device_mut(&mut self) -> &mut dyn Device { + match *self { + DeviceWrapper::Storage(ref mut storage) => storage, + DeviceWrapper::Pro(ref mut pro) => pro, + } + } } impl From<Pro> for DeviceWrapper { @@ -757,8 +758,8 @@ impl GenerateOtp for DeviceWrapper { self.device().get_totp_slot_name(slot) } - fn get_hotp_code(&self, slot: u8) -> Result<String, Error> { - self.device().get_hotp_code(slot) + fn get_hotp_code(&mut self, slot: u8) -> Result<String, Error> { + self.device_mut().get_hotp_code(slot) } fn get_totp_code(&self, slot: u8) -> Result<String, Error> { @@ -791,7 +792,7 @@ impl Pro { /// /// match nitrokey::Pro::connect() { /// Ok(device) => use_pro(device), - /// Err(err) => println!("Could not connect to the Nitrokey Pro: {}", err), + /// Err(err) => eprintln!("Could not connect to the Nitrokey Pro: {}", err), /// } /// ``` /// @@ -844,7 +845,7 @@ impl Storage { /// /// match nitrokey::Storage::connect() { /// Ok(device) => use_storage(device), - /// Err(err) => println!("Could not connect to the Nitrokey Storage: {}", err), + /// Err(err) => eprintln!("Could not connect to the Nitrokey Storage: {}", err), /// } /// ``` /// @@ -881,10 +882,10 @@ impl Storage { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::Storage::connect()?; + /// let mut device = nitrokey::Storage::connect()?; /// match device.change_update_pin("12345678", "87654321") { /// Ok(()) => println!("Updated update PIN."), - /// Err(err) => println!("Failed to update update PIN: {}", err), + /// Err(err) => eprintln!("Failed to update update PIN: {}", err), /// }; /// # Ok(()) /// # } @@ -892,7 +893,7 @@ impl Storage { /// /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - pub fn change_update_pin(&self, current: &str, new: &str) -> Result<(), Error> { + pub fn change_update_pin(&mut self, current: &str, new: &str) -> Result<(), Error> { let current_string = get_cstring(current)?; let new_string = get_cstring(new)?; get_command_result(unsafe { @@ -918,10 +919,10 @@ impl Storage { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::Storage::connect()?; + /// let mut device = nitrokey::Storage::connect()?; /// match device.enable_firmware_update("12345678") { /// Ok(()) => println!("Nitrokey entered update mode."), - /// Err(err) => println!("Could not enter update mode: {}", err), + /// Err(err) => eprintln!("Could not enter update mode: {}", err), /// }; /// # Ok(()) /// # } @@ -929,7 +930,7 @@ impl Storage { /// /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - pub fn enable_firmware_update(&self, update_pin: &str) -> Result<(), Error> { + pub fn enable_firmware_update(&mut self, update_pin: &str) -> Result<(), Error> { let update_pin_string = get_cstring(update_pin)?; get_command_result(unsafe { nitrokey_sys::NK_enable_firmware_update(update_pin_string.as_ptr()) @@ -952,10 +953,10 @@ impl Storage { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::Storage::connect()?; + /// let mut device = nitrokey::Storage::connect()?; /// match device.enable_encrypted_volume("123456") { /// Ok(()) => println!("Enabled the encrypted volume."), - /// Err(err) => println!("Could not enable the encrypted volume: {}", err), + /// Err(err) => eprintln!("Could not enable the encrypted volume: {}", err), /// }; /// # Ok(()) /// # } @@ -963,7 +964,7 @@ impl Storage { /// /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - pub fn enable_encrypted_volume(&self, user_pin: &str) -> Result<(), Error> { + pub fn enable_encrypted_volume(&mut self, user_pin: &str) -> Result<(), Error> { let user_pin = get_cstring(user_pin)?; get_command_result(unsafe { nitrokey_sys::NK_unlock_encrypted_volume(user_pin.as_ptr()) }) } @@ -981,7 +982,7 @@ impl Storage { /// fn use_volume() {} /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::Storage::connect()?; + /// let mut device = nitrokey::Storage::connect()?; /// match device.enable_encrypted_volume("123456") { /// Ok(()) => { /// println!("Enabled the encrypted volume."); @@ -989,16 +990,16 @@ impl Storage { /// match device.disable_encrypted_volume() { /// Ok(()) => println!("Disabled the encrypted volume."), /// Err(err) => { - /// println!("Could not disable the encrypted volume: {}", err); + /// eprintln!("Could not disable the encrypted volume: {}", err); /// }, /// }; /// }, - /// Err(err) => println!("Could not enable the encrypted volume: {}", err), + /// Err(err) => eprintln!("Could not enable the encrypted volume: {}", err), /// }; /// # Ok(()) /// # } /// ``` - pub fn disable_encrypted_volume(&self) -> Result<(), Error> { + pub fn disable_encrypted_volume(&mut self) -> Result<(), Error> { get_command_result(unsafe { nitrokey_sys::NK_lock_encrypted_volume() }) } @@ -1027,11 +1028,11 @@ impl Storage { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::Storage::connect()?; + /// let mut device = nitrokey::Storage::connect()?; /// device.enable_encrypted_volume("123445")?; /// match device.enable_hidden_volume("hidden-pw") { /// Ok(()) => println!("Enabled a hidden volume."), - /// Err(err) => println!("Could not enable the hidden volume: {}", err), + /// Err(err) => eprintln!("Could not enable the hidden volume: {}", err), /// }; /// # Ok(()) /// # } @@ -1040,7 +1041,7 @@ impl Storage { /// [`enable_encrypted_volume`]: #method.enable_encrypted_volume /// [`AesDecryptionFailed`]: enum.CommandError.html#variant.AesDecryptionFailed /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString - pub fn enable_hidden_volume(&self, volume_password: &str) -> Result<(), Error> { + pub fn enable_hidden_volume(&mut self, volume_password: &str) -> Result<(), Error> { let volume_password = get_cstring(volume_password)?; get_command_result(unsafe { nitrokey_sys::NK_unlock_hidden_volume(volume_password.as_ptr()) @@ -1060,7 +1061,7 @@ impl Storage { /// fn use_volume() {} /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::Storage::connect()?; + /// let mut device = nitrokey::Storage::connect()?; /// device.enable_encrypted_volume("123445")?; /// match device.enable_hidden_volume("hidden-pw") { /// Ok(()) => { @@ -1069,16 +1070,16 @@ impl Storage { /// match device.disable_hidden_volume() { /// Ok(()) => println!("Disabled the hidden volume."), /// Err(err) => { - /// println!("Could not disable the hidden volume: {}", err); + /// eprintln!("Could not disable the hidden volume: {}", err); /// }, /// }; /// }, - /// Err(err) => println!("Could not enable the hidden volume: {}", err), + /// Err(err) => eprintln!("Could not enable the hidden volume: {}", err), /// }; /// # Ok(()) /// # } /// ``` - pub fn disable_hidden_volume(&self) -> Result<(), Error> { + pub fn disable_hidden_volume(&mut self) -> Result<(), Error> { get_command_result(unsafe { nitrokey_sys::NK_lock_hidden_volume() }) } @@ -1107,7 +1108,7 @@ impl Storage { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::Storage::connect()?; + /// let mut device = nitrokey::Storage::connect()?; /// device.enable_encrypted_volume("123445")?; /// device.create_hidden_volume(0, 0, 100, "hidden-pw")?; /// # Ok(()) @@ -1117,7 +1118,7 @@ impl Storage { /// [`AesDecryptionFailed`]: enum.CommandError.html#variant.AesDecryptionFailed /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString pub fn create_hidden_volume( - &self, + &mut self, slot: u8, start: u8, end: u8, @@ -1147,10 +1148,10 @@ impl Storage { /// use nitrokey::VolumeMode; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::Storage::connect()?; - /// match device.set_unencrypted_volume_mode("123456", VolumeMode::ReadWrite) { + /// let mut device = nitrokey::Storage::connect()?; + /// match device.set_unencrypted_volume_mode("12345678", VolumeMode::ReadWrite) { /// Ok(()) => println!("Set the unencrypted volume to read-write mode."), - /// Err(err) => println!("Could not set the unencrypted volume to read-write mode: {}", err), + /// Err(err) => eprintln!("Could not set the unencrypted volume to read-write mode: {}", err), /// }; /// # Ok(()) /// # } @@ -1159,7 +1160,7 @@ impl Storage { /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword pub fn set_unencrypted_volume_mode( - &self, + &mut self, admin_pin: &str, mode: VolumeMode, ) -> Result<(), Error> { @@ -1175,6 +1176,51 @@ impl Storage { get_command_result(result) } + /// Sets the access mode of the encrypted volume. + /// + /// This command will reconnect the encrypted volume so buffers should be flushed before + /// calling it. It is only available in firmware version 0.49. + /// + /// # Errors + /// + /// - [`InvalidString`][] if the provided password contains a null byte + /// - [`WrongPassword`][] if the provided admin password is wrong + /// + /// # Example + /// + /// ```no_run + /// # use nitrokey::Error; + /// use nitrokey::VolumeMode; + /// + /// # fn try_main() -> Result<(), Error> { + /// let mut device = nitrokey::Storage::connect()?; + /// match device.set_encrypted_volume_mode("12345678", VolumeMode::ReadWrite) { + /// Ok(()) => println!("Set the encrypted volume to read-write mode."), + /// Err(err) => eprintln!("Could not set the encrypted volume to read-write mode: {}", err), + /// }; + /// # Ok(()) + /// # } + /// ``` + /// + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString + /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword + pub fn set_encrypted_volume_mode( + &mut self, + admin_pin: &str, + mode: VolumeMode, + ) -> Result<(), Error> { + let admin_pin = get_cstring(admin_pin)?; + let result = match mode { + VolumeMode::ReadOnly => unsafe { + nitrokey_sys::NK_set_encrypted_read_only(admin_pin.as_ptr()) + }, + VolumeMode::ReadWrite => unsafe { + nitrokey_sys::NK_set_encrypted_read_write(admin_pin.as_ptr()) + }, + }; + get_command_result(result) + } + /// Returns the status of the connected storage device. /// /// # Example @@ -1190,7 +1236,7 @@ impl Storage { /// Ok(status) => { /// println!("SD card ID: {:#x}", status.serial_number_sd_card); /// }, - /// Err(err) => println!("Could not get Storage status: {}", err), + /// Err(err) => eprintln!("Could not get Storage status: {}", err), /// }; /// # Ok(()) /// # } @@ -1234,7 +1280,7 @@ impl Storage { /// println!("SD card ID: {:#x}", data.sd_card.serial_number); /// println!("SD card size: {} GB", data.sd_card.size); /// }, - /// Err(err) => println!("Could not get Storage production info: {}", err), + /// Err(err) => eprintln!("Could not get Storage production info: {}", err), /// }; /// # Ok(()) /// # } @@ -1276,10 +1322,10 @@ impl Storage { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::Storage::connect()?; + /// let mut device = nitrokey::Storage::connect()?; /// match device.clear_new_sd_card_warning("12345678") { /// Ok(()) => println!("Cleared the new SD card warning."), - /// Err(err) => println!("Could not set the clear the new SD card warning: {}", err), + /// Err(err) => eprintln!("Could not set the clear the new SD card warning: {}", err), /// }; /// # Ok(()) /// # } @@ -1287,7 +1333,7 @@ impl Storage { /// /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - pub fn clear_new_sd_card_warning(&self, admin_pin: &str) -> Result<(), Error> { + pub fn clear_new_sd_card_warning(&mut self, admin_pin: &str) -> Result<(), Error> { let admin_pin = get_cstring(admin_pin)?; get_command_result(unsafe { nitrokey_sys::NK_clear_new_sd_card_warning(admin_pin.as_ptr()) @@ -1295,7 +1341,7 @@ impl Storage { } /// Blinks the red and green LED alternatively and infinitely until the device is reconnected. - pub fn wink(&self) -> Result<(), Error> { + pub fn wink(&mut self) -> Result<(), Error> { get_command_result(unsafe { nitrokey_sys::NK_wink() }) } @@ -1315,7 +1361,7 @@ impl Storage { /// /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - pub fn export_firmware(&self, admin_pin: &str) -> Result<(), Error> { + pub fn export_firmware(&mut self, admin_pin: &str) -> Result<(), Error> { let admin_pin_string = get_cstring(admin_pin)?; get_command_result(unsafe { nitrokey_sys::NK_export_firmware(admin_pin_string.as_ptr()) }) } diff --git a/nitrokey/src/lib.rs b/nitrokey/src/lib.rs index f2d524e..c35829c 100644 --- a/nitrokey/src/lib.rs +++ b/nitrokey/src/lib.rs @@ -47,13 +47,13 @@ //! let device = nitrokey::connect()?; //! let slot_data = OtpSlotData::new(1, "test", "01234567890123456689", OtpMode::SixDigits); //! match device.authenticate_admin("12345678") { -//! Ok(admin) => { +//! Ok(mut admin) => { //! match admin.write_hotp_slot(slot_data, 0) { //! Ok(()) => println!("Successfully wrote slot."), -//! Err(err) => println!("Could not write slot: {}", err), +//! Err(err) => eprintln!("Could not write slot: {}", err), //! } //! }, -//! Err((_, err)) => println!("Could not authenticate as admin: {}", err), +//! Err((_, err)) => eprintln!("Could not authenticate as admin: {}", err), //! } //! # Ok(()) //! # } @@ -66,10 +66,10 @@ //! # use nitrokey::Error; //! //! # fn try_main() -> Result<(), Error> { -//! let device = nitrokey::connect()?; +//! let mut device = nitrokey::connect()?; //! match device.get_hotp_code(1) { //! Ok(code) => println!("Generated HOTP code: {}", code), -//! Err(err) => println!("Could not generate HOTP code: {}", err), +//! Err(err) => eprintln!("Could not generate HOTP code: {}", err), //! } //! # Ok(()) //! # } diff --git a/nitrokey/src/otp.rs b/nitrokey/src/otp.rs index 6e0379b..ee142c7 100644 --- a/nitrokey/src/otp.rs +++ b/nitrokey/src/otp.rs @@ -38,13 +38,13 @@ pub trait ConfigureOtp { /// let device = nitrokey::connect()?; /// let slot_data = OtpSlotData::new(1, "test", "01234567890123456689", OtpMode::SixDigits); /// match device.authenticate_admin("12345678") { - /// Ok(admin) => { + /// Ok(mut admin) => { /// match admin.write_hotp_slot(slot_data, 0) { /// Ok(()) => println!("Successfully wrote slot."), - /// Err(err) => println!("Could not write slot: {}", err), + /// Err(err) => eprintln!("Could not write slot: {}", err), /// } /// }, - /// Err((_, err)) => println!("Could not authenticate as admin: {}", err), + /// Err((_, err)) => eprintln!("Could not authenticate as admin: {}", err), /// } /// # Ok(()) /// # } @@ -53,7 +53,7 @@ pub trait ConfigureOtp { /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`NoName`]: enum.CommandError.html#variant.NoName - fn write_hotp_slot(&self, data: OtpSlotData, counter: u64) -> Result<(), Error>; + fn write_hotp_slot(&mut self, data: OtpSlotData, counter: u64) -> Result<(), Error>; /// Configure a TOTP slot with the given data and set the TOTP time window to the given value /// (default 30). @@ -74,13 +74,13 @@ pub trait ConfigureOtp { /// let device = nitrokey::connect()?; /// let slot_data = OtpSlotData::new(1, "test", "01234567890123456689", OtpMode::EightDigits); /// match device.authenticate_admin("12345678") { - /// Ok(admin) => { + /// Ok(mut admin) => { /// match admin.write_totp_slot(slot_data, 30) { /// Ok(()) => println!("Successfully wrote slot."), - /// Err(err) => println!("Could not write slot: {}", err), + /// Err(err) => eprintln!("Could not write slot: {}", err), /// } /// }, - /// Err((_, err)) => println!("Could not authenticate as admin: {}", err), + /// Err((_, err)) => eprintln!("Could not authenticate as admin: {}", err), /// } /// # Ok(()) /// # } @@ -89,7 +89,7 @@ pub trait ConfigureOtp { /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`NoName`]: enum.CommandError.html#variant.NoName - fn write_totp_slot(&self, data: OtpSlotData, time_window: u16) -> Result<(), Error>; + fn write_totp_slot(&mut self, data: OtpSlotData, time_window: u16) -> Result<(), Error>; /// Erases an HOTP slot. /// @@ -106,20 +106,20 @@ pub trait ConfigureOtp { /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// match device.authenticate_admin("12345678") { - /// Ok(admin) => { + /// Ok(mut admin) => { /// match admin.erase_hotp_slot(1) { /// Ok(()) => println!("Successfully erased slot."), - /// Err(err) => println!("Could not erase slot: {}", err), + /// Err(err) => eprintln!("Could not erase slot: {}", err), /// } /// }, - /// Err((_, err)) => println!("Could not authenticate as admin: {}", err), + /// Err((_, err)) => eprintln!("Could not authenticate as admin: {}", err), /// } /// # Ok(()) /// # } /// ``` /// /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot - fn erase_hotp_slot(&self, slot: u8) -> Result<(), Error>; + fn erase_hotp_slot(&mut self, slot: u8) -> Result<(), Error>; /// Erases a TOTP slot. /// @@ -136,20 +136,20 @@ pub trait ConfigureOtp { /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// match device.authenticate_admin("12345678") { - /// Ok(admin) => { + /// Ok(mut admin) => { /// match admin.erase_totp_slot(1) { /// Ok(()) => println!("Successfully erased slot."), - /// Err(err) => println!("Could not erase slot: {}", err), + /// Err(err) => eprintln!("Could not erase slot: {}", err), /// } /// }, - /// Err((_, err)) => println!("Could not authenticate as admin: {}", err), + /// Err((_, err)) => eprintln!("Could not authenticate as admin: {}", err), /// } /// # Ok(()) /// # } /// ``` /// /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot - fn erase_totp_slot(&self, slot: u8) -> Result<(), Error>; + fn erase_totp_slot(&mut self, slot: u8) -> Result<(), Error>; } /// Provides methods to generate OTP codes and to query OTP slots on a Nitrokey @@ -171,11 +171,11 @@ pub trait GenerateOtp { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// let time = time::SystemTime::now().duration_since(time::UNIX_EPOCH); /// match time { /// Ok(time) => device.set_time(time.as_secs(), false)?, - /// Err(_) => println!("The system time is before the Unix epoch!"), + /// Err(_) => eprintln!("The system time is before the Unix epoch!"), /// } /// # Ok(()) /// # } @@ -187,7 +187,7 @@ pub trait GenerateOtp { /// /// [`get_totp_code`]: #method.get_totp_code /// [`Timestamp`]: enum.CommandError.html#variant.Timestamp - fn set_time(&self, time: u64, force: bool) -> Result<(), Error> { + fn set_time(&mut self, time: u64, force: bool) -> Result<(), Error> { let result = if force { unsafe { nitrokey_sys::NK_totp_set_time(time) } } else { @@ -212,8 +212,8 @@ pub trait GenerateOtp { /// let device = nitrokey::connect()?; /// match device.get_hotp_slot_name(1) { /// Ok(name) => println!("HOTP slot 1: {}", name), - /// Err(Error::CommandError(CommandError::SlotNotProgrammed)) => println!("HOTP slot 1 not programmed"), - /// Err(err) => println!("Could not get slot name: {}", err), + /// Err(Error::CommandError(CommandError::SlotNotProgrammed)) => eprintln!("HOTP slot 1 not programmed"), + /// Err(err) => eprintln!("Could not get slot name: {}", err), /// }; /// # Ok(()) /// # } @@ -241,8 +241,8 @@ pub trait GenerateOtp { /// let device = nitrokey::connect()?; /// match device.get_totp_slot_name(1) { /// Ok(name) => println!("TOTP slot 1: {}", name), - /// Err(Error::CommandError(CommandError::SlotNotProgrammed)) => println!("TOTP slot 1 not programmed"), - /// Err(err) => println!("Could not get slot name: {}", err), + /// Err(Error::CommandError(CommandError::SlotNotProgrammed)) => eprintln!("TOTP slot 1 not programmed"), + /// Err(err) => eprintln!("Could not get slot name: {}", err), /// }; /// # Ok(()) /// # } @@ -270,7 +270,7 @@ pub trait GenerateOtp { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// let code = device.get_hotp_code(1)?; /// println!("Generated HOTP code on slot 1: {}", code); /// # Ok(()) @@ -281,7 +281,7 @@ pub trait GenerateOtp { /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot /// [`NotAuthorized`]: enum.CommandError.html#variant.NotAuthorized /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed - fn get_hotp_code(&self, slot: u8) -> Result<String, Error> { + fn get_hotp_code(&mut self, slot: u8) -> Result<String, Error> { result_from_string(unsafe { nitrokey_sys::NK_get_hotp_code(slot) }) } @@ -305,7 +305,7 @@ pub trait GenerateOtp { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// let time = time::SystemTime::now().duration_since(time::UNIX_EPOCH); /// match time { /// Ok(time) => { @@ -313,7 +313,7 @@ pub trait GenerateOtp { /// let code = device.get_totp_code(1)?; /// println!("Generated TOTP code on slot 1: {}", code); /// }, - /// Err(_) => println!("Timestamps before 1970-01-01 are not supported!"), + /// Err(_) => eprintln!("Timestamps before 1970-01-01 are not supported!"), /// } /// # Ok(()) /// # } diff --git a/nitrokey/src/pws.rs b/nitrokey/src/pws.rs index fcf057b..371de6e 100644 --- a/nitrokey/src/pws.rs +++ b/nitrokey/src/pws.rs @@ -43,9 +43,10 @@ pub const SLOT_COUNT: u8 = 16; /// } /// /// # fn try_main() -> Result<(), Error> { -/// let device = nitrokey::connect()?; +/// let mut device = nitrokey::connect()?; /// let pws = device.get_password_safe("123456")?; /// use_password_safe(&pws); +/// drop(pws); /// device.lock()?; /// # Ok(()) /// # } @@ -97,14 +98,14 @@ pub trait GetPasswordSafe { /// fn use_password_safe(pws: &PasswordSafe) {} /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// match device.get_password_safe("123456") { /// Ok(pws) => { /// use_password_safe(&pws); - /// device.lock()?; /// }, - /// Err(err) => println!("Could not open the password safe: {}", err), + /// Err(err) => eprintln!("Could not open the password safe: {}", err), /// }; + /// device.lock()?; /// # Ok(()) /// # } /// ``` @@ -116,7 +117,7 @@ pub trait GetPasswordSafe { /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`Unknown`]: enum.CommandError.html#variant.Unknown /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe<'_>, Error>; + fn get_password_safe(&mut self, user_pin: &str) -> Result<PasswordSafe<'_>, Error>; } fn get_password_safe<'a>( @@ -148,7 +149,7 @@ impl<'a> PasswordSafe<'a> { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// let pws = device.get_password_safe("123456")?; /// pws.get_slot_status()?.iter().enumerate().for_each(|(slot, programmed)| { /// let status = match *programmed { @@ -193,7 +194,7 @@ impl<'a> PasswordSafe<'a> { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// match device.get_password_safe("123456") { /// Ok(pws) => { /// let name = pws.get_slot_name(0)?; @@ -201,7 +202,7 @@ impl<'a> PasswordSafe<'a> { /// let password = pws.get_slot_login(0)?; /// println!("Credentials for {}: login {}, password {}", name, login, password); /// }, - /// Err(err) => println!("Could not open the password safe: {}", err), + /// Err(err) => eprintln!("Could not open the password safe: {}", err), /// }; /// # Ok(()) /// # } @@ -230,7 +231,7 @@ impl<'a> PasswordSafe<'a> { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// let pws = device.get_password_safe("123456")?; /// let name = pws.get_slot_name(0)?; /// let login = pws.get_slot_login(0)?; @@ -263,7 +264,7 @@ impl<'a> PasswordSafe<'a> { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// let pws = device.get_password_safe("123456")?; /// let name = pws.get_slot_name(0)?; /// let login = pws.get_slot_login(0)?; @@ -294,7 +295,7 @@ impl<'a> PasswordSafe<'a> { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// let pws = device.get_password_safe("123456")?; /// let name = pws.get_slot_name(0)?; /// let login = pws.get_slot_login(0)?; @@ -307,7 +308,7 @@ impl<'a> PasswordSafe<'a> { /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString pub fn write_slot( - &self, + &mut self, slot: u8, name: &str, login: &str, @@ -340,18 +341,18 @@ impl<'a> PasswordSafe<'a> { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; - /// let pws = device.get_password_safe("123456")?; + /// let mut device = nitrokey::connect()?; + /// let mut pws = device.get_password_safe("123456")?; /// match pws.erase_slot(0) { /// Ok(()) => println!("Erased slot 0."), - /// Err(err) => println!("Could not erase slot 0: {}", err), + /// Err(err) => eprintln!("Could not erase slot 0: {}", err), /// }; /// # Ok(()) /// # } /// ``` /// /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot - pub fn erase_slot(&self, slot: u8) -> Result<(), Error> { + pub fn erase_slot(&mut self, slot: u8) -> Result<(), Error> { get_command_result(unsafe { nitrokey_sys::NK_erase_password_safe_slot(slot) }) } } @@ -364,19 +365,19 @@ impl<'a> Drop for PasswordSafe<'a> { } impl GetPasswordSafe for Pro { - fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe<'_>, Error> { + fn get_password_safe(&mut self, user_pin: &str) -> Result<PasswordSafe<'_>, Error> { get_password_safe(self, user_pin) } } impl GetPasswordSafe for Storage { - fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe<'_>, Error> { + fn get_password_safe(&mut self, user_pin: &str) -> Result<PasswordSafe<'_>, Error> { get_password_safe(self, user_pin) } } impl GetPasswordSafe for DeviceWrapper { - fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe<'_>, Error> { + fn get_password_safe(&mut self, user_pin: &str) -> Result<PasswordSafe<'_>, Error> { get_password_safe(self, user_pin) } } diff --git a/nitrokey/src/util.rs b/nitrokey/src/util.rs index b7e8cd3..fdb73c3 100644 --- a/nitrokey/src/util.rs +++ b/nitrokey/src/util.rs @@ -53,6 +53,10 @@ pub fn result_from_string(ptr: *const c_char) -> Result<String, Error> { } } +pub fn result_or_error<T>(value: T) -> Result<T, Error> { + get_last_result().and(Ok(value)) +} + pub fn get_command_result(value: c_int) -> Result<(), Error> { if value == 0 { Ok(()) diff --git a/nitrokey/tests/device.rs b/nitrokey/tests/device.rs index c790049..5c52024 100644 --- a/nitrokey/tests/device.rs +++ b/nitrokey/tests/device.rs @@ -10,11 +10,10 @@ use std::{thread, time}; use nitrokey::{ Authenticate, CommandError, CommunicationError, Config, ConfigureOtp, Device, Error, GenerateOtp, GetPasswordSafe, LibraryError, OtpMode, OtpSlotData, Storage, VolumeMode, + DEFAULT_ADMIN_PIN, DEFAULT_USER_PIN, }; use nitrokey_test::test as test_device; -use crate::util::{ADMIN_PASSWORD, USER_PASSWORD}; - static ADMIN_NEW_PASSWORD: &str = "1234567890"; static UPDATE_PIN: &str = "12345678"; static UPDATE_NEW_PIN: &str = "87654321"; @@ -55,9 +54,9 @@ fn connect_pro(device: Pro) { assert_eq!(device.get_model(), nitrokey::Model::Pro); drop(device); - assert!(nitrokey::connect().is_ok()); - assert!(nitrokey::connect_model(nitrokey::Model::Pro).is_ok()); - assert!(nitrokey::Pro::connect().is_ok()); + assert_any_ok!(nitrokey::connect()); + assert_any_ok!(nitrokey::connect_model(nitrokey::Model::Pro)); + assert_any_ok!(nitrokey::Pro::connect()); } #[test_device] @@ -65,9 +64,9 @@ fn connect_storage(device: Storage) { assert_eq!(device.get_model(), nitrokey::Model::Storage); drop(device); - assert!(nitrokey::connect().is_ok()); - assert!(nitrokey::connect_model(nitrokey::Model::Storage).is_ok()); - assert!(nitrokey::Storage::connect().is_ok()); + assert_any_ok!(nitrokey::connect()); + assert_any_ok!(nitrokey::connect_model(nitrokey::Model::Storage)); + assert_any_ok!(nitrokey::Storage::connect()); } fn assert_empty_serial_number() { @@ -87,36 +86,34 @@ fn disconnect(device: DeviceWrapper) { #[test_device] fn get_serial_number(device: DeviceWrapper) { - let result = device.get_serial_number(); - assert!(result.is_ok()); - let serial_number = result.unwrap(); + let serial_number = unwrap_ok!(device.get_serial_number()); assert!(serial_number.is_ascii()); assert!(serial_number.chars().all(|c| c.is_ascii_hexdigit())); } #[test_device] fn get_firmware_version(device: Pro) { - assert_eq!(0, device.get_major_firmware_version()); - let minor = device.get_minor_firmware_version(); - assert!(minor > 0); + let version = unwrap_ok!(device.get_firmware_version()); + assert_eq!(0, version.major); + assert!(version.minor > 0); } fn admin_retry<T: Authenticate + Device>(device: T, suffix: &str, count: u8) -> T { - let result = device.authenticate_admin(&(ADMIN_PASSWORD.to_owned() + suffix)); + let result = device.authenticate_admin(&(DEFAULT_ADMIN_PIN.to_owned() + suffix)); let device = match result { Ok(admin) => admin.device(), Err((device, _)) => device, }; - assert_eq!(count, device.get_admin_retry_count()); + assert_ok!(count, device.get_admin_retry_count()); return device; } fn user_retry<T: Authenticate + Device>(device: T, suffix: &str, count: u8) -> T { - let result = device.authenticate_user(&(USER_PASSWORD.to_owned() + suffix)); + let result = device.authenticate_user(&(DEFAULT_USER_PIN.to_owned() + suffix)); let device = match result { Ok(admin) => admin.device(), Err((device, _)) => device, }; - assert_eq!(count, device.get_user_retry_count()); + assert_ok!(count, device.get_user_retry_count()); return device; } @@ -135,7 +132,7 @@ fn get_retry_count(device: DeviceWrapper) { #[test_device] fn config(device: DeviceWrapper) { - let admin = device.authenticate_admin(ADMIN_PASSWORD).unwrap(); + let mut admin = unwrap_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN)); let config = Config::new(None, None, None, true); assert_ok!((), admin.write_config(config)); @@ -155,53 +152,67 @@ fn config(device: DeviceWrapper) { #[test_device] fn change_user_pin(device: DeviceWrapper) { - let device = device.authenticate_user(USER_PASSWORD).unwrap().device(); + let device = device.authenticate_user(DEFAULT_USER_PIN).unwrap().device(); let device = device.authenticate_user(USER_NEW_PASSWORD).unwrap_err().0; - assert_ok!((), device.change_user_pin(USER_PASSWORD, USER_NEW_PASSWORD)); + let mut device = device; + assert_ok!( + (), + device.change_user_pin(DEFAULT_USER_PIN, USER_NEW_PASSWORD) + ); - let device = device.authenticate_user(USER_PASSWORD).unwrap_err().0; + let device = device.authenticate_user(DEFAULT_USER_PIN).unwrap_err().0; let device = device .authenticate_user(USER_NEW_PASSWORD) .unwrap() .device(); - let result = device.change_user_pin(USER_PASSWORD, USER_PASSWORD); + let mut device = device; + let result = device.change_user_pin(DEFAULT_USER_PIN, DEFAULT_USER_PIN); assert_cmd_err!(CommandError::WrongPassword, result); - assert_ok!((), device.change_user_pin(USER_NEW_PASSWORD, USER_PASSWORD)); + assert_ok!( + (), + device.change_user_pin(USER_NEW_PASSWORD, DEFAULT_USER_PIN) + ); - let device = device.authenticate_user(USER_PASSWORD).unwrap().device(); + let device = device.authenticate_user(DEFAULT_USER_PIN).unwrap().device(); assert!(device.authenticate_user(USER_NEW_PASSWORD).is_err()); } #[test_device] fn change_admin_pin(device: DeviceWrapper) { - let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap().device(); - let device = device.authenticate_admin(ADMIN_NEW_PASSWORD).unwrap_err().0; + let device = device + .authenticate_admin(DEFAULT_ADMIN_PIN) + .unwrap() + .device(); + let mut device = device.authenticate_admin(ADMIN_NEW_PASSWORD).unwrap_err().0; assert_ok!( (), - device.change_admin_pin(ADMIN_PASSWORD, ADMIN_NEW_PASSWORD) + device.change_admin_pin(DEFAULT_ADMIN_PIN, ADMIN_NEW_PASSWORD) ); - let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap_err().0; - let device = device + let device = device.authenticate_admin(DEFAULT_ADMIN_PIN).unwrap_err().0; + let mut device = device .authenticate_admin(ADMIN_NEW_PASSWORD) .unwrap() .device(); assert_cmd_err!( CommandError::WrongPassword, - device.change_admin_pin(ADMIN_PASSWORD, ADMIN_PASSWORD) + device.change_admin_pin(DEFAULT_ADMIN_PIN, DEFAULT_ADMIN_PIN) ); assert_ok!( (), - device.change_admin_pin(ADMIN_NEW_PASSWORD, ADMIN_PASSWORD) + device.change_admin_pin(ADMIN_NEW_PASSWORD, DEFAULT_ADMIN_PIN) ); - let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap().device(); + let device = device + .authenticate_admin(DEFAULT_ADMIN_PIN) + .unwrap() + .device(); device.authenticate_admin(ADMIN_NEW_PASSWORD).unwrap_err(); } @@ -222,63 +233,85 @@ where #[test_device] fn unlock_user_pin(device: DeviceWrapper) { - let device = device.authenticate_user(USER_PASSWORD).unwrap().device(); - assert_ok!((), device.unlock_user_pin(ADMIN_PASSWORD, USER_PASSWORD)); + let mut device = device.authenticate_user(DEFAULT_USER_PIN).unwrap().device(); + assert_ok!( + (), + device.unlock_user_pin(DEFAULT_ADMIN_PIN, DEFAULT_USER_PIN) + ); assert_cmd_err!( CommandError::WrongPassword, - device.unlock_user_pin(USER_PASSWORD, USER_PASSWORD) + device.unlock_user_pin(DEFAULT_USER_PIN, DEFAULT_USER_PIN) ); // block user PIN - let wrong_password = USER_PASSWORD.to_owned() + "foo"; + let wrong_password = DEFAULT_USER_PIN.to_owned() + "foo"; let device = require_failed_user_login(device, &wrong_password, CommandError::WrongPassword); let device = require_failed_user_login(device, &wrong_password, CommandError::WrongPassword); let device = require_failed_user_login(device, &wrong_password, CommandError::WrongPassword); - let device = require_failed_user_login(device, USER_PASSWORD, CommandError::WrongPassword); + let mut device = + require_failed_user_login(device, DEFAULT_USER_PIN, CommandError::WrongPassword); // unblock with current PIN assert_cmd_err!( CommandError::WrongPassword, - device.unlock_user_pin(USER_PASSWORD, USER_PASSWORD) + device.unlock_user_pin(DEFAULT_USER_PIN, DEFAULT_USER_PIN) ); - assert_ok!((), device.unlock_user_pin(ADMIN_PASSWORD, USER_PASSWORD)); - let device = device.authenticate_user(USER_PASSWORD).unwrap().device(); + assert_ok!( + (), + device.unlock_user_pin(DEFAULT_ADMIN_PIN, DEFAULT_USER_PIN) + ); + let device = device.authenticate_user(DEFAULT_USER_PIN).unwrap().device(); // block user PIN let device = require_failed_user_login(device, &wrong_password, CommandError::WrongPassword); let device = require_failed_user_login(device, &wrong_password, CommandError::WrongPassword); let device = require_failed_user_login(device, &wrong_password, CommandError::WrongPassword); - let device = require_failed_user_login(device, USER_PASSWORD, CommandError::WrongPassword); + let mut device = + require_failed_user_login(device, DEFAULT_USER_PIN, CommandError::WrongPassword); // unblock with new PIN assert_cmd_err!( CommandError::WrongPassword, - device.unlock_user_pin(USER_PASSWORD, USER_PASSWORD) + device.unlock_user_pin(DEFAULT_USER_PIN, DEFAULT_USER_PIN) ); assert_ok!( (), - device.unlock_user_pin(ADMIN_PASSWORD, USER_NEW_PASSWORD) + device.unlock_user_pin(DEFAULT_ADMIN_PIN, USER_NEW_PASSWORD) ); // reset user PIN - assert_ok!((), device.change_user_pin(USER_NEW_PASSWORD, USER_PASSWORD)); + assert_ok!( + (), + device.change_user_pin(USER_NEW_PASSWORD, DEFAULT_USER_PIN) + ); +} + +fn assert_utf8_err_or_ne(left: &str, right: Result<String, Error>) { + match right { + Ok(s) => assert_ne!(left.to_string(), s), + Err(Error::Utf8Error(_)) => {} + Err(err) => panic!("Expected Utf8Error, got {}!", err), + } } #[test_device] fn factory_reset(device: DeviceWrapper) { - let admin = device.authenticate_admin(ADMIN_PASSWORD).unwrap(); + let mut admin = unwrap_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN)); let otp_data = OtpSlotData::new(1, "test", "0123468790", OtpMode::SixDigits); assert_ok!((), admin.write_totp_slot(otp_data, 30)); - let device = admin.device(); - let pws = device.get_password_safe(USER_PASSWORD).unwrap(); + let mut device = admin.device(); + let mut pws = unwrap_ok!(device.get_password_safe(DEFAULT_USER_PIN)); assert_ok!((), pws.write_slot(0, "test", "testlogin", "testpw")); drop(pws); - assert_ok!((), device.change_user_pin(USER_PASSWORD, USER_NEW_PASSWORD)); assert_ok!( (), - device.change_admin_pin(ADMIN_PASSWORD, ADMIN_NEW_PASSWORD) + device.change_user_pin(DEFAULT_USER_PIN, USER_NEW_PASSWORD) + ); + assert_ok!( + (), + device.change_admin_pin(DEFAULT_ADMIN_PIN, ADMIN_NEW_PASSWORD) ); assert_cmd_err!( @@ -287,46 +320,55 @@ fn factory_reset(device: DeviceWrapper) { ); assert_cmd_err!( CommandError::WrongPassword, - device.factory_reset(ADMIN_PASSWORD) + device.factory_reset(DEFAULT_ADMIN_PIN) ); assert_ok!((), device.factory_reset(ADMIN_NEW_PASSWORD)); - let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap().device(); + let device = device + .authenticate_admin(DEFAULT_ADMIN_PIN) + .unwrap() + .device(); - let user = device.authenticate_user(USER_PASSWORD).unwrap(); + let user = unwrap_ok!(device.authenticate_user(DEFAULT_USER_PIN)); assert_cmd_err!(CommandError::SlotNotProgrammed, user.get_totp_slot_name(1)); - let device = user.device(); - let pws = device.get_password_safe(USER_PASSWORD).unwrap(); - assert_ne!("test".to_string(), pws.get_slot_name(0).unwrap()); - assert_ne!("testlogin".to_string(), pws.get_slot_login(0).unwrap()); - assert_ne!("testpw".to_string(), pws.get_slot_password(0).unwrap()); + let mut device = user.device(); + let pws = unwrap_ok!(device.get_password_safe(DEFAULT_USER_PIN)); + assert_utf8_err_or_ne("test", pws.get_slot_name(0)); + assert_utf8_err_or_ne("testlogin", pws.get_slot_login(0)); + assert_utf8_err_or_ne("testpw", pws.get_slot_password(0)); + drop(pws); - assert_ok!((), device.build_aes_key(ADMIN_PASSWORD)); + assert_ok!((), device.build_aes_key(DEFAULT_ADMIN_PIN)); } #[test_device] fn build_aes_key(device: DeviceWrapper) { - let pws = device.get_password_safe(USER_PASSWORD).unwrap(); + let mut device = device; + let mut pws = unwrap_ok!(device.get_password_safe(DEFAULT_USER_PIN)); assert_ok!((), pws.write_slot(0, "test", "testlogin", "testpw")); drop(pws); assert_cmd_err!( CommandError::WrongPassword, - device.build_aes_key(USER_PASSWORD) + device.build_aes_key(DEFAULT_USER_PIN) ); - assert_ok!((), device.build_aes_key(ADMIN_PASSWORD)); + assert_ok!((), device.build_aes_key(DEFAULT_ADMIN_PIN)); - let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap().device(); + let mut device = device + .authenticate_admin(DEFAULT_ADMIN_PIN) + .unwrap() + .device(); - let pws = device.get_password_safe(USER_PASSWORD).unwrap(); - assert_ne!("test".to_string(), pws.get_slot_name(0).unwrap()); - assert_ne!("testlogin".to_string(), pws.get_slot_login(0).unwrap()); - assert_ne!("testpw".to_string(), pws.get_slot_password(0).unwrap()); + let pws = unwrap_ok!(device.get_password_safe(DEFAULT_USER_PIN)); + assert_utf8_err_or_ne("test", pws.get_slot_name(0)); + assert_utf8_err_or_ne("testlogin", pws.get_slot_login(0)); + assert_utf8_err_or_ne("testpw", pws.get_slot_password(0)); } #[test_device] fn change_update_pin(device: Storage) { + let mut device = device; assert_cmd_err!( CommandError::WrongPassword, device.change_update_pin(UPDATE_NEW_PIN, UPDATE_PIN) @@ -337,6 +379,7 @@ fn change_update_pin(device: Storage) { #[test_device] fn encrypted_volume(device: Storage) { + let mut device = device; assert_ok!((), device.lock()); assert_eq!(1, count_nitrokey_block_devices()); @@ -347,7 +390,7 @@ fn encrypted_volume(device: Storage) { device.enable_encrypted_volume("123") ); assert_eq!(1, count_nitrokey_block_devices()); - assert_ok!((), device.enable_encrypted_volume(USER_PASSWORD)); + assert_ok!((), device.enable_encrypted_volume(DEFAULT_USER_PIN)); assert_eq!(2, count_nitrokey_block_devices()); assert_ok!((), device.disable_encrypted_volume()); assert_eq!(1, count_nitrokey_block_devices()); @@ -355,13 +398,14 @@ fn encrypted_volume(device: Storage) { #[test_device] fn hidden_volume(device: Storage) { + let mut device = device; assert_ok!((), device.lock()); assert_eq!(1, count_nitrokey_block_devices()); assert_ok!((), device.disable_hidden_volume()); assert_eq!(1, count_nitrokey_block_devices()); - assert_ok!((), device.enable_encrypted_volume(USER_PASSWORD)); + assert_ok!((), device.enable_encrypted_volume(DEFAULT_USER_PIN)); assert_eq!(2, count_nitrokey_block_devices()); // TODO: why this error code? @@ -390,51 +434,85 @@ fn hidden_volume(device: Storage) { #[test_device] fn lock(device: Storage) { - assert_ok!((), device.enable_encrypted_volume(USER_PASSWORD)); + let mut device = device; + assert_ok!((), device.enable_encrypted_volume(DEFAULT_USER_PIN)); assert_ok!((), device.lock()); assert_eq!(1, count_nitrokey_block_devices()); } #[test_device] +fn set_encrypted_volume_mode(device: Storage) { + // This test case does not check the device status as the command only works with firmware + // version 0.49. For later versions, it does not do anything and always returns Ok(()). + let mut device = device; + + assert_ok!( + (), + device.set_encrypted_volume_mode(DEFAULT_ADMIN_PIN, VolumeMode::ReadOnly) + ); + + // TODO: re-enable once the password is checked in the firmware + // assert_cmd_err!( + // CommandError::WrongPassword, + // device.set_encrypted_volume_mode(DEFAULT_USER_PIN, VolumeMode::ReadOnly) + // ); + + assert_ok!( + (), + device.set_encrypted_volume_mode(DEFAULT_ADMIN_PIN, VolumeMode::ReadOnly) + ); + assert_ok!( + (), + device.set_encrypted_volume_mode(DEFAULT_ADMIN_PIN, VolumeMode::ReadWrite) + ); + assert_ok!( + (), + device.set_encrypted_volume_mode(DEFAULT_ADMIN_PIN, VolumeMode::ReadOnly) + ); +} + +#[test_device] fn set_unencrypted_volume_mode(device: Storage) { fn assert_mode(device: &Storage, mode: VolumeMode) { - let status = device.get_status(); - assert!(status.is_ok()); + let status = unwrap_ok!(device.get_status()); assert_eq!( - status.unwrap().unencrypted_volume.read_only, + status.unencrypted_volume.read_only, mode == VolumeMode::ReadOnly ); } - fn assert_success(device: &Storage, mode: VolumeMode) { - assert_ok!((), device.set_unencrypted_volume_mode(ADMIN_PASSWORD, mode)); + fn assert_success(device: &mut Storage, mode: VolumeMode) { + assert_ok!( + (), + device.set_unencrypted_volume_mode(DEFAULT_ADMIN_PIN, mode) + ); assert_mode(&device, mode); } - assert_success(&device, VolumeMode::ReadOnly); + let mut device = device; + assert_success(&mut device, VolumeMode::ReadOnly); assert_cmd_err!( CommandError::WrongPassword, - device.set_unencrypted_volume_mode(USER_PASSWORD, VolumeMode::ReadOnly) + device.set_unencrypted_volume_mode(DEFAULT_USER_PIN, VolumeMode::ReadOnly) ); assert_mode(&device, VolumeMode::ReadOnly); - assert_success(&device, VolumeMode::ReadWrite); - assert_success(&device, VolumeMode::ReadWrite); - assert_success(&device, VolumeMode::ReadOnly); + assert_success(&mut device, VolumeMode::ReadWrite); + assert_success(&mut device, VolumeMode::ReadWrite); + assert_success(&mut device, VolumeMode::ReadOnly); } #[test_device] fn get_storage_status(device: Storage) { - let status = device.get_status().unwrap(); - + let status = unwrap_ok!(device.get_status()); assert!(status.serial_number_sd_card > 0); assert!(status.serial_number_smart_card > 0); } #[test_device] fn get_production_info(device: Storage) { - let info = device.get_production_info().unwrap(); + let info = unwrap_ok!(device.get_production_info()); assert_eq!(0, info.firmware_version.major); assert!(info.firmware_version.minor != 0); assert!(info.serial_number_cpu != 0); @@ -447,43 +525,45 @@ fn get_production_info(device: Storage) { assert!(info.sd_card.oem != 0); assert!(info.sd_card.manufacturer != 0); - let status = device.get_status().unwrap(); + let status = unwrap_ok!(device.get_status()); assert_eq!(status.firmware_version, info.firmware_version); assert_eq!(status.serial_number_sd_card, info.sd_card.serial_number); } #[test_device] fn clear_new_sd_card_warning(device: Storage) { - assert_ok!((), device.factory_reset(ADMIN_PASSWORD)); + let mut device = device; + assert_ok!((), device.factory_reset(DEFAULT_ADMIN_PIN)); thread::sleep(time::Duration::from_secs(3)); - assert_ok!((), device.build_aes_key(ADMIN_PASSWORD)); + assert_ok!((), device.build_aes_key(DEFAULT_ADMIN_PIN)); // We have to perform an SD card operation to reset the new_sd_card_found field assert_ok!((), device.lock()); - let status = device.get_status().unwrap(); + let status = unwrap_ok!(device.get_status()); assert!(status.new_sd_card_found); - assert_ok!((), device.clear_new_sd_card_warning(ADMIN_PASSWORD)); + assert_ok!((), device.clear_new_sd_card_warning(DEFAULT_ADMIN_PIN)); - let status = device.get_status().unwrap(); + let status = unwrap_ok!(device.get_status()); assert!(!status.new_sd_card_found); } #[test_device] fn export_firmware(device: Storage) { + let mut device = device; assert_cmd_err!( CommandError::WrongPassword, device.export_firmware("someadminpn") ); - assert_ok!((), device.export_firmware(ADMIN_PASSWORD)); + assert_ok!((), device.export_firmware(DEFAULT_ADMIN_PIN)); assert_ok!( (), - device.set_unencrypted_volume_mode(ADMIN_PASSWORD, VolumeMode::ReadWrite) + device.set_unencrypted_volume_mode(DEFAULT_ADMIN_PIN, VolumeMode::ReadWrite) ); - assert_ok!((), device.export_firmware(ADMIN_PASSWORD)); + assert_ok!((), device.export_firmware(DEFAULT_ADMIN_PIN)); assert_ok!( (), - device.set_unencrypted_volume_mode(ADMIN_PASSWORD, VolumeMode::ReadOnly) + device.set_unencrypted_volume_mode(DEFAULT_ADMIN_PIN, VolumeMode::ReadOnly) ); } diff --git a/nitrokey/tests/lib.rs b/nitrokey/tests/lib.rs index 697024d..8ab75f6 100644 --- a/nitrokey/tests/lib.rs +++ b/nitrokey/tests/lib.rs @@ -1,9 +1,11 @@ // Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org> // SPDX-License-Identifier: MIT +mod util; + #[test] fn get_library_version() { - let version = nitrokey::get_library_version().unwrap(); + let version = unwrap_ok!(nitrokey::get_library_version()); assert!(version.git.is_empty() || version.git.starts_with("v")); assert!(version.major > 0); diff --git a/nitrokey/tests/otp.rs b/nitrokey/tests/otp.rs index e424673..c0bbecf 100644 --- a/nitrokey/tests/otp.rs +++ b/nitrokey/tests/otp.rs @@ -4,16 +4,14 @@ mod util; use std::fmt::Debug; -use std::ops::Deref; +use std::ops::DerefMut; use nitrokey::{ Admin, Authenticate, CommandError, Config, ConfigureOtp, Device, GenerateOtp, LibraryError, - OtpMode, OtpSlotData, + OtpMode, OtpSlotData, DEFAULT_ADMIN_PIN, DEFAULT_USER_PIN, }; use nitrokey_test::test as test_device; -use crate::util::{ADMIN_PASSWORD, USER_PASSWORD}; - // test suite according to RFC 4226, Appendix D static HOTP_SECRET: &str = "3132333435363738393031323334353637383930"; static HOTP_CODES: &[&str] = &[ @@ -43,27 +41,25 @@ where T: Device, (T, nitrokey::Error): Debug, { - device - .authenticate_admin(ADMIN_PASSWORD) - .expect("Could not login as admin.") + unwrap_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN)) } -fn configure_hotp(admin: &ConfigureOtp, counter: u8) { +fn configure_hotp(admin: &mut ConfigureOtp, counter: u8) { let slot_data = OtpSlotData::new(1, "test-hotp", HOTP_SECRET, OtpMode::SixDigits); assert_ok!((), admin.write_hotp_slot(slot_data, counter.into())); } -fn check_hotp_codes(device: &GenerateOtp, offset: u8) { +fn check_hotp_codes(device: &mut GenerateOtp, offset: u8) { HOTP_CODES.iter().enumerate().for_each(|(i, code)| { if i >= offset as usize { - let result = device.get_hotp_code(1); - assert_eq!(code, &result.unwrap()); + assert_ok!(code.to_string(), device.get_hotp_code(1)); } }); } #[test_device] fn set_time(device: DeviceWrapper) { + let mut device = device; assert_ok!((), device.set_time(1546385382, true)); assert_ok!((), device.set_time(1546385392, false)); assert_cmd_err!(CommandError::Timestamp, device.set_time(1546385292, false)); @@ -72,49 +68,47 @@ fn set_time(device: DeviceWrapper) { #[test_device] fn hotp_no_pin(device: DeviceWrapper) { - let admin = make_admin_test_device(device); + let mut admin = make_admin_test_device(device); let config = Config::new(None, None, None, false); assert_ok!((), admin.write_config(config)); - configure_hotp(&admin, 0); - check_hotp_codes(admin.deref(), 0); + configure_hotp(&mut admin, 0); + check_hotp_codes(admin.deref_mut(), 0); - configure_hotp(&admin, 5); - check_hotp_codes(admin.deref(), 5); + configure_hotp(&mut admin, 5); + check_hotp_codes(admin.deref_mut(), 5); - configure_hotp(&admin, 0); - check_hotp_codes(&admin.device(), 0); + configure_hotp(&mut admin, 0); + check_hotp_codes(&mut admin.device(), 0); } #[test_device] fn hotp_pin(device: DeviceWrapper) { - let admin = make_admin_test_device(device); + let mut admin = make_admin_test_device(device); let config = Config::new(None, None, None, true); assert_ok!((), admin.write_config(config)); - configure_hotp(&admin, 0); - let user = admin.device().authenticate_user(USER_PASSWORD).unwrap(); - check_hotp_codes(&user, 0); + configure_hotp(&mut admin, 0); + let mut user = unwrap_ok!(admin.device().authenticate_user(DEFAULT_USER_PIN)); + check_hotp_codes(&mut user, 0); assert_cmd_err!(CommandError::NotAuthorized, user.device().get_hotp_code(1)); } #[test_device] fn hotp_slot_name(device: DeviceWrapper) { - let admin = make_admin_test_device(device); + let mut admin = make_admin_test_device(device); let slot_data = OtpSlotData::new(1, "test-hotp", HOTP_SECRET, OtpMode::SixDigits); assert_ok!((), admin.write_hotp_slot(slot_data, 0)); let device = admin.device(); - let result = device.get_hotp_slot_name(1); - assert_eq!("test-hotp", result.unwrap()); - let result = device.get_hotp_slot_name(4); - assert_lib_err!(LibraryError::InvalidSlot, result); + assert_ok!("test-hotp".to_string(), device.get_hotp_slot_name(1)); + assert_lib_err!(LibraryError::InvalidSlot, device.get_hotp_slot_name(4)); } #[test_device] fn hotp_error(device: DeviceWrapper) { - let admin = make_admin_test_device(device); + let mut admin = make_admin_test_device(device); let slot_data = OtpSlotData::new(1, "", HOTP_SECRET, OtpMode::SixDigits); assert_cmd_err!(CommandError::NoName, admin.write_hotp_slot(slot_data, 0)); let slot_data = OtpSlotData::new(4, "test", HOTP_SECRET, OtpMode::SixDigits); @@ -133,7 +127,7 @@ fn hotp_error(device: DeviceWrapper) { #[test_device] fn hotp_erase(device: DeviceWrapper) { - let admin = make_admin_test_device(device); + let mut admin = make_admin_test_device(device); let config = Config::new(None, None, None, false); assert_ok!((), admin.write_config(config)); let slot_data = OtpSlotData::new(1, "test1", HOTP_SECRET, OtpMode::SixDigits); @@ -143,22 +137,22 @@ fn hotp_erase(device: DeviceWrapper) { assert_ok!((), admin.erase_hotp_slot(1)); - let device = admin.device(); + let mut device = admin.device(); let result = device.get_hotp_slot_name(1); assert_cmd_err!(CommandError::SlotNotProgrammed, result); let result = device.get_hotp_code(1); assert_cmd_err!(CommandError::SlotNotProgrammed, result); - assert_eq!("test2", device.get_hotp_slot_name(2).unwrap()); + assert_ok!("test2".to_string(), device.get_hotp_slot_name(2)); } -fn configure_totp(admin: &ConfigureOtp, factor: u64) { +fn configure_totp(admin: &mut ConfigureOtp, factor: u64) { let slot_data = OtpSlotData::new(1, "test-totp", TOTP_SECRET, OtpMode::EightDigits); let time_window = 30u64.checked_mul(factor).unwrap(); assert_ok!((), admin.write_totp_slot(slot_data, time_window as u16)); } -fn check_totp_codes(device: &GenerateOtp, factor: u64, timestamp_size: TotpTimestampSize) { +fn check_totp_codes(device: &mut GenerateOtp, factor: u64, timestamp_size: TotpTimestampSize) { for (base_time, codes) in TOTP_CODES { let time = base_time.checked_mul(factor).unwrap(); let is_u64 = time > u32::max_value() as u64; @@ -167,7 +161,7 @@ fn check_totp_codes(device: &GenerateOtp, factor: u64, timestamp_size: TotpTimes } assert_ok!((), device.set_time(time, true)); - let code = device.get_totp_code(1).unwrap(); + let code = unwrap_ok!(device.get_totp_code(1)); assert!( code.contains(&code), "Generated TOTP code {} for {}, but expected one of {}", @@ -180,49 +174,47 @@ fn check_totp_codes(device: &GenerateOtp, factor: u64, timestamp_size: TotpTimes #[test_device] fn totp_no_pin(device: DeviceWrapper) { - // TODO: this test may fail due to bad timing --> find solution - let admin = make_admin_test_device(device); + let mut admin = make_admin_test_device(device); let config = Config::new(None, None, None, false); assert_ok!((), admin.write_config(config)); - configure_totp(&admin, 1); - check_totp_codes(admin.deref(), 1, TotpTimestampSize::U32); + configure_totp(&mut admin, 1); + check_totp_codes(admin.deref_mut(), 1, TotpTimestampSize::U32); - configure_totp(&admin, 2); - check_totp_codes(admin.deref(), 2, TotpTimestampSize::U32); + configure_totp(&mut admin, 2); + check_totp_codes(admin.deref_mut(), 2, TotpTimestampSize::U32); - configure_totp(&admin, 1); - check_totp_codes(&admin.device(), 1, TotpTimestampSize::U32); + configure_totp(&mut admin, 1); + check_totp_codes(&mut admin.device(), 1, TotpTimestampSize::U32); } #[test_device] // Nitrokey Storage does only support timestamps that fit in a 32-bit // unsigned integer, so don't test with it. fn totp_no_pin_64(device: Pro) { - let admin = make_admin_test_device(device); + let mut admin = make_admin_test_device(device); let config = Config::new(None, None, None, false); assert_ok!((), admin.write_config(config)); - configure_totp(&admin, 1); - check_totp_codes(admin.deref(), 1, TotpTimestampSize::U64); + configure_totp(&mut admin, 1); + check_totp_codes(admin.deref_mut(), 1, TotpTimestampSize::U64); - configure_totp(&admin, 2); - check_totp_codes(admin.deref(), 2, TotpTimestampSize::U64); + configure_totp(&mut admin, 2); + check_totp_codes(admin.deref_mut(), 2, TotpTimestampSize::U64); - configure_totp(&admin, 1); - check_totp_codes(&admin.device(), 1, TotpTimestampSize::U64); + configure_totp(&mut admin, 1); + check_totp_codes(&mut admin.device(), 1, TotpTimestampSize::U64); } #[test_device] fn totp_pin(device: DeviceWrapper) { - // TODO: this test may fail due to bad timing --> find solution - let admin = make_admin_test_device(device); + let mut admin = make_admin_test_device(device); let config = Config::new(None, None, None, true); assert_ok!((), admin.write_config(config)); - configure_totp(&admin, 1); - let user = admin.device().authenticate_user(USER_PASSWORD).unwrap(); - check_totp_codes(&user, 1, TotpTimestampSize::U32); + configure_totp(&mut admin, 1); + let mut user = unwrap_ok!(admin.device().authenticate_user(DEFAULT_USER_PIN)); + check_totp_codes(&mut user, 1, TotpTimestampSize::U32); assert_cmd_err!(CommandError::NotAuthorized, user.device().get_totp_code(1)); } @@ -230,20 +222,20 @@ fn totp_pin(device: DeviceWrapper) { #[test_device] // See comment for totp_no_pin_64. fn totp_pin_64(device: Pro) { - let admin = make_admin_test_device(device); + let mut admin = make_admin_test_device(device); let config = Config::new(None, None, None, true); assert_ok!((), admin.write_config(config)); - configure_totp(&admin, 1); - let user = admin.device().authenticate_user(USER_PASSWORD).unwrap(); - check_totp_codes(&user, 1, TotpTimestampSize::U64); + configure_totp(&mut admin, 1); + let mut user = unwrap_ok!(admin.device().authenticate_user(DEFAULT_USER_PIN)); + check_totp_codes(&mut user, 1, TotpTimestampSize::U64); assert_cmd_err!(CommandError::NotAuthorized, user.device().get_totp_code(1)); } #[test_device] fn totp_slot_name(device: DeviceWrapper) { - let admin = make_admin_test_device(device); + let mut admin = make_admin_test_device(device); let slot_data = OtpSlotData::new(1, "test-totp", TOTP_SECRET, OtpMode::EightDigits); assert_ok!((), admin.write_totp_slot(slot_data, 0)); @@ -256,7 +248,7 @@ fn totp_slot_name(device: DeviceWrapper) { #[test_device] fn totp_error(device: DeviceWrapper) { - let admin = make_admin_test_device(device); + let mut admin = make_admin_test_device(device); let slot_data = OtpSlotData::new(1, "", TOTP_SECRET, OtpMode::SixDigits); assert_cmd_err!(CommandError::NoName, admin.write_totp_slot(slot_data, 0)); let slot_data = OtpSlotData::new(20, "test", TOTP_SECRET, OtpMode::SixDigits); @@ -275,7 +267,7 @@ fn totp_error(device: DeviceWrapper) { #[test_device] fn totp_erase(device: DeviceWrapper) { - let admin = make_admin_test_device(device); + let mut admin = make_admin_test_device(device); let config = Config::new(None, None, None, false); assert_ok!((), admin.write_config(config)); let slot_data = OtpSlotData::new(1, "test1", TOTP_SECRET, OtpMode::SixDigits); @@ -291,5 +283,5 @@ fn totp_erase(device: DeviceWrapper) { let result = device.get_totp_code(1); assert_cmd_err!(CommandError::SlotNotProgrammed, result); - assert_eq!("test2", device.get_totp_slot_name(2).unwrap()); + assert_ok!("test2".to_string(), device.get_totp_slot_name(2)); } diff --git a/nitrokey/tests/pws.rs b/nitrokey/tests/pws.rs index df99e1c..b0e5abe 100644 --- a/nitrokey/tests/pws.rs +++ b/nitrokey/tests/pws.rs @@ -7,13 +7,12 @@ use std::ffi::CStr; use libc::{c_int, c_void, free}; use nitrokey::{ - CommandError, Device, Error, GetPasswordSafe, LibraryError, PasswordSafe, SLOT_COUNT, + CommandError, Device, Error, GetPasswordSafe, LibraryError, PasswordSafe, DEFAULT_ADMIN_PIN, + DEFAULT_USER_PIN, SLOT_COUNT, }; use nitrokey_sys; use nitrokey_test::test as test_device; -use crate::util::{ADMIN_PASSWORD, USER_PASSWORD}; - fn get_slot_name_direct(slot: u8) -> Result<String, Error> { let ptr = unsafe { nitrokey_sys::NK_get_password_safe_slot_name(slot) }; if ptr.is_null() { @@ -33,33 +32,35 @@ fn get_slot_name_direct(slot: u8) -> Result<String, Error> { } } -fn get_pws<T>(device: &T) -> PasswordSafe +fn get_pws<T>(device: &mut T) -> PasswordSafe where T: Device, { - device.get_password_safe(USER_PASSWORD).unwrap() + unwrap_ok!(device.get_password_safe(DEFAULT_USER_PIN)) } #[test_device] fn enable(device: DeviceWrapper) { + let mut device = device; assert_cmd_err!( CommandError::WrongPassword, - device.get_password_safe(&(USER_PASSWORD.to_owned() + "123")) + device.get_password_safe(&(DEFAULT_USER_PIN.to_owned() + "123")) ); - assert!(device.get_password_safe(USER_PASSWORD).is_ok()); + assert_any_ok!(device.get_password_safe(DEFAULT_USER_PIN)); assert_cmd_err!( CommandError::WrongPassword, - device.get_password_safe(ADMIN_PASSWORD) + device.get_password_safe(DEFAULT_ADMIN_PIN) ); - assert!(device.get_password_safe(USER_PASSWORD).is_ok()); + assert_any_ok!(device.get_password_safe(DEFAULT_USER_PIN)); } #[test_device] fn drop(device: DeviceWrapper) { + let mut device = device; { - let pws = get_pws(&device); + let mut pws = get_pws(&mut device); assert_ok!((), pws.write_slot(1, "name", "login", "password")); - assert_eq!("name", pws.get_slot_name(1).unwrap()); + assert_ok!("name".to_string(), pws.get_slot_name(1)); let result = get_slot_name_direct(1); assert_ok!(String::from("name"), result); } @@ -72,15 +73,16 @@ fn drop(device: DeviceWrapper) { #[test_device] fn get_status(device: DeviceWrapper) { - let pws = get_pws(&device); + let mut device = device; + let mut pws = get_pws(&mut device); for i in 0..SLOT_COUNT { assert_ok!((), pws.erase_slot(i)); } - let status = pws.get_slot_status().unwrap(); + let status = unwrap_ok!(pws.get_slot_status()); assert_eq!(status, [false; SLOT_COUNT as usize]); assert_ok!((), pws.write_slot(1, "name", "login", "password")); - let status = pws.get_slot_status().unwrap(); + let status = unwrap_ok!(pws.get_slot_status()); for i in 0..SLOT_COUNT { assert_eq!(i == 1, status[i as usize]); } @@ -88,17 +90,17 @@ fn get_status(device: DeviceWrapper) { for i in 0..SLOT_COUNT { assert_ok!((), pws.write_slot(i, "name", "login", "password")); } - let status = pws.get_slot_status().unwrap(); - assert_eq!(status, [true; SLOT_COUNT as usize]); + assert_ok!([true; SLOT_COUNT as usize], pws.get_slot_status()); } #[test_device] fn get_data(device: DeviceWrapper) { - let pws = get_pws(&device); + let mut device = device; + let mut pws = get_pws(&mut device); assert_ok!((), pws.write_slot(1, "name", "login", "password")); - assert_eq!("name", pws.get_slot_name(1).unwrap()); - assert_eq!("login", pws.get_slot_login(1).unwrap()); - assert_eq!("password", pws.get_slot_password(1).unwrap()); + assert_ok!("name".to_string(), pws.get_slot_name(1)); + assert_ok!("login".to_string(), pws.get_slot_login(1)); + assert_ok!("password".to_string(), pws.get_slot_password(1)); assert_ok!((), pws.erase_slot(1)); assert_cmd_err!(CommandError::SlotNotProgrammed, pws.get_slot_name(1)); @@ -109,9 +111,9 @@ fn get_data(device: DeviceWrapper) { let login = "pär@test.com"; let password = "'i3lJc[09?I:,[u7dWz9"; assert_ok!((), pws.write_slot(1, name, login, password)); - assert_eq!(name, pws.get_slot_name(1).unwrap()); - assert_eq!(login, pws.get_slot_login(1).unwrap()); - assert_eq!(password, pws.get_slot_password(1).unwrap()); + assert_ok!(name.to_string(), pws.get_slot_name(1)); + assert_ok!(login.to_string(), pws.get_slot_login(1)); + assert_ok!(password.to_string(), pws.get_slot_password(1)); assert_lib_err!(LibraryError::InvalidSlot, pws.get_slot_name(SLOT_COUNT)); assert_lib_err!(LibraryError::InvalidSlot, pws.get_slot_login(SLOT_COUNT)); @@ -120,7 +122,8 @@ fn get_data(device: DeviceWrapper) { #[test_device] fn write(device: DeviceWrapper) { - let pws = get_pws(&device); + let mut device = device; + let mut pws = get_pws(&mut device); assert_lib_err!( LibraryError::InvalidSlot, @@ -145,7 +148,8 @@ fn write(device: DeviceWrapper) { #[test_device] fn erase(device: DeviceWrapper) { - let pws = get_pws(&device); + let mut device = device; + let mut pws = get_pws(&mut device); assert_lib_err!(LibraryError::InvalidSlot, pws.erase_slot(SLOT_COUNT)); assert_ok!((), pws.write_slot(0, "name", "login", "password")); diff --git a/nitrokey/tests/util/mod.rs b/nitrokey/tests/util/mod.rs index 49ec13e..f2b20ec 100644 --- a/nitrokey/tests/util/mod.rs +++ b/nitrokey/tests/util/mod.rs @@ -1,8 +1,35 @@ // Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org> // SPDX-License-Identifier: MIT -pub static ADMIN_PASSWORD: &str = "12345678"; -pub static USER_PASSWORD: &str = "123456"; +#[macro_export] +macro_rules! unwrap_ok { + ($val:expr) => {{ + match $val { + Ok(val) => val, + Err(err) => panic!( + r#"assertion failed: `(left == right)` + left: `Ok(_)`, + right: `Err({:?})`"#, + err + ), + } + }}; +} + +#[macro_export] +macro_rules! assert_any_ok { + ($val:expr) => {{ + match &$val { + Ok(_) => {} + Err(err) => panic!( + r#"assertion failed: `(left == right)` + left: `Ok(_)`, + right: `Err({:?})`"#, + err + ), + } + }}; +} #[macro_export] macro_rules! assert_ok { |