From d18cb04ff4d201fe4532cedd22b9753e08385a7f Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Wed, 16 Jan 2019 23:08:56 +0000 Subject: Introduce the FirmwareVersion struct The FirmwareVersion struct stores the major and minor firmware version of a Nitrokey device. We refactor the StorageProductionInfo and StorageStatus structs to use this new struct. --- tests/device.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/tests/device.rs b/tests/device.rs index 849d2ff..abede67 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -441,8 +441,8 @@ fn get_storage_status(device: Storage) { #[test_device] fn get_production_info(device: Storage) { let info = device.get_production_info().unwrap(); - assert_eq!(0, info.firmware_version_major); - assert!(info.firmware_version_minor != 0); + assert_eq!(0, info.firmware_version.major); + assert!(info.firmware_version.minor != 0); assert!(info.serial_number_cpu != 0); assert!(info.sd_card.serial_number != 0); assert!(info.sd_card.size > 0); @@ -454,8 +454,7 @@ fn get_production_info(device: Storage) { assert!(info.sd_card.manufacturer != 0); let status = device.get_status().unwrap(); - assert_eq!(status.firmware_version_major, info.firmware_version_major); - assert_eq!(status.firmware_version_minor, info.firmware_version_minor); + assert_eq!(status.firmware_version, info.firmware_version); assert_eq!(status.serial_number_sd_card, info.sd_card.serial_number); } -- cgit v1.2.3 From c43b63b70ee32f9fa8e980d89eff5383931f5c39 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 17 Jan 2019 03:49:13 +0000 Subject: Add assert_cmd_err and assert_ok macros to tests These macros allow easier comparisions using the new error type. This patch fixes all tests and updates nitrokey-test to 0.2.0 so that it integrates with the new error structure. Some tests may still fail until CommunicationError::NotConnected is actually returned. --- Cargo.toml | 2 +- tests/device.rs | 157 +++++++++++++++++++++++++----------------------------- tests/otp.rs | 93 +++++++++++++++----------------- tests/pws.rs | 99 +++++++++++++++------------------- tests/util/mod.rs | 67 +++++++++++++++++++++++ 5 files changed, 226 insertions(+), 192 deletions(-) (limited to 'tests') diff --git a/Cargo.toml b/Cargo.toml index 99ddd0c..cfe1579 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,4 +19,4 @@ rand_core = {version = "0.3", default-features = false} rand_os = {version = "0.1"} [dev-dependencies] -nitrokey-test = {version = "0.1"} +nitrokey-test = {version = "0.2"} diff --git a/tests/device.rs b/tests/device.rs index abede67..174624f 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -5,7 +5,7 @@ use std::process::Command; use std::{thread, time}; use nitrokey::{ - Authenticate, CommandError, Config, ConfigureOtp, Device, GenerateOtp, GetPasswordSafe, + Authenticate, CommandError, Config, ConfigureOtp, Device, Error, GenerateOtp, GetPasswordSafe, OtpMode, OtpSlotData, Storage, VolumeMode, }; use nitrokey_test::test as test_device; @@ -125,20 +125,20 @@ fn get_retry_count(device: DeviceWrapper) { fn config(device: DeviceWrapper) { let admin = device.authenticate_admin(ADMIN_PASSWORD).unwrap(); let config = Config::new(None, None, None, true); - assert_eq!(Ok(()), admin.write_config(config)); + assert_ok!((), admin.write_config(config)); let get_config = admin.get_config().unwrap(); assert_eq!(config, get_config); let config = Config::new(None, Some(9), None, true); - assert_eq!(Err(CommandError::InvalidSlot), admin.write_config(config)); + assert_cmd_err!(CommandError::InvalidSlot, admin.write_config(config)); let config = Config::new(Some(1), None, Some(0), false); - assert_eq!(Ok(()), admin.write_config(config)); + assert_ok!((), admin.write_config(config)); let get_config = admin.get_config().unwrap(); assert_eq!(config, get_config); let config = Config::new(None, None, None, false); - assert_eq!(Ok(()), admin.write_config(config)); + assert_ok!((), admin.write_config(config)); let get_config = admin.get_config().unwrap(); assert_eq!(config, get_config); } @@ -159,7 +159,7 @@ fn change_user_pin(device: DeviceWrapper) { .device(); let result = device.change_user_pin(USER_PASSWORD, USER_PASSWORD); - assert_eq!(Err(CommandError::WrongPassword), result); + assert_cmd_err!(CommandError::WrongPassword, result); assert!(device .change_user_pin(USER_NEW_PASSWORD, USER_PASSWORD) @@ -184,8 +184,8 @@ fn change_admin_pin(device: DeviceWrapper) { .unwrap() .device(); - assert_eq!( - Err(CommandError::WrongPassword), + assert_cmd_err!( + CommandError::WrongPassword, device.change_admin_pin(ADMIN_PASSWORD, ADMIN_PASSWORD) ); @@ -205,7 +205,10 @@ where let result = device.authenticate_user(password); assert!(result.is_err()); let err = result.unwrap_err(); - assert_eq!(error, err.1); + match err.1 { + Error::CommandError(err) => assert_eq!(error, err), + _ => assert!(false), + }; err.0 } @@ -215,8 +218,8 @@ fn unlock_user_pin(device: DeviceWrapper) { assert!(device .unlock_user_pin(ADMIN_PASSWORD, USER_PASSWORD) .is_ok()); - assert_eq!( - Err(CommandError::WrongPassword), + assert_cmd_err!( + CommandError::WrongPassword, device.unlock_user_pin(USER_PASSWORD, USER_PASSWORD) ); @@ -228,8 +231,8 @@ fn unlock_user_pin(device: DeviceWrapper) { let device = require_failed_user_login(device, USER_PASSWORD, CommandError::WrongPassword); // unblock with current PIN - assert_eq!( - Err(CommandError::WrongPassword), + assert_cmd_err!( + CommandError::WrongPassword, device.unlock_user_pin(USER_PASSWORD, USER_PASSWORD) ); assert!(device @@ -244,8 +247,8 @@ fn unlock_user_pin(device: DeviceWrapper) { let device = require_failed_user_login(device, USER_PASSWORD, CommandError::WrongPassword); // unblock with new PIN - assert_eq!( - Err(CommandError::WrongPassword), + assert_cmd_err!( + CommandError::WrongPassword, device.unlock_user_pin(USER_PASSWORD, USER_PASSWORD) ); assert!(device @@ -262,39 +265,33 @@ fn unlock_user_pin(device: DeviceWrapper) { fn factory_reset(device: DeviceWrapper) { let admin = device.authenticate_admin(ADMIN_PASSWORD).unwrap(); let otp_data = OtpSlotData::new(1, "test", "0123468790", OtpMode::SixDigits); - assert_eq!(Ok(()), admin.write_totp_slot(otp_data, 30)); + assert_ok!((), admin.write_totp_slot(otp_data, 30)); let device = admin.device(); let pws = device.get_password_safe(USER_PASSWORD).unwrap(); - assert_eq!(Ok(()), pws.write_slot(0, "test", "testlogin", "testpw")); + assert_ok!((), pws.write_slot(0, "test", "testlogin", "testpw")); drop(pws); - assert_eq!( - Ok(()), - device.change_user_pin(USER_PASSWORD, USER_NEW_PASSWORD) - ); - assert_eq!( - Ok(()), + assert_ok!((), device.change_user_pin(USER_PASSWORD, USER_NEW_PASSWORD)); + assert_ok!( + (), device.change_admin_pin(ADMIN_PASSWORD, ADMIN_NEW_PASSWORD) ); - assert_eq!( - Err(CommandError::WrongPassword), + assert_cmd_err!( + CommandError::WrongPassword, device.factory_reset(USER_NEW_PASSWORD) ); - assert_eq!( - Err(CommandError::WrongPassword), + assert_cmd_err!( + CommandError::WrongPassword, device.factory_reset(ADMIN_PASSWORD) ); - assert_eq!(Ok(()), device.factory_reset(ADMIN_NEW_PASSWORD)); + assert_ok!((), device.factory_reset(ADMIN_NEW_PASSWORD)); let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap().device(); let user = device.authenticate_user(USER_PASSWORD).unwrap(); - assert_eq!( - Err(CommandError::SlotNotProgrammed), - user.get_totp_slot_name(1) - ); + assert_cmd_err!(CommandError::SlotNotProgrammed, user.get_totp_slot_name(1)); let device = user.device(); let pws = device.get_password_safe(USER_PASSWORD).unwrap(); @@ -302,20 +299,20 @@ fn factory_reset(device: DeviceWrapper) { assert_ne!("testlogin".to_string(), pws.get_slot_login(0).unwrap()); assert_ne!("testpw".to_string(), pws.get_slot_password(0).unwrap()); - assert_eq!(Ok(()), device.build_aes_key(ADMIN_PASSWORD)); + assert_ok!((), device.build_aes_key(ADMIN_PASSWORD)); } #[test_device] fn build_aes_key(device: DeviceWrapper) { let pws = device.get_password_safe(USER_PASSWORD).unwrap(); - assert_eq!(Ok(()), pws.write_slot(0, "test", "testlogin", "testpw")); + assert_ok!((), pws.write_slot(0, "test", "testlogin", "testpw")); drop(pws); - assert_eq!( - Err(CommandError::WrongPassword), + assert_cmd_err!( + CommandError::WrongPassword, device.build_aes_key(USER_PASSWORD) ); - assert_eq!(Ok(()), device.build_aes_key(ADMIN_PASSWORD)); + assert_ok!((), device.build_aes_key(ADMIN_PASSWORD)); let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap().device(); @@ -327,74 +324,71 @@ fn build_aes_key(device: DeviceWrapper) { #[test_device] fn change_update_pin(device: Storage) { - assert_eq!( - Err(CommandError::WrongPassword), + assert_cmd_err!( + CommandError::WrongPassword, device.change_update_pin(UPDATE_NEW_PIN, UPDATE_PIN) ); - assert_eq!(Ok(()), device.change_update_pin(UPDATE_PIN, UPDATE_NEW_PIN)); - assert_eq!(Ok(()), device.change_update_pin(UPDATE_NEW_PIN, UPDATE_PIN)); + assert_ok!((), device.change_update_pin(UPDATE_PIN, UPDATE_NEW_PIN)); + assert_ok!((), device.change_update_pin(UPDATE_NEW_PIN, UPDATE_PIN)); } #[test_device] fn encrypted_volume(device: Storage) { - assert_eq!(Ok(()), device.lock()); + assert_ok!((), device.lock()); assert_eq!(1, count_nitrokey_block_devices()); - assert_eq!(Ok(()), device.disable_encrypted_volume()); + assert_ok!((), device.disable_encrypted_volume()); assert_eq!(1, count_nitrokey_block_devices()); - assert_eq!( - Err(CommandError::WrongPassword), + assert_cmd_err!( + CommandError::WrongPassword, device.enable_encrypted_volume("123") ); assert_eq!(1, count_nitrokey_block_devices()); - assert_eq!(Ok(()), device.enable_encrypted_volume(USER_PASSWORD)); + assert_ok!((), device.enable_encrypted_volume(USER_PASSWORD)); assert_eq!(2, count_nitrokey_block_devices()); - assert_eq!(Ok(()), device.disable_encrypted_volume()); + assert_ok!((), device.disable_encrypted_volume()); assert_eq!(1, count_nitrokey_block_devices()); } #[test_device] fn hidden_volume(device: Storage) { - assert_eq!(Ok(()), device.lock()); + assert_ok!((), device.lock()); assert_eq!(1, count_nitrokey_block_devices()); - assert_eq!(Ok(()), device.disable_hidden_volume()); + assert_ok!((), device.disable_hidden_volume()); assert_eq!(1, count_nitrokey_block_devices()); - assert_eq!(Ok(()), device.enable_encrypted_volume(USER_PASSWORD)); + assert_ok!((), device.enable_encrypted_volume(USER_PASSWORD)); assert_eq!(2, count_nitrokey_block_devices()); // TODO: why this error code? - assert_eq!( - Err(CommandError::WrongPassword), + assert_cmd_err!( + CommandError::WrongPassword, device.create_hidden_volume(5, 0, 100, "hiddenpw") ); - assert_eq!(Ok(()), device.create_hidden_volume(0, 20, 21, "hidden-pw")); - assert_eq!( - Ok(()), - device.create_hidden_volume(0, 20, 21, "hiddenpassword") - ); - assert_eq!(Ok(()), device.create_hidden_volume(1, 0, 1, "otherpw")); + assert_ok!((), device.create_hidden_volume(0, 20, 21, "hidden-pw")); + assert_ok!((), device.create_hidden_volume(0, 20, 21, "hiddenpassword")); + assert_ok!((), device.create_hidden_volume(1, 0, 1, "otherpw")); // TODO: test invalid range (not handled by libnitrokey) assert_eq!(2, count_nitrokey_block_devices()); - assert_eq!( - Err(CommandError::WrongPassword), + assert_cmd_err!( + CommandError::WrongPassword, device.enable_hidden_volume("blubb") ); - assert_eq!(Ok(()), device.enable_hidden_volume("hiddenpassword")); + assert_ok!((), device.enable_hidden_volume("hiddenpassword")); assert_eq!(2, count_nitrokey_block_devices()); - assert_eq!(Ok(()), device.enable_hidden_volume("otherpw")); + assert_ok!((), device.enable_hidden_volume("otherpw")); assert_eq!(2, count_nitrokey_block_devices()); - assert_eq!(Ok(()), device.disable_hidden_volume()); + assert_ok!((), device.disable_hidden_volume()); assert_eq!(1, count_nitrokey_block_devices()); } #[test_device] fn lock(device: Storage) { - assert_eq!(Ok(()), device.enable_encrypted_volume(USER_PASSWORD)); - assert_eq!(Ok(()), device.lock()); + assert_ok!((), device.enable_encrypted_volume(USER_PASSWORD)); + assert_ok!((), device.lock()); assert_eq!(1, count_nitrokey_block_devices()); } @@ -410,17 +404,14 @@ fn set_unencrypted_volume_mode(device: Storage) { } fn assert_success(device: &Storage, mode: VolumeMode) { - assert_eq!( - Ok(()), - device.set_unencrypted_volume_mode(ADMIN_PASSWORD, mode) - ); + assert_ok!((), device.set_unencrypted_volume_mode(ADMIN_PASSWORD, mode)); assert_mode(&device, mode); } assert_success(&device, VolumeMode::ReadOnly); - assert_eq!( - Err(CommandError::WrongPassword), + assert_cmd_err!( + CommandError::WrongPassword, device.set_unencrypted_volume_mode(USER_PASSWORD, VolumeMode::ReadOnly) ); assert_mode(&device, VolumeMode::ReadOnly); @@ -460,17 +451,17 @@ fn get_production_info(device: Storage) { #[test_device] fn clear_new_sd_card_warning(device: Storage) { - assert_eq!(Ok(()), device.factory_reset(ADMIN_PASSWORD)); + assert_ok!((), device.factory_reset(ADMIN_PASSWORD)); thread::sleep(time::Duration::from_secs(3)); - assert_eq!(Ok(()), device.build_aes_key(ADMIN_PASSWORD)); + assert_ok!((), device.build_aes_key(ADMIN_PASSWORD)); // We have to perform an SD card operation to reset the new_sd_card_found field - assert_eq!(Ok(()), device.lock()); + assert_ok!((), device.lock()); let status = device.get_status().unwrap(); assert!(status.new_sd_card_found); - assert_eq!(Ok(()), device.clear_new_sd_card_warning(ADMIN_PASSWORD)); + assert_ok!((), device.clear_new_sd_card_warning(ADMIN_PASSWORD)); let status = device.get_status().unwrap(); assert!(!status.new_sd_card_found); @@ -478,18 +469,18 @@ fn clear_new_sd_card_warning(device: Storage) { #[test_device] fn export_firmware(device: Storage) { - assert_eq!( - Err(CommandError::WrongPassword), + assert_cmd_err!( + CommandError::WrongPassword, device.export_firmware("someadminpn") ); - assert_eq!(Ok(()), device.export_firmware(ADMIN_PASSWORD)); - assert_eq!( - Ok(()), + assert_ok!((), device.export_firmware(ADMIN_PASSWORD)); + assert_ok!( + (), device.set_unencrypted_volume_mode(ADMIN_PASSWORD, VolumeMode::ReadWrite) ); - assert_eq!(Ok(()), device.export_firmware(ADMIN_PASSWORD)); - assert_eq!( - Ok(()), + assert_ok!((), device.export_firmware(ADMIN_PASSWORD)); + assert_ok!( + (), device.set_unencrypted_volume_mode(ADMIN_PASSWORD, VolumeMode::ReadOnly) ); } diff --git a/tests/otp.rs b/tests/otp.rs index 712f7a2..d328351 100644 --- a/tests/otp.rs +++ b/tests/otp.rs @@ -38,7 +38,7 @@ enum TotpTimestampSize { fn make_admin_test_device(device: T) -> Admin where T: Device, - (T, nitrokey::CommandError): Debug, + (T, nitrokey::Error): Debug, { device .authenticate_admin(ADMIN_PASSWORD) @@ -47,7 +47,7 @@ where fn configure_hotp(admin: &ConfigureOtp, counter: u8) { let slot_data = OtpSlotData::new(1, "test-hotp", HOTP_SECRET, OtpMode::SixDigits); - assert_eq!(Ok(()), admin.write_hotp_slot(slot_data, counter.into())); + assert_ok!((), admin.write_hotp_slot(slot_data, counter.into())); } fn check_hotp_codes(device: &GenerateOtp, offset: u8) { @@ -61,20 +61,17 @@ fn check_hotp_codes(device: &GenerateOtp, offset: u8) { #[test_device] fn set_time(device: DeviceWrapper) { - assert_eq!(Ok(()), device.set_time(1546385382, true)); - assert_eq!(Ok(()), device.set_time(1546385392, false)); - assert_eq!( - Err(CommandError::Timestamp), - device.set_time(1546385292, false) - ); - assert_eq!(Ok(()), device.set_time(1546385382, true)); + assert_ok!((), device.set_time(1546385382, true)); + assert_ok!((), device.set_time(1546385392, false)); + assert_cmd_err!(CommandError::Timestamp, device.set_time(1546385292, false)); + assert_ok!((), device.set_time(1546385382, true)); } #[test_device] fn hotp_no_pin(device: DeviceWrapper) { let admin = make_admin_test_device(device); let config = Config::new(None, None, None, false); - assert_eq!(Ok(()), admin.write_config(config)); + assert_ok!((), admin.write_config(config)); configure_hotp(&admin, 0); check_hotp_codes(admin.deref(), 0); @@ -90,7 +87,7 @@ fn hotp_no_pin(device: DeviceWrapper) { fn hotp_pin(device: DeviceWrapper) { let admin = make_admin_test_device(device); let config = Config::new(None, None, None, true); - assert_eq!(Ok(()), admin.write_config(config)); + assert_ok!((), admin.write_config(config)); configure_hotp(&admin, 0); let user = admin.device().authenticate_user(USER_PASSWORD).unwrap(); @@ -103,54 +100,51 @@ fn hotp_pin(device: DeviceWrapper) { fn hotp_slot_name(device: DeviceWrapper) { let admin = make_admin_test_device(device); let slot_data = OtpSlotData::new(1, "test-hotp", HOTP_SECRET, OtpMode::SixDigits); - assert_eq!(Ok(()), admin.write_hotp_slot(slot_data, 0)); + 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_eq!(CommandError::InvalidSlot, result.unwrap_err()); + assert_cmd_err!(CommandError::InvalidSlot, result); } #[test_device] fn hotp_error(device: DeviceWrapper) { let admin = make_admin_test_device(device); let slot_data = OtpSlotData::new(1, "", HOTP_SECRET, OtpMode::SixDigits); - assert_eq!( - Err(CommandError::NoName), - admin.write_hotp_slot(slot_data, 0) - ); + assert_cmd_err!(CommandError::NoName, admin.write_hotp_slot(slot_data, 0)); let slot_data = OtpSlotData::new(4, "test", HOTP_SECRET, OtpMode::SixDigits); - assert_eq!( - Err(CommandError::InvalidSlot), + assert_cmd_err!( + CommandError::InvalidSlot, admin.write_hotp_slot(slot_data, 0) ); let slot_data = OtpSlotData::new(1, "test", "foobar", OtpMode::SixDigits); - assert_eq!( - Err(CommandError::InvalidHexString), + assert_cmd_err!( + CommandError::InvalidHexString, admin.write_hotp_slot(slot_data, 0) ); let code = admin.get_hotp_code(4); - assert_eq!(CommandError::InvalidSlot, code.unwrap_err()); + assert_cmd_err!(CommandError::InvalidSlot, code); } #[test_device] fn hotp_erase(device: DeviceWrapper) { let admin = make_admin_test_device(device); let config = Config::new(None, None, None, false); - assert_eq!(Ok(()), admin.write_config(config)); + assert_ok!((), admin.write_config(config)); let slot_data = OtpSlotData::new(1, "test1", HOTP_SECRET, OtpMode::SixDigits); - assert_eq!(Ok(()), admin.write_hotp_slot(slot_data, 0)); + assert_ok!((), admin.write_hotp_slot(slot_data, 0)); let slot_data = OtpSlotData::new(2, "test2", HOTP_SECRET, OtpMode::SixDigits); - assert_eq!(Ok(()), admin.write_hotp_slot(slot_data, 0)); + assert_ok!((), admin.write_hotp_slot(slot_data, 0)); - assert_eq!(Ok(()), admin.erase_hotp_slot(1)); + assert_ok!((), admin.erase_hotp_slot(1)); let device = admin.device(); let result = device.get_hotp_slot_name(1); - assert_eq!(CommandError::SlotNotProgrammed, result.unwrap_err()); + assert_cmd_err!(CommandError::SlotNotProgrammed, result); let result = device.get_hotp_code(1); - assert_eq!(CommandError::SlotNotProgrammed, result.unwrap_err()); + assert_cmd_err!(CommandError::SlotNotProgrammed, result); assert_eq!("test2", device.get_hotp_slot_name(2).unwrap()); } @@ -158,7 +152,7 @@ fn hotp_erase(device: DeviceWrapper) { fn configure_totp(admin: &ConfigureOtp, factor: u64) { let slot_data = OtpSlotData::new(1, "test-totp", TOTP_SECRET, OtpMode::EightDigits); let time_window = 30u64.checked_mul(factor).unwrap(); - assert_eq!(Ok(()), admin.write_totp_slot(slot_data, time_window as u16)); + assert_ok!((), admin.write_totp_slot(slot_data, time_window as u16)); } fn check_totp_codes(device: &GenerateOtp, factor: u64, timestamp_size: TotpTimestampSize) { @@ -169,7 +163,7 @@ fn check_totp_codes(device: &GenerateOtp, factor: u64, timestamp_size: TotpTimes continue; } - assert_eq!(Ok(()), device.set_time(time, true)); + assert_ok!((), device.set_time(time, true)); let result = device.get_totp_code(1); assert!(result.is_ok()); let result_code = result.unwrap(); @@ -186,7 +180,7 @@ 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 config = Config::new(None, None, None, false); - assert_eq!(Ok(()), admin.write_config(config)); + assert_ok!((), admin.write_config(config)); configure_totp(&admin, 1); check_totp_codes(admin.deref(), 1, TotpTimestampSize::U32); @@ -204,7 +198,7 @@ fn totp_no_pin(device: DeviceWrapper) { fn totp_no_pin_64(device: Pro) { let admin = make_admin_test_device(device); let config = Config::new(None, None, None, false); - assert_eq!(Ok(()), admin.write_config(config)); + assert_ok!((), admin.write_config(config)); configure_totp(&admin, 1); check_totp_codes(admin.deref(), 1, TotpTimestampSize::U64); @@ -221,7 +215,7 @@ fn totp_pin(device: DeviceWrapper) { // TODO: this test may fail due to bad timing --> find solution let admin = make_admin_test_device(device); let config = Config::new(None, None, None, true); - assert_eq!(Ok(()), admin.write_config(config)); + assert_ok!((), admin.write_config(config)); configure_totp(&admin, 1); let user = admin.device().authenticate_user(USER_PASSWORD).unwrap(); @@ -235,7 +229,7 @@ fn totp_pin(device: DeviceWrapper) { fn totp_pin_64(device: Pro) { let admin = make_admin_test_device(device); let config = Config::new(None, None, None, true); - assert_eq!(Ok(()), admin.write_config(config)); + assert_ok!((), admin.write_config(config)); configure_totp(&admin, 1); let user = admin.device().authenticate_user(USER_PASSWORD).unwrap(); @@ -248,55 +242,52 @@ fn totp_pin_64(device: Pro) { fn totp_slot_name(device: DeviceWrapper) { let admin = make_admin_test_device(device); let slot_data = OtpSlotData::new(1, "test-totp", TOTP_SECRET, OtpMode::EightDigits); - assert_eq!(Ok(()), admin.write_totp_slot(slot_data, 0)); + assert_ok!((), admin.write_totp_slot(slot_data, 0)); let device = admin.device(); let result = device.get_totp_slot_name(1); assert!(result.is_ok()); assert_eq!("test-totp", result.unwrap()); let result = device.get_totp_slot_name(16); - assert_eq!(CommandError::InvalidSlot, result.unwrap_err()); + assert_cmd_err!(CommandError::InvalidSlot, result); } #[test_device] fn totp_error(device: DeviceWrapper) { let admin = make_admin_test_device(device); let slot_data = OtpSlotData::new(1, "", TOTP_SECRET, OtpMode::SixDigits); - assert_eq!( - Err(CommandError::NoName), - admin.write_totp_slot(slot_data, 0) - ); + assert_cmd_err!(CommandError::NoName, admin.write_totp_slot(slot_data, 0)); let slot_data = OtpSlotData::new(20, "test", TOTP_SECRET, OtpMode::SixDigits); - assert_eq!( - Err(CommandError::InvalidSlot), + assert_cmd_err!( + CommandError::InvalidSlot, admin.write_totp_slot(slot_data, 0) ); let slot_data = OtpSlotData::new(4, "test", "foobar", OtpMode::SixDigits); - assert_eq!( - Err(CommandError::InvalidHexString), + assert_cmd_err!( + CommandError::InvalidHexString, admin.write_totp_slot(slot_data, 0) ); let code = admin.get_totp_code(20); - assert_eq!(CommandError::InvalidSlot, code.unwrap_err()); + assert_cmd_err!(CommandError::InvalidSlot, code); } #[test_device] fn totp_erase(device: DeviceWrapper) { let admin = make_admin_test_device(device); let config = Config::new(None, None, None, false); - assert_eq!(Ok(()), admin.write_config(config)); + assert_ok!((), admin.write_config(config)); let slot_data = OtpSlotData::new(1, "test1", TOTP_SECRET, OtpMode::SixDigits); - assert_eq!(Ok(()), admin.write_totp_slot(slot_data, 0)); + assert_ok!((), admin.write_totp_slot(slot_data, 0)); let slot_data = OtpSlotData::new(2, "test2", TOTP_SECRET, OtpMode::SixDigits); - assert_eq!(Ok(()), admin.write_totp_slot(slot_data, 0)); + assert_ok!((), admin.write_totp_slot(slot_data, 0)); - assert_eq!(Ok(()), admin.erase_totp_slot(1)); + assert_ok!((), admin.erase_totp_slot(1)); let device = admin.device(); let result = device.get_totp_slot_name(1); - assert_eq!(CommandError::SlotNotProgrammed, result.unwrap_err()); + assert_cmd_err!(CommandError::SlotNotProgrammed, result); let result = device.get_totp_code(1); - assert_eq!(CommandError::SlotNotProgrammed, result.unwrap_err()); + assert_cmd_err!(CommandError::SlotNotProgrammed, result); assert_eq!("test2", device.get_totp_slot_name(2).unwrap()); } diff --git a/tests/pws.rs b/tests/pws.rs index fbcc0c1..f12af48 100644 --- a/tests/pws.rs +++ b/tests/pws.rs @@ -3,16 +3,16 @@ mod util; use std::ffi::CStr; use libc::{c_int, c_void, free}; -use nitrokey::{CommandError, Device, GetPasswordSafe, PasswordSafe, SLOT_COUNT}; +use nitrokey::{CommandError, Device, Error, GetPasswordSafe, PasswordSafe, 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 { +fn get_slot_name_direct(slot: u8) -> Result { let ptr = unsafe { nitrokey_sys::NK_get_password_safe_slot_name(slot) }; if ptr.is_null() { - return Err(CommandError::Undefined); + return Err(CommandError::Undefined.into()); } let s = unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() }; unsafe { free(ptr as *mut c_void) }; @@ -21,7 +21,7 @@ fn get_slot_name_direct(slot: u8) -> Result { let error = unsafe { nitrokey_sys::NK_get_last_command_status() } as c_int; match error { 0 => Ok(s), - other => Err(CommandError::from(other)), + other => Err(CommandError::from(other).into()), } } false => Ok(s), @@ -49,35 +49,35 @@ fn enable(device: DeviceWrapper) { fn drop(device: DeviceWrapper) { { let pws = get_pws(&device); - assert_eq!(Ok(()), pws.write_slot(1, "name", "login", "password")); + assert_ok!((), pws.write_slot(1, "name", "login", "password")); assert_eq!("name", pws.get_slot_name(1).unwrap()); let result = get_slot_name_direct(1); - assert_eq!(Ok(String::from("name")), result); + assert_ok!(String::from("name"), result); } let result = get_slot_name_direct(1); - assert_eq!(Ok(String::from("name")), result); - assert_eq!(Ok(()), device.lock()); + assert_ok!(String::from("name"), result); + assert_ok!((), device.lock()); let result = get_slot_name_direct(1); - assert_eq!(Err(CommandError::NotAuthorized), result); + assert_cmd_err!(CommandError::NotAuthorized, result); } #[test_device] fn get_status(device: DeviceWrapper) { let pws = get_pws(&device); for i in 0..SLOT_COUNT { - assert_eq!(Ok(()), pws.erase_slot(i), "Could not erase slot {}", i); + assert_ok!((), pws.erase_slot(i)); } let status = pws.get_slot_status().unwrap(); assert_eq!(status, [false; SLOT_COUNT as usize]); - assert_eq!(Ok(()), pws.write_slot(1, "name", "login", "password")); + assert_ok!((), pws.write_slot(1, "name", "login", "password")); let status = pws.get_slot_status().unwrap(); for i in 0..SLOT_COUNT { assert_eq!(i == 1, status[i as usize]); } for i in 0..SLOT_COUNT { - assert_eq!(Ok(()), pws.write_slot(i, "name", "login", "password")); + assert_ok!((), pws.write_slot(i, "name", "login", "password")); } let status = pws.get_slot_status().unwrap(); assert_eq!(status, [true; SLOT_COUNT as usize]); @@ -86,76 +86,61 @@ fn get_status(device: DeviceWrapper) { #[test_device] fn get_data(device: DeviceWrapper) { let pws = get_pws(&device); - assert_eq!(Ok(()), pws.write_slot(1, "name", "login", "password")); + 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_eq!(Ok(()), pws.erase_slot(1)); - assert_eq!(Err(CommandError::SlotNotProgrammed), pws.get_slot_name(1)); - assert_eq!(Err(CommandError::SlotNotProgrammed), pws.get_slot_login(1)); - assert_eq!( - Err(CommandError::SlotNotProgrammed), - pws.get_slot_password(1) - ); + assert_ok!((), pws.erase_slot(1)); + assert_cmd_err!(CommandError::SlotNotProgrammed, pws.get_slot_name(1)); + assert_cmd_err!(CommandError::SlotNotProgrammed, pws.get_slot_login(1)); + assert_cmd_err!(CommandError::SlotNotProgrammed, pws.get_slot_password(1)); let name = "with å"; let login = "pär@test.com"; let password = "'i3lJc[09?I:,[u7dWz9"; - assert_eq!(Ok(()), pws.write_slot(1, name, login, password)); + 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_eq!( - Err(CommandError::InvalidSlot), - pws.get_slot_name(SLOT_COUNT) - ); - assert_eq!( - Err(CommandError::InvalidSlot), - pws.get_slot_login(SLOT_COUNT) - ); - assert_eq!( - Err(CommandError::InvalidSlot), - pws.get_slot_password(SLOT_COUNT) - ); + assert_cmd_err!(CommandError::InvalidSlot, pws.get_slot_name(SLOT_COUNT)); + assert_cmd_err!(CommandError::InvalidSlot, pws.get_slot_login(SLOT_COUNT)); + assert_cmd_err!(CommandError::InvalidSlot, pws.get_slot_password(SLOT_COUNT)); } #[test_device] fn write(device: DeviceWrapper) { let pws = get_pws(&device); - assert_eq!( - Err(CommandError::InvalidSlot), + assert_cmd_err!( + CommandError::InvalidSlot, pws.write_slot(SLOT_COUNT, "name", "login", "password") ); - assert_eq!(Ok(()), pws.write_slot(0, "", "login", "password")); - assert_eq!(Err(CommandError::SlotNotProgrammed), pws.get_slot_name(0)); - assert_eq!(Ok(String::from("login")), pws.get_slot_login(0)); - assert_eq!(Ok(String::from("password")), pws.get_slot_password(0)); - - assert_eq!(Ok(()), pws.write_slot(0, "name", "", "password")); - assert_eq!(Ok(String::from("name")), pws.get_slot_name(0)); - assert_eq!(Err(CommandError::SlotNotProgrammed), pws.get_slot_login(0)); - assert_eq!(Ok(String::from("password")), pws.get_slot_password(0)); - - assert_eq!(Ok(()), pws.write_slot(0, "name", "login", "")); - assert_eq!(Ok(String::from("name")), pws.get_slot_name(0)); - assert_eq!(Ok(String::from("login")), pws.get_slot_login(0)); - assert_eq!( - Err(CommandError::SlotNotProgrammed), - pws.get_slot_password(0) - ); + assert_ok!((), pws.write_slot(0, "", "login", "password")); + assert_cmd_err!(CommandError::SlotNotProgrammed, pws.get_slot_name(0)); + assert_ok!(String::from("login"), pws.get_slot_login(0)); + assert_ok!(String::from("password"), pws.get_slot_password(0)); + + assert_ok!((), pws.write_slot(0, "name", "", "password")); + assert_ok!(String::from("name"), pws.get_slot_name(0)); + assert_cmd_err!(CommandError::SlotNotProgrammed, pws.get_slot_login(0)); + assert_ok!(String::from("password"), pws.get_slot_password(0)); + + assert_ok!((), pws.write_slot(0, "name", "login", "")); + assert_ok!(String::from("name"), pws.get_slot_name(0)); + assert_ok!(String::from("login"), pws.get_slot_login(0)); + assert_cmd_err!(CommandError::SlotNotProgrammed, pws.get_slot_password(0)); } #[test_device] fn erase(device: DeviceWrapper) { let pws = get_pws(&device); - assert_eq!(Err(CommandError::InvalidSlot), pws.erase_slot(SLOT_COUNT)); + assert_cmd_err!(CommandError::InvalidSlot, pws.erase_slot(SLOT_COUNT)); - assert_eq!(Ok(()), pws.write_slot(0, "name", "login", "password")); - assert_eq!(Ok(()), pws.erase_slot(0)); - assert_eq!(Ok(()), pws.erase_slot(0)); - assert_eq!(Err(CommandError::SlotNotProgrammed), pws.get_slot_name(0)); + assert_ok!((), pws.write_slot(0, "name", "login", "password")); + assert_ok!((), pws.erase_slot(0)); + assert_ok!((), pws.erase_slot(0)); + assert_cmd_err!(CommandError::SlotNotProgrammed, pws.get_slot_name(0)); } diff --git a/tests/util/mod.rs b/tests/util/mod.rs index cbf6b93..f0d0bb5 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -1,2 +1,69 @@ pub static ADMIN_PASSWORD: &str = "12345678"; pub static USER_PASSWORD: &str = "123456"; + +#[macro_export] +macro_rules! assert_ok { + ($left:expr, $right:expr) => {{ + match &$right { + Ok(right) => match &$left { + left => { + if !(*left == *right) { + panic!( + r#"assertion failed: `(left == right)` + left: `{:?}`, + right: `{:?}`"#, + left, right + ) + } + } + }, + Err(right_err) => panic!( + r#"assertion failed: `(left == right)` + left: `Ok({:?})`, + right: `Err({:?})`"#, + $left, right_err + ), + } + }}; +} + +#[macro_export] +macro_rules! assert_err { + ($err:path, $left:expr, $right:expr) => { + match &$right { + Err($err(ref right_err)) => match &$left { + left_err => { + if !(*left_err == *right_err) { + panic!( + r#"assertion failed: `(left == right)` + left: `{:?}`, + right: `{:?}`"#, + left_err, right_err + ) + } + } + }, + Err(ref right_err) => panic!( + r#"assertion failed: `(left == right)` + left: `{:?}`, + right: `{:?}`"#, + $err($left), + right_err + ), + Ok(right_ok) => panic!( + r#"assertion failed: `(left == right)` + left: `Err({:?})`, + right: `Ok({:?})`"#, + $err($left), + right_ok + ), + } + }; +} + +#[macro_export] +macro_rules! assert_cmd_err { + ($left:expr, $right:expr) => { + assert_err!(::nitrokey::Error::CommandError, $left, $right); + }; +} -- cgit v1.2.3 From 944e1fa0d51e547dde2a9368d2b8431b109f63c4 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 17 Jan 2019 04:15:31 +0000 Subject: Move the CommandError::Unknown to Error An error code can not only indiciate a command error, but also a library or device communication error. Therefore, the variant for an unknown error code should be placed in the top-level Error enum instead of the CommandError enum. --- CHANGELOG.md | 3 ++- src/auth.rs | 4 ++-- src/error.rs | 61 +++++++++++++++++++++++++++++++++--------------------------- src/util.rs | 2 +- tests/pws.rs | 2 +- 5 files changed, 40 insertions(+), 32 deletions(-) (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a78724..def5273 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,8 @@ - Implement `std::error::Error` for `CommandError`. - Add the `Error` enum and the `Result` typedef. - Return `Error` instead of `CommandError` in all public functions. - - Move the `CommandError::RngError` variant to `Error::RandError`. + - Move the `CommandError::RngError` variant to `Error::RandError` and the + `CommandError::Unknown` variant to `Error::Unknown`. # v0.3.4 (2019-01-20) - Fix authentication methods that assumed that `char` is signed. diff --git a/src/auth.rs b/src/auth.rs index 509d3aa..e05f6b3 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -6,7 +6,7 @@ use nitrokey_sys; use crate::config::{Config, RawConfig}; use crate::device::{Device, DeviceWrapper, Pro, Storage}; -use crate::error::{CommandError, Error}; +use crate::error::Error; use crate::otp::{ConfigureOtp, GenerateOtp, OtpMode, OtpSlotData, RawOtpSlotData}; use crate::util::{generate_password, get_command_result, get_cstring, result_from_string}; @@ -161,7 +161,7 @@ where let temp_password_ptr = temp_password.as_ptr() as *const c_char; return match callback(password_ptr, temp_password_ptr) { 0 => Ok(A::new(device, temp_password)), - rv => Err((device, CommandError::from(rv).into())), + rv => Err((device, Error::from(rv))), }; } diff --git a/src/error.rs b/src/error.rs index dfe1680..c5a975e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -13,6 +13,18 @@ pub enum Error { CommunicationError(CommunicationError), /// An error that occured during random number generation. RandError(rand_core::Error), + /// An unknown error returned by libnitrokey. + Unknown(i64), +} + +impl From for Error { + fn from(code: raw::c_int) -> Self { + if let Some(err) = CommandError::try_from(code) { + Error::CommandError(err) + } else { + Error::Unknown(code.into()) + } + } } impl From for Error { @@ -33,6 +45,7 @@ impl error::Error for Error { Error::CommandError(ref err) => Some(err), Error::CommunicationError(_) => None, Error::RandError(ref err) => Some(err), + Error::Unknown(_) => None, } } } @@ -43,6 +56,7 @@ impl fmt::Display for Error { Error::CommandError(ref err) => write!(f, "Command error: {}", err), Error::CommunicationError(_) => write!(f, "Placeholder"), Error::RandError(ref err) => write!(f, "RNG error: {}", err), + Error::Unknown(ref err) => write!(f, "Unknown error: {}", err), } } } @@ -74,8 +88,6 @@ pub enum CommandError { UnknownCommand, /// AES decryption failed. AesDecryptionFailed, - /// An unknown error occurred. - Unknown(i64), /// An unspecified error occurred. Undefined, /// You passed a string containing a null byte. @@ -98,6 +110,26 @@ pub enum CommunicationError { } impl CommandError { + fn try_from(value: raw::c_int) -> Option { + match value { + 1 => Some(CommandError::WrongCrc), + 2 => Some(CommandError::WrongSlot), + 3 => Some(CommandError::SlotNotProgrammed), + 4 => Some(CommandError::WrongPassword), + 5 => Some(CommandError::NotAuthorized), + 6 => Some(CommandError::Timestamp), + 7 => Some(CommandError::NoName), + 8 => Some(CommandError::NotSupported), + 9 => Some(CommandError::UnknownCommand), + 10 => Some(CommandError::AesDecryptionFailed), + 200 => Some(CommandError::StringTooLong), + 201 => Some(CommandError::InvalidSlot), + 202 => Some(CommandError::InvalidHexString), + 203 => Some(CommandError::TargetBufferTooSmall), + _ => None, + } + } + fn as_str(&self) -> borrow::Cow<'static, str> { match *self { CommandError::WrongCrc => { @@ -116,9 +148,6 @@ impl CommandError { CommandError::NotSupported => "This command is not supported by this device".into(), CommandError::UnknownCommand => "This command is unknown".into(), CommandError::AesDecryptionFailed => "AES decryption failed".into(), - CommandError::Unknown(x) => { - borrow::Cow::from(format!("An unknown error occurred ({})", x)) - } CommandError::Undefined => "An unspecified error occurred".into(), CommandError::InvalidString => "You passed a string containing a null byte".into(), CommandError::StringTooLong => "The supplied string is too long".into(), @@ -138,25 +167,3 @@ impl fmt::Display for CommandError { write!(f, "{}", self.as_str()) } } - -impl From for CommandError { - fn from(value: raw::c_int) -> Self { - match value { - 1 => CommandError::WrongCrc, - 2 => CommandError::WrongSlot, - 3 => CommandError::SlotNotProgrammed, - 4 => CommandError::WrongPassword, - 5 => CommandError::NotAuthorized, - 6 => CommandError::Timestamp, - 7 => CommandError::NoName, - 8 => CommandError::NotSupported, - 9 => CommandError::UnknownCommand, - 10 => CommandError::AesDecryptionFailed, - 200 => CommandError::StringTooLong, - 201 => CommandError::InvalidSlot, - 202 => CommandError::InvalidHexString, - 203 => CommandError::TargetBufferTooSmall, - x => CommandError::Unknown(x.into()), - } - } -} diff --git a/src/util.rs b/src/util.rs index 06bb854..3b9904f 100644 --- a/src/util.rs +++ b/src/util.rs @@ -54,7 +54,7 @@ pub fn result_from_string(ptr: *const c_char) -> Result { pub fn get_command_result(value: c_int) -> Result<(), Error> { match value { 0 => Ok(()), - other => Err(CommandError::from(other).into()), + other => Err(Error::from(other)), } } diff --git a/tests/pws.rs b/tests/pws.rs index f12af48..a4647bd 100644 --- a/tests/pws.rs +++ b/tests/pws.rs @@ -21,7 +21,7 @@ fn get_slot_name_direct(slot: u8) -> Result { let error = unsafe { nitrokey_sys::NK_get_last_command_status() } as c_int; match error { 0 => Ok(s), - other => Err(CommandError::from(other).into()), + other => Err(Error::from(other)), } } false => Ok(s), -- cgit v1.2.3 From 5e258d26b55af6bed7c316b1c7ac12e20946702d Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 17 Jan 2019 12:47:52 +0000 Subject: Refactor library errors into LibraryError enum Previously, library errors were part of the CommandError enum. As command errors and library errors are two different error types, they should be split into two enums. --- CHANGELOG.md | 2 ++ src/auth.rs | 6 ++--- src/config.rs | 4 +-- src/device.rs | 22 ++++++++--------- src/error.rs | 74 +++++++++++++++++++++++++++++++++++++++---------------- src/lib.rs | 2 +- src/otp.rs | 20 +++++++-------- src/pws.rs | 14 +++++------ src/util.rs | 4 +-- tests/device.rs | 4 +-- tests/otp.rs | 28 ++++++++++----------- tests/pws.rs | 16 ++++++------ tests/util/mod.rs | 7 ++++++ 13 files changed, 123 insertions(+), 80 deletions(-) (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index def5273..c34175e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ - Refactor the error handling code: - Implement `std::error::Error` for `CommandError`. - Add the `Error` enum and the `Result` typedef. + - Add the `LibraryError` enum and move the library error variants from + `CommandError` to `LibraryError`. - Return `Error` instead of `CommandError` in all public functions. - Move the `CommandError::RngError` variant to `Error::RandError` and the `CommandError::Unknown` variant to `Error::Unknown`. diff --git a/src/auth.rs b/src/auth.rs index e05f6b3..d1eb049 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -55,7 +55,7 @@ pub trait Authenticate { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`RngError`]: enum.CommandError.html#variant.RngError /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword fn authenticate_user(self, password: &str) -> Result, (Self, Error)> @@ -101,7 +101,7 @@ pub trait Authenticate { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`RngError`]: enum.CommandError.html#variant.RngError /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> @@ -287,7 +287,7 @@ impl Admin { /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot pub fn write_config(&self, config: Config) -> Result<(), Error> { let raw_config = RawConfig::try_from(config)?; unsafe { diff --git a/src/config.rs b/src/config.rs index 741d67e..6aa6d10 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,4 +1,4 @@ -use crate::error::{CommandError, Error}; +use crate::error::{Error, LibraryError}; /// The configuration for a Nitrokey. #[derive(Clone, Copy, Debug, PartialEq)] @@ -41,7 +41,7 @@ fn option_to_config_otp_slot(value: Option) -> Result { if value < 3 { Ok(value) } else { - Err(CommandError::InvalidSlot.into()) + Err(LibraryError::InvalidSlot.into()) } } None => Ok(255), diff --git a/src/device.rs b/src/device.rs index ccd0597..5c4014b 100644 --- a/src/device.rs +++ b/src/device.rs @@ -461,7 +461,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword fn change_admin_pin(&self, current: &str, new: &str) -> Result<(), Error> { let current_string = get_cstring(current)?; @@ -497,7 +497,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword fn change_user_pin(&self, current: &str, new: &str) -> Result<(), Error> { let current_string = get_cstring(current)?; @@ -533,7 +533,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`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> { let admin_pin_string = get_cstring(admin_pin)?; @@ -867,7 +867,7 @@ impl Storage { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword pub fn change_update_pin(&self, current: &str, new: &str) -> Result<(), Error> { let current_string = get_cstring(current)?; @@ -907,7 +907,7 @@ impl Storage { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword pub fn enable_firmware_update(&self, update_pin: &str) -> Result<(), Error> { let update_pin_string = get_cstring(update_pin)?; @@ -943,7 +943,7 @@ impl Storage { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword pub fn enable_encrypted_volume(&self, user_pin: &str) -> Result<(), Error> { let user_pin = get_cstring(user_pin)?; @@ -1021,7 +1021,7 @@ impl Storage { /// /// [`enable_encrypted_volume`]: #method.enable_encrypted_volume /// [`AesDecryptionFailed`]: enum.CommandError.html#variant.AesDecryptionFailed - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString pub fn enable_hidden_volume(&self, volume_password: &str) -> Result<(), Error> { let volume_password = get_cstring(volume_password)?; unsafe { @@ -1099,7 +1099,7 @@ impl Storage { /// ``` /// /// [`AesDecryptionFailed`]: enum.CommandError.html#variant.AesDecryptionFailed - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString pub fn create_hidden_volume( &self, slot: u8, @@ -1145,7 +1145,7 @@ impl Storage { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword pub fn set_unencrypted_volume_mode( &self, @@ -1276,7 +1276,7 @@ impl Storage { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`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> { let admin_pin = get_cstring(admin_pin)?; @@ -1304,7 +1304,7 @@ impl Storage { /// - [`InvalidString`][] if one of the provided passwords contains a null byte /// - [`WrongPassword`][] if the admin password is wrong /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword pub fn export_firmware(&self, admin_pin: &str) -> Result<(), Error> { let admin_pin_string = get_cstring(admin_pin)?; diff --git a/src/error.rs b/src/error.rs index c5a975e..f40d07f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -11,6 +11,8 @@ pub enum Error { CommandError(CommandError), /// Placeholder for testing. CommunicationError(CommunicationError), + /// A library usage error. + LibraryError(LibraryError), /// An error that occured during random number generation. RandError(rand_core::Error), /// An unknown error returned by libnitrokey. @@ -21,6 +23,8 @@ impl From for Error { fn from(code: raw::c_int) -> Self { if let Some(err) = CommandError::try_from(code) { Error::CommandError(err) + } else if let Some(err) = LibraryError::try_from(code) { + Error::LibraryError(err) } else { Error::Unknown(code.into()) } @@ -33,6 +37,12 @@ impl From for Error { } } +impl From for Error { + fn from(err: LibraryError) -> Self { + Error::LibraryError(err) + } +} + impl From for Error { fn from(error: rand_core::Error) -> Self { Error::RandError(error) @@ -44,6 +54,7 @@ impl error::Error for Error { match *self { Error::CommandError(ref err) => Some(err), Error::CommunicationError(_) => None, + Error::LibraryError(ref err) => Some(err), Error::RandError(ref err) => Some(err), Error::Unknown(_) => None, } @@ -55,6 +66,7 @@ impl fmt::Display for Error { match *self { Error::CommandError(ref err) => write!(f, "Command error: {}", err), Error::CommunicationError(_) => write!(f, "Placeholder"), + Error::LibraryError(ref err) => write!(f, "Library error: {}", err), Error::RandError(ref err) => write!(f, "RNG error: {}", err), Error::Unknown(ref err) => write!(f, "Unknown error: {}", err), } @@ -90,16 +102,6 @@ pub enum CommandError { AesDecryptionFailed, /// An unspecified error occurred. Undefined, - /// You passed a string containing a null byte. - InvalidString, - /// A supplied string exceeded a length limit. - StringTooLong, - /// You passed an invalid slot. - InvalidSlot, - /// The supplied string was not in hexadecimal format. - InvalidHexString, - /// The target buffer was smaller than the source. - TargetBufferTooSmall, } /// Placeholder for testing. @@ -122,10 +124,6 @@ impl CommandError { 8 => Some(CommandError::NotSupported), 9 => Some(CommandError::UnknownCommand), 10 => Some(CommandError::AesDecryptionFailed), - 200 => Some(CommandError::StringTooLong), - 201 => Some(CommandError::InvalidSlot), - 202 => Some(CommandError::InvalidHexString), - 203 => Some(CommandError::TargetBufferTooSmall), _ => None, } } @@ -149,13 +147,6 @@ impl CommandError { CommandError::UnknownCommand => "This command is unknown".into(), CommandError::AesDecryptionFailed => "AES decryption failed".into(), CommandError::Undefined => "An unspecified error occurred".into(), - CommandError::InvalidString => "You passed a string containing a null byte".into(), - CommandError::StringTooLong => "The supplied string is too long".into(), - CommandError::InvalidSlot => "The given slot is invalid".into(), - CommandError::InvalidHexString => { - "The supplied string is not in hexadecimal format".into() - } - CommandError::TargetBufferTooSmall => "The target buffer is too small".into(), } } } @@ -167,3 +158,44 @@ impl fmt::Display for CommandError { write!(f, "{}", self.as_str()) } } + +/// A library usage error. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum LibraryError { + /// A supplied string exceeded a length limit. + StringTooLong, + /// You passed an invalid slot. + InvalidSlot, + /// The supplied string was not in hexadecimal format. + InvalidHexString, + /// The target buffer was smaller than the source. + TargetBufferTooSmall, + /// You passed a string containing a null byte. + InvalidString, +} + +impl LibraryError { + fn try_from(value: raw::c_int) -> Option { + match value { + 200 => Some(LibraryError::StringTooLong), + 201 => Some(LibraryError::InvalidSlot), + 202 => Some(LibraryError::InvalidHexString), + 203 => Some(LibraryError::TargetBufferTooSmall), + _ => None, + } + } +} + +impl error::Error for LibraryError {} + +impl fmt::Display for LibraryError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match *self { + LibraryError::StringTooLong => "The supplied string is too long", + LibraryError::InvalidSlot => "The given slot is invalid", + LibraryError::InvalidHexString => "The supplied string is not in hexadecimal format", + LibraryError::TargetBufferTooSmall => "The target buffer is too small", + LibraryError::InvalidString => "You passed a string containing a null byte", + }) + } +} diff --git a/src/lib.rs b/src/lib.rs index 8522e83..993ec92 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -104,7 +104,7 @@ pub use crate::device::{ connect, connect_model, Device, DeviceWrapper, Model, Pro, SdCardData, Storage, StorageProductionInfo, StorageStatus, VolumeMode, VolumeStatus, }; -pub use crate::error::{CommandError, CommunicationError, Error, Result}; +pub use crate::error::{CommandError, CommunicationError, Error, LibraryError, Result}; pub use crate::otp::{ConfigureOtp, GenerateOtp, OtpMode, OtpSlotData}; pub use crate::pws::{GetPasswordSafe, PasswordSafe, SLOT_COUNT}; pub use crate::util::LogLevel; diff --git a/src/otp.rs b/src/otp.rs index 5dfe8b1..7535a77 100644 --- a/src/otp.rs +++ b/src/otp.rs @@ -47,8 +47,8 @@ pub trait ConfigureOtp { /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`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>; @@ -83,8 +83,8 @@ pub trait ConfigureOtp { /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`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>; @@ -115,7 +115,7 @@ pub trait ConfigureOtp { /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot fn erase_hotp_slot(&self, slot: u8) -> Result<(), Error>; /// Erases a TOTP slot. @@ -145,7 +145,7 @@ pub trait ConfigureOtp { /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot fn erase_totp_slot(&self, slot: u8) -> Result<(), Error>; } @@ -216,7 +216,7 @@ pub trait GenerateOtp { /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed fn get_hotp_slot_name(&self, slot: u8) -> Result { unsafe { result_from_string(nitrokey_sys::NK_get_hotp_slot_name(slot)) } @@ -245,7 +245,7 @@ pub trait GenerateOtp { /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed fn get_totp_slot_name(&self, slot: u8) -> Result { unsafe { result_from_string(nitrokey_sys::NK_get_totp_slot_name(slot)) } @@ -275,7 +275,7 @@ pub trait GenerateOtp { /// ``` /// /// [`get_config`]: trait.Device.html#method.get_config - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot + /// [`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 { @@ -320,7 +320,7 @@ pub trait GenerateOtp { /// /// [`set_time`]: #method.set_time /// [`get_config`]: trait.Device.html#method.get_config - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot /// [`NotAuthorized`]: enum.CommandError.html#variant.NotAuthorized /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed fn get_totp_code(&self, slot: u8) -> Result { diff --git a/src/pws.rs b/src/pws.rs index e974737..47965d7 100644 --- a/src/pws.rs +++ b/src/pws.rs @@ -109,7 +109,7 @@ pub trait GetPasswordSafe { /// [`lock`]: trait.Device.html#method.lock /// [`AesDecryptionFailed`]: enum.CommandError.html#variant.AesDecryptionFailed /// [`Device::build_aes_key`]: trait.Device.html#method.build_aes_key - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`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, Error>; @@ -207,7 +207,7 @@ impl<'a> PasswordSafe<'a> { /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed pub fn get_slot_name(&self, slot: u8) -> Result { unsafe { result_from_string(nitrokey_sys::NK_get_password_safe_slot_name(slot)) } @@ -240,7 +240,7 @@ impl<'a> PasswordSafe<'a> { /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed pub fn get_slot_login(&self, slot: u8) -> Result { unsafe { result_from_string(nitrokey_sys::NK_get_password_safe_slot_login(slot)) } @@ -273,7 +273,7 @@ impl<'a> PasswordSafe<'a> { /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed pub fn get_slot_password(&self, slot: u8) -> Result { unsafe { result_from_string(nitrokey_sys::NK_get_password_safe_slot_password(slot)) } @@ -304,8 +304,8 @@ impl<'a> PasswordSafe<'a> { /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString pub fn write_slot( &self, slot: u8, @@ -350,7 +350,7 @@ impl<'a> PasswordSafe<'a> { /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot pub fn erase_slot(&self, slot: u8) -> Result<(), Error> { unsafe { get_command_result(nitrokey_sys::NK_erase_password_safe_slot(slot)) } } diff --git a/src/util.rs b/src/util.rs index 3b9904f..2738fce 100644 --- a/src/util.rs +++ b/src/util.rs @@ -5,7 +5,7 @@ use libc::{c_void, free}; use rand_core::RngCore; use rand_os::OsRng; -use crate::error::{CommandError, Error}; +use crate::error::{CommandError, Error, LibraryError}; /// Log level for libnitrokey. /// @@ -78,7 +78,7 @@ pub fn generate_password(length: usize) -> Result, Error> { } pub fn get_cstring>>(s: T) -> Result { - CString::new(s).or(Err(CommandError::InvalidString.into())) + CString::new(s).or(Err(LibraryError::InvalidString.into())) } impl Into for LogLevel { diff --git a/tests/device.rs b/tests/device.rs index 174624f..ee5dae1 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -6,7 +6,7 @@ use std::{thread, time}; use nitrokey::{ Authenticate, CommandError, Config, ConfigureOtp, Device, Error, GenerateOtp, GetPasswordSafe, - OtpMode, OtpSlotData, Storage, VolumeMode, + LibraryError, OtpMode, OtpSlotData, Storage, VolumeMode, }; use nitrokey_test::test as test_device; @@ -130,7 +130,7 @@ fn config(device: DeviceWrapper) { assert_eq!(config, get_config); let config = Config::new(None, Some(9), None, true); - assert_cmd_err!(CommandError::InvalidSlot, admin.write_config(config)); + assert_lib_err!(LibraryError::InvalidSlot, admin.write_config(config)); let config = Config::new(Some(1), None, Some(0), false); assert_ok!((), admin.write_config(config)); diff --git a/tests/otp.rs b/tests/otp.rs index d328351..51a6539 100644 --- a/tests/otp.rs +++ b/tests/otp.rs @@ -4,8 +4,8 @@ use std::fmt::Debug; use std::ops::Deref; use nitrokey::{ - Admin, Authenticate, CommandError, Config, ConfigureOtp, Device, GenerateOtp, OtpMode, - OtpSlotData, + Admin, Authenticate, CommandError, Config, ConfigureOtp, Device, GenerateOtp, LibraryError, + OtpMode, OtpSlotData, }; use nitrokey_test::test as test_device; @@ -106,7 +106,7 @@ fn hotp_slot_name(device: DeviceWrapper) { let result = device.get_hotp_slot_name(1); assert_eq!("test-hotp", result.unwrap()); let result = device.get_hotp_slot_name(4); - assert_cmd_err!(CommandError::InvalidSlot, result); + assert_lib_err!(LibraryError::InvalidSlot, result); } #[test_device] @@ -115,17 +115,17 @@ fn hotp_error(device: DeviceWrapper) { 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); - assert_cmd_err!( - CommandError::InvalidSlot, + assert_lib_err!( + LibraryError::InvalidSlot, admin.write_hotp_slot(slot_data, 0) ); let slot_data = OtpSlotData::new(1, "test", "foobar", OtpMode::SixDigits); - assert_cmd_err!( - CommandError::InvalidHexString, + assert_lib_err!( + LibraryError::InvalidHexString, admin.write_hotp_slot(slot_data, 0) ); let code = admin.get_hotp_code(4); - assert_cmd_err!(CommandError::InvalidSlot, code); + assert_lib_err!(LibraryError::InvalidSlot, code); } #[test_device] @@ -249,7 +249,7 @@ fn totp_slot_name(device: DeviceWrapper) { assert!(result.is_ok()); assert_eq!("test-totp", result.unwrap()); let result = device.get_totp_slot_name(16); - assert_cmd_err!(CommandError::InvalidSlot, result); + assert_lib_err!(LibraryError::InvalidSlot, result); } #[test_device] @@ -258,17 +258,17 @@ fn totp_error(device: DeviceWrapper) { 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); - assert_cmd_err!( - CommandError::InvalidSlot, + assert_lib_err!( + LibraryError::InvalidSlot, admin.write_totp_slot(slot_data, 0) ); let slot_data = OtpSlotData::new(4, "test", "foobar", OtpMode::SixDigits); - assert_cmd_err!( - CommandError::InvalidHexString, + assert_lib_err!( + LibraryError::InvalidHexString, admin.write_totp_slot(slot_data, 0) ); let code = admin.get_totp_code(20); - assert_cmd_err!(CommandError::InvalidSlot, code); + assert_lib_err!(LibraryError::InvalidSlot, code); } #[test_device] diff --git a/tests/pws.rs b/tests/pws.rs index a4647bd..51e6189 100644 --- a/tests/pws.rs +++ b/tests/pws.rs @@ -3,7 +3,9 @@ mod util; use std::ffi::CStr; use libc::{c_int, c_void, free}; -use nitrokey::{CommandError, Device, Error, GetPasswordSafe, PasswordSafe, SLOT_COUNT}; +use nitrokey::{ + CommandError, Device, Error, GetPasswordSafe, LibraryError, PasswordSafe, SLOT_COUNT, +}; use nitrokey_sys; use nitrokey_test::test as test_device; @@ -104,17 +106,17 @@ fn get_data(device: DeviceWrapper) { assert_eq!(login, pws.get_slot_login(1).unwrap()); assert_eq!(password, pws.get_slot_password(1).unwrap()); - assert_cmd_err!(CommandError::InvalidSlot, pws.get_slot_name(SLOT_COUNT)); - assert_cmd_err!(CommandError::InvalidSlot, pws.get_slot_login(SLOT_COUNT)); - assert_cmd_err!(CommandError::InvalidSlot, pws.get_slot_password(SLOT_COUNT)); + assert_lib_err!(LibraryError::InvalidSlot, pws.get_slot_name(SLOT_COUNT)); + assert_lib_err!(LibraryError::InvalidSlot, pws.get_slot_login(SLOT_COUNT)); + assert_lib_err!(LibraryError::InvalidSlot, pws.get_slot_password(SLOT_COUNT)); } #[test_device] fn write(device: DeviceWrapper) { let pws = get_pws(&device); - assert_cmd_err!( - CommandError::InvalidSlot, + assert_lib_err!( + LibraryError::InvalidSlot, pws.write_slot(SLOT_COUNT, "name", "login", "password") ); @@ -137,7 +139,7 @@ fn write(device: DeviceWrapper) { #[test_device] fn erase(device: DeviceWrapper) { let pws = get_pws(&device); - assert_cmd_err!(CommandError::InvalidSlot, pws.erase_slot(SLOT_COUNT)); + assert_lib_err!(LibraryError::InvalidSlot, pws.erase_slot(SLOT_COUNT)); assert_ok!((), pws.write_slot(0, "name", "login", "password")); assert_ok!((), pws.erase_slot(0)); diff --git a/tests/util/mod.rs b/tests/util/mod.rs index f0d0bb5..b1d3ea3 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -67,3 +67,10 @@ macro_rules! assert_cmd_err { assert_err!(::nitrokey::Error::CommandError, $left, $right); }; } + +#[macro_export] +macro_rules! assert_lib_err { + ($left:expr, $right:expr) => { + assert_err!(::nitrokey::Error::LibraryError, $left, $right); + }; +} -- cgit v1.2.3 From c191e875492ff8aeab1b4493b87486cd265f0edc Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 17 Jan 2019 13:38:28 +0000 Subject: Introduce the Error::UnexpectedError variant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The UnexpectedError variant is used when a libnitrokey function returns a value that violates the function’s contract, for example if a function returns a null pointer although it guarantees to never return null. Previously, we returned a CommandError::Unspecified in these cases. --- src/error.rs | 4 ++++ src/util.rs | 6 +++--- tests/pws.rs | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/src/error.rs b/src/error.rs index ef9b149..b27124c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -14,6 +14,8 @@ pub enum Error { LibraryError(LibraryError), /// An error that occured during random number generation. RandError(rand_core::Error), + /// An error that is caused by an unexpected value returned by libnitrokey. + UnexpectedError, /// An unknown error returned by libnitrokey. Unknown(i64), } @@ -63,6 +65,7 @@ impl error::Error for Error { Error::CommunicationError(ref err) => Some(err), Error::LibraryError(ref err) => Some(err), Error::RandError(ref err) => Some(err), + Error::UnexpectedError => None, Error::Unknown(_) => None, } } @@ -75,6 +78,7 @@ impl fmt::Display for Error { Error::CommunicationError(ref err) => write!(f, "Communication error: {}", err), Error::LibraryError(ref err) => write!(f, "Library error: {}", err), Error::RandError(ref err) => write!(f, "RNG error: {}", err), + Error::UnexpectedError => write!(f, "An unexpected error occurred"), Error::Unknown(ref err) => write!(f, "Unknown error: {}", err), } } diff --git a/src/util.rs b/src/util.rs index 2738fce..79b8c34 100644 --- a/src/util.rs +++ b/src/util.rs @@ -5,7 +5,7 @@ use libc::{c_void, free}; use rand_core::RngCore; use rand_os::OsRng; -use crate::error::{CommandError, Error, LibraryError}; +use crate::error::{Error, LibraryError}; /// Log level for libnitrokey. /// @@ -36,7 +36,7 @@ pub fn owned_str_from_ptr(ptr: *const c_char) -> String { pub fn result_from_string(ptr: *const c_char) -> Result { if ptr.is_null() { - return Err(CommandError::Undefined.into()); + return Err(Error::UnexpectedError); } unsafe { let s = owned_str_from_ptr(ptr); @@ -65,7 +65,7 @@ pub fn get_last_result() -> Result<(), Error> { pub fn get_last_error() -> Error { return match get_last_result() { - Ok(()) => CommandError::Undefined.into(), + Ok(()) => Error::UnexpectedError, Err(err) => err, }; } diff --git a/tests/pws.rs b/tests/pws.rs index 51e6189..b89d7f6 100644 --- a/tests/pws.rs +++ b/tests/pws.rs @@ -14,7 +14,7 @@ use crate::util::{ADMIN_PASSWORD, USER_PASSWORD}; fn get_slot_name_direct(slot: u8) -> Result { let ptr = unsafe { nitrokey_sys::NK_get_password_safe_slot_name(slot) }; if ptr.is_null() { - return Err(CommandError::Undefined.into()); + return Err(Error::UnexpectedError); } let s = unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() }; unsafe { free(ptr as *mut c_void) }; -- cgit v1.2.3 From d87859975dc158919ecd5bf11a1111a2da5fcb30 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 17 Jan 2019 14:21:44 +0000 Subject: Check specific error codes in the tests If possible, check specific error codes instead of `is_err()`. This makes the code more readable and catches bugs resulting in the wrong error code. Also, using the assert_*_err and assert_ok macros yields error messages containing the expected and the actual value. To be able to use these macros with the `get_password_safe` method, we also have to implement `Debug` for `PasswordSafe` and `Device`. --- TODO.md | 1 - src/device.rs | 2 +- src/pws.rs | 1 + tests/device.rs | 46 +++++++++++++++------------------------------- tests/otp.rs | 20 ++++++-------------- tests/pws.rs | 6 ++---- tests/util/mod.rs | 7 +++++++ 7 files changed, 32 insertions(+), 51 deletions(-) (limited to 'tests') diff --git a/TODO.md b/TODO.md index 53de7e9..487f56d 100644 --- a/TODO.md +++ b/TODO.md @@ -9,7 +9,6 @@ - Clear passwords from memory. - Find a nicer syntax for the `write_config` test. - Prevent construction of internal types. -- More specific error checking in the tests. - Check integer conversions. - Consider implementing `Into` for `(Device, CommandError)` - Lock password safe in `PasswordSafe::drop()` (see [nitrokey-storage-firmware diff --git a/src/device.rs b/src/device.rs index 1cf9da9..16064c3 100644 --- a/src/device.rs +++ b/src/device.rs @@ -286,7 +286,7 @@ pub struct StorageStatus { /// /// This trait provides the commands that can be executed without authentication and that are /// present on all supported Nitrokey devices. -pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { +pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// Returns the model of the connected Nitrokey device. /// /// # Example diff --git a/src/pws.rs b/src/pws.rs index 47965d7..a21527c 100644 --- a/src/pws.rs +++ b/src/pws.rs @@ -52,6 +52,7 @@ pub const SLOT_COUNT: u8 = 16; /// [`get_password_safe`]: trait.GetPasswordSafe.html#method.get_password_safe /// [`lock`]: trait.Device.html#method.lock /// [`GetPasswordSafe`]: trait.GetPasswordSafe.html +#[derive(Debug)] pub struct PasswordSafe<'a> { _device: &'a dyn Device, } diff --git a/tests/device.rs b/tests/device.rs index ee5dae1..c502945 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -5,8 +5,8 @@ use std::process::Command; use std::{thread, time}; use nitrokey::{ - Authenticate, CommandError, Config, ConfigureOtp, Device, Error, GenerateOtp, GetPasswordSafe, - LibraryError, OtpMode, OtpSlotData, Storage, VolumeMode, + Authenticate, CommandError, CommunicationError, Config, ConfigureOtp, Device, Error, + GenerateOtp, GetPasswordSafe, LibraryError, OtpMode, OtpSlotData, Storage, VolumeMode, }; use nitrokey_test::test as test_device; @@ -31,11 +31,11 @@ fn count_nitrokey_block_devices() -> usize { #[test_device] fn connect_no_device() { - assert!(nitrokey::connect().is_err()); - assert!(nitrokey::connect_model(nitrokey::Model::Pro).is_err()); - assert!(nitrokey::connect_model(nitrokey::Model::Storage).is_err()); - assert!(nitrokey::Pro::connect().is_err()); - assert!(nitrokey::Storage::connect().is_err()); + assert_cmu_err!(CommunicationError::NotConnected, nitrokey::connect()); + assert_cmu_err!(CommunicationError::NotConnected, nitrokey::connect_model(nitrokey::Model::Pro)); + assert_cmu_err!(CommunicationError::NotConnected, nitrokey::connect_model(nitrokey::Model::Storage)); + assert_cmu_err!(CommunicationError::NotConnected, nitrokey::Pro::connect()); + assert_cmu_err!(CommunicationError::NotConnected, nitrokey::Storage::connect()); } #[test_device] @@ -148,9 +148,7 @@ fn change_user_pin(device: DeviceWrapper) { let device = device.authenticate_user(USER_PASSWORD).unwrap().device(); let device = device.authenticate_user(USER_NEW_PASSWORD).unwrap_err().0; - assert!(device - .change_user_pin(USER_PASSWORD, USER_NEW_PASSWORD) - .is_ok()); + assert_ok!((), device.change_user_pin(USER_PASSWORD, USER_NEW_PASSWORD)); let device = device.authenticate_user(USER_PASSWORD).unwrap_err().0; let device = device @@ -161,9 +159,7 @@ fn change_user_pin(device: DeviceWrapper) { let result = device.change_user_pin(USER_PASSWORD, USER_PASSWORD); assert_cmd_err!(CommandError::WrongPassword, result); - assert!(device - .change_user_pin(USER_NEW_PASSWORD, USER_PASSWORD) - .is_ok()); + assert_ok!((), device.change_user_pin(USER_NEW_PASSWORD, USER_PASSWORD)); let device = device.authenticate_user(USER_PASSWORD).unwrap().device(); assert!(device.authenticate_user(USER_NEW_PASSWORD).is_err()); @@ -174,9 +170,7 @@ 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; - assert!(device - .change_admin_pin(ADMIN_PASSWORD, ADMIN_NEW_PASSWORD) - .is_ok()); + assert_ok!((), device.change_admin_pin(ADMIN_PASSWORD, ADMIN_NEW_PASSWORD)); let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap_err().0; let device = device @@ -189,9 +183,7 @@ fn change_admin_pin(device: DeviceWrapper) { device.change_admin_pin(ADMIN_PASSWORD, ADMIN_PASSWORD) ); - assert!(device - .change_admin_pin(ADMIN_NEW_PASSWORD, ADMIN_PASSWORD) - .is_ok()); + assert_ok!((), device.change_admin_pin(ADMIN_NEW_PASSWORD, ADMIN_PASSWORD)); let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap().device(); device.authenticate_admin(ADMIN_NEW_PASSWORD).unwrap_err(); @@ -215,9 +207,7 @@ where #[test_device] fn unlock_user_pin(device: DeviceWrapper) { let device = device.authenticate_user(USER_PASSWORD).unwrap().device(); - assert!(device - .unlock_user_pin(ADMIN_PASSWORD, USER_PASSWORD) - .is_ok()); + assert_ok!((), device.unlock_user_pin(ADMIN_PASSWORD, USER_PASSWORD)); assert_cmd_err!( CommandError::WrongPassword, device.unlock_user_pin(USER_PASSWORD, USER_PASSWORD) @@ -235,9 +225,7 @@ fn unlock_user_pin(device: DeviceWrapper) { CommandError::WrongPassword, device.unlock_user_pin(USER_PASSWORD, USER_PASSWORD) ); - assert!(device - .unlock_user_pin(ADMIN_PASSWORD, USER_PASSWORD) - .is_ok()); + assert_ok!((), device.unlock_user_pin(ADMIN_PASSWORD, USER_PASSWORD)); let device = device.authenticate_user(USER_PASSWORD).unwrap().device(); // block user PIN @@ -251,14 +239,10 @@ fn unlock_user_pin(device: DeviceWrapper) { CommandError::WrongPassword, device.unlock_user_pin(USER_PASSWORD, USER_PASSWORD) ); - assert!(device - .unlock_user_pin(ADMIN_PASSWORD, USER_NEW_PASSWORD) - .is_ok()); + assert_ok!((), device.unlock_user_pin(ADMIN_PASSWORD, USER_NEW_PASSWORD)); // reset user PIN - assert!(device - .change_user_pin(USER_NEW_PASSWORD, USER_PASSWORD) - .is_ok()); + assert_ok!((), device.change_user_pin(USER_NEW_PASSWORD, USER_PASSWORD)); } #[test_device] diff --git a/tests/otp.rs b/tests/otp.rs index 51a6539..96da371 100644 --- a/tests/otp.rs +++ b/tests/otp.rs @@ -93,7 +93,7 @@ fn hotp_pin(device: DeviceWrapper) { let user = admin.device().authenticate_user(USER_PASSWORD).unwrap(); check_hotp_codes(&user, 0); - assert!(user.device().get_hotp_code(1).is_err()); + assert_cmd_err!(CommandError::NotAuthorized, user.device().get_hotp_code(1)); } #[test_device] @@ -156,7 +156,7 @@ fn configure_totp(admin: &ConfigureOtp, factor: u64) { } fn check_totp_codes(device: &GenerateOtp, factor: u64, timestamp_size: TotpTimestampSize) { - for (i, &(base_time, code)) in TOTP_CODES.iter().enumerate() { + for (base_time, code) in TOTP_CODES { let time = base_time.checked_mul(factor).unwrap(); let is_u64 = time > u32::max_value() as u64; if is_u64 != (timestamp_size == TotpTimestampSize::U64) { @@ -164,14 +164,7 @@ fn check_totp_codes(device: &GenerateOtp, factor: u64, timestamp_size: TotpTimes } assert_ok!((), device.set_time(time, true)); - let result = device.get_totp_code(1); - assert!(result.is_ok()); - let result_code = result.unwrap(); - assert_eq!( - code, result_code, - "TOTP code {} should be {} but is {}", - i, code, result_code - ); + assert_ok!(code.to_string(), device.get_totp_code(1)); } } @@ -221,7 +214,7 @@ fn totp_pin(device: DeviceWrapper) { let user = admin.device().authenticate_user(USER_PASSWORD).unwrap(); check_totp_codes(&user, 1, TotpTimestampSize::U32); - assert!(user.device().get_totp_code(1).is_err()); + assert_cmd_err!(CommandError::NotAuthorized, user.device().get_totp_code(1)); } #[test_device] @@ -235,7 +228,7 @@ fn totp_pin_64(device: Pro) { let user = admin.device().authenticate_user(USER_PASSWORD).unwrap(); check_totp_codes(&user, 1, TotpTimestampSize::U64); - assert!(user.device().get_totp_code(1).is_err()); + assert_cmd_err!(CommandError::NotAuthorized, user.device().get_totp_code(1)); } #[test_device] @@ -246,8 +239,7 @@ fn totp_slot_name(device: DeviceWrapper) { let device = admin.device(); let result = device.get_totp_slot_name(1); - assert!(result.is_ok()); - assert_eq!("test-totp", result.unwrap()); + assert_ok!("test-totp", result); let result = device.get_totp_slot_name(16); assert_lib_err!(LibraryError::InvalidSlot, result); } diff --git a/tests/pws.rs b/tests/pws.rs index b89d7f6..7a97983 100644 --- a/tests/pws.rs +++ b/tests/pws.rs @@ -39,11 +39,9 @@ where #[test_device] fn enable(device: DeviceWrapper) { - assert!(device - .get_password_safe(&(USER_PASSWORD.to_owned() + "123")) - .is_err()); + assert_cmd_err!(CommandError::WrongPassword, device.get_password_safe(&(USER_PASSWORD.to_owned() + "123"))); assert!(device.get_password_safe(USER_PASSWORD).is_ok()); - assert!(device.get_password_safe(ADMIN_PASSWORD).is_err()); + assert_cmd_err!(CommandError::WrongPassword, device.get_password_safe(ADMIN_PASSWORD)); assert!(device.get_password_safe(USER_PASSWORD).is_ok()); } diff --git a/tests/util/mod.rs b/tests/util/mod.rs index b1d3ea3..4a00a66 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -68,6 +68,13 @@ macro_rules! assert_cmd_err { }; } +#[macro_export] +macro_rules! assert_cmu_err { + ($left:expr, $right:expr) => { + assert_err!(::nitrokey::Error::CommunicationError, $left, $right); + }; +} + #[macro_export] macro_rules! assert_lib_err { ($left:expr, $right:expr) => { -- cgit v1.2.3 From 07bb11fec9de6579ffaa8d128796f242f818292f Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 20 Jan 2019 21:35:19 +0000 Subject: Fix formatting in tests --- tests/device.rs | 30 ++++++++++++++++++++++++------ tests/pws.rs | 10 ++++++++-- 2 files changed, 32 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/tests/device.rs b/tests/device.rs index c502945..0431f8b 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -32,10 +32,19 @@ fn count_nitrokey_block_devices() -> usize { #[test_device] fn connect_no_device() { assert_cmu_err!(CommunicationError::NotConnected, nitrokey::connect()); - assert_cmu_err!(CommunicationError::NotConnected, nitrokey::connect_model(nitrokey::Model::Pro)); - assert_cmu_err!(CommunicationError::NotConnected, nitrokey::connect_model(nitrokey::Model::Storage)); + assert_cmu_err!( + CommunicationError::NotConnected, + nitrokey::connect_model(nitrokey::Model::Pro) + ); + assert_cmu_err!( + CommunicationError::NotConnected, + nitrokey::connect_model(nitrokey::Model::Storage) + ); assert_cmu_err!(CommunicationError::NotConnected, nitrokey::Pro::connect()); - assert_cmu_err!(CommunicationError::NotConnected, nitrokey::Storage::connect()); + assert_cmu_err!( + CommunicationError::NotConnected, + nitrokey::Storage::connect() + ); } #[test_device] @@ -170,7 +179,10 @@ 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; - assert_ok!((), device.change_admin_pin(ADMIN_PASSWORD, ADMIN_NEW_PASSWORD)); + assert_ok!( + (), + device.change_admin_pin(ADMIN_PASSWORD, ADMIN_NEW_PASSWORD) + ); let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap_err().0; let device = device @@ -183,7 +195,10 @@ fn change_admin_pin(device: DeviceWrapper) { device.change_admin_pin(ADMIN_PASSWORD, ADMIN_PASSWORD) ); - assert_ok!((), device.change_admin_pin(ADMIN_NEW_PASSWORD, ADMIN_PASSWORD)); + assert_ok!( + (), + device.change_admin_pin(ADMIN_NEW_PASSWORD, ADMIN_PASSWORD) + ); let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap().device(); device.authenticate_admin(ADMIN_NEW_PASSWORD).unwrap_err(); @@ -239,7 +254,10 @@ fn unlock_user_pin(device: DeviceWrapper) { CommandError::WrongPassword, device.unlock_user_pin(USER_PASSWORD, USER_PASSWORD) ); - assert_ok!((), device.unlock_user_pin(ADMIN_PASSWORD, USER_NEW_PASSWORD)); + assert_ok!( + (), + device.unlock_user_pin(ADMIN_PASSWORD, USER_NEW_PASSWORD) + ); // reset user PIN assert_ok!((), device.change_user_pin(USER_NEW_PASSWORD, USER_PASSWORD)); diff --git a/tests/pws.rs b/tests/pws.rs index 7a97983..8bdf532 100644 --- a/tests/pws.rs +++ b/tests/pws.rs @@ -39,9 +39,15 @@ where #[test_device] fn enable(device: DeviceWrapper) { - assert_cmd_err!(CommandError::WrongPassword, device.get_password_safe(&(USER_PASSWORD.to_owned() + "123"))); + assert_cmd_err!( + CommandError::WrongPassword, + device.get_password_safe(&(USER_PASSWORD.to_owned() + "123")) + ); assert!(device.get_password_safe(USER_PASSWORD).is_ok()); - assert_cmd_err!(CommandError::WrongPassword, device.get_password_safe(ADMIN_PASSWORD)); + assert_cmd_err!( + CommandError::WrongPassword, + device.get_password_safe(ADMIN_PASSWORD) + ); assert!(device.get_password_safe(USER_PASSWORD).is_ok()); } -- cgit v1.2.3 From d4663961c41a0fb6f81f4a54aefd0fedce49d350 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Wed, 23 Jan 2019 04:27:14 +0000 Subject: Return UTF-8 error if libnitrokey returns an invalid string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, we used lossy UTF-8 conversion. Yet the user should be notified if we have a problem instead of silently changing the data. Therefore, we now return an error if we enocunter an invalid UTF-8 string. This leads to a change in `get_library_version`’s signature. --- CHANGELOG.md | 2 ++ src/lib.rs | 17 +++++++++++++---- src/util.rs | 9 +++++---- tests/lib.rs | 2 +- 4 files changed, 21 insertions(+), 9 deletions(-) (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index c800521..70bd7cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ - Add `Pro::new` and `Storage::new` functions. - Implement `From` and `From` for `DeviceWrapper`. - Add `Error::Utf8Error` variant. + - Return `Result` instead of `Version` from `get_library_version`. + - Return `Error::Utf8Error` if libnitrokey returns an invalid UTF-8 string. # v0.3.4 (2019-01-20) - Fix authentication methods that assumed that `char` is signed. diff --git a/src/lib.rs b/src/lib.rs index 993ec92..a1edb6b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -168,21 +168,30 @@ pub fn set_log_level(level: LogLevel) { /// Returns the libnitrokey library version. /// +/// # Errors +/// +/// - [`Utf8Error`][] if libnitrokey returned an invalid UTF-8 string +/// /// # Example /// /// ``` -/// let version = nitrokey::get_library_version(); +/// # fn main() -> Result<(), nitrokey::Error> { +/// let version = nitrokey::get_library_version()?; /// println!("Using libnitrokey {}", version.git); +/// # Ok(()) +/// # } /// ``` -pub fn get_library_version() -> Version { +/// +/// [`Utf8Error`]: enum.Error.html#variant.Utf8Error +pub fn get_library_version() -> Result { // NK_get_library_version returns a static string, so we don’t have to free the pointer. let git = unsafe { nitrokey_sys::NK_get_library_version() }; let git = if git.is_null() { String::new() } else { - util::owned_str_from_ptr(git) + util::owned_str_from_ptr(git)? }; let major = unsafe { nitrokey_sys::NK_get_major_library_version() }; let minor = unsafe { nitrokey_sys::NK_get_minor_library_version() }; - Version { git, major, minor } + Ok(Version { git, major, minor }) } diff --git a/src/util.rs b/src/util.rs index f8ad9c9..64dde39 100644 --- a/src/util.rs +++ b/src/util.rs @@ -28,17 +28,18 @@ pub enum LogLevel { DebugL2, } -pub fn owned_str_from_ptr(ptr: *const c_char) -> String { +pub fn owned_str_from_ptr(ptr: *const c_char) -> Result { unsafe { CStr::from_ptr(ptr) } - .to_string_lossy() - .into_owned() + .to_str() + .map(String::from) + .map_err(Error::from) } pub fn result_from_string(ptr: *const c_char) -> Result { if ptr.is_null() { return Err(Error::UnexpectedError); } - let s = owned_str_from_ptr(ptr); + let s = owned_str_from_ptr(ptr)?; unsafe { free(ptr as *mut c_void) }; // An empty string can both indicate an error or be a valid return value. In this case, we // have to check the last command status to decide what to return. diff --git a/tests/lib.rs b/tests/lib.rs index c92e224..d298048 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,6 +1,6 @@ #[test] fn get_library_version() { - let version = nitrokey::get_library_version(); + let version = nitrokey::get_library_version().unwrap(); assert!(version.git.is_empty() || version.git.starts_with("v")); assert!(version.major > 0); -- cgit v1.2.3 From 35fd6be074cd16796f701770845ade471e2c13bd Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Wed, 23 Jan 2019 04:56:43 +0000 Subject: Refactor device::config test case --- TODO.md | 1 - tests/device.rs | 10 ++++------ 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/TODO.md b/TODO.md index 49e4e08..d3f0018 100644 --- a/TODO.md +++ b/TODO.md @@ -7,7 +7,6 @@ - `NK_connect_with_ID` - Fix timing issues with the `totp_no_pin` and `totp_pin` test cases. - Clear passwords from memory. -- Find a nicer syntax for the `write_config` test. - Lock password safe in `PasswordSafe::drop()` (see [nitrokey-storage-firmware issue 65][]). - Disable creation of multiple password safes at the same time. diff --git a/tests/device.rs b/tests/device.rs index 0431f8b..59c9348 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -133,23 +133,21 @@ fn get_retry_count(device: DeviceWrapper) { #[test_device] fn config(device: DeviceWrapper) { let admin = device.authenticate_admin(ADMIN_PASSWORD).unwrap(); + let config = Config::new(None, None, None, true); assert_ok!((), admin.write_config(config)); - let get_config = admin.get_config().unwrap(); - assert_eq!(config, get_config); + assert_ok!(config, admin.get_config()); let config = Config::new(None, Some(9), None, true); assert_lib_err!(LibraryError::InvalidSlot, admin.write_config(config)); let config = Config::new(Some(1), None, Some(0), false); assert_ok!((), admin.write_config(config)); - let get_config = admin.get_config().unwrap(); - assert_eq!(config, get_config); + assert_ok!(config, admin.get_config()); let config = Config::new(None, None, None, false); assert_ok!((), admin.write_config(config)); - let get_config = admin.get_config().unwrap(); - assert_eq!(config, get_config); + assert_ok!(config, admin.get_config()); } #[test_device] -- cgit v1.2.3 From fdb7bac3063e62776bfc13f184cf786da19f42d1 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Wed, 23 Jan 2019 16:33:26 +0100 Subject: Add license and copyright information This patch adds license and copyright information to all files to make nitrokey-rs compliant with the REUSE practices [0]. [0] https://reuse.software/practices/2.0/ --- .builds/archlinux-use-system-lib.yaml | 2 ++ .builds/archlinux.yml | 2 ++ CHANGELOG.md | 5 +++++ Cargo.toml | 3 +++ LICENSE | 3 +++ README.md | 8 ++++++++ TODO.md | 5 +++++ src/auth.rs | 3 +++ src/config.rs | 3 +++ src/device.rs | 3 +++ src/error.rs | 3 +++ src/lib.rs | 3 +++ src/otp.rs | 3 +++ src/pws.rs | 3 +++ src/util.rs | 3 +++ tests/device.rs | 3 +++ tests/lib.rs | 3 +++ tests/otp.rs | 3 +++ tests/pws.rs | 3 +++ tests/util/mod.rs | 3 +++ 20 files changed, 67 insertions(+) (limited to 'tests') diff --git a/.builds/archlinux-use-system-lib.yaml b/.builds/archlinux-use-system-lib.yaml index 6fba33a..13f7581 100644 --- a/.builds/archlinux-use-system-lib.yaml +++ b/.builds/archlinux-use-system-lib.yaml @@ -1,3 +1,5 @@ +# Copyright (C) 2019 Robin Krahl +# SPDX-License-Identifier: MIT image: archlinux packages: - rust diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml index 9d45386..7dab954 100644 --- a/.builds/archlinux.yml +++ b/.builds/archlinux.yml @@ -1,3 +1,5 @@ +# Copyright (C) 2019 Robin Krahl +# SPDX-License-Identifier: MIT image: archlinux packages: - rust diff --git a/CHANGELOG.md b/CHANGELOG.md index c28c228..49ff8fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ + + # Unreleased - Remove the `test-pro` and `test-storage` features. - Implement `Display` for `Version`. diff --git a/Cargo.toml b/Cargo.toml index ceaa57d..696adfd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,6 @@ +# Copyright (C) 2019 Robin Krahl +# SPDX-License-Identifier: MIT + [package] name = "nitrokey" version = "0.3.4" diff --git a/LICENSE b/LICENSE index 1a3601d..6c67cd5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,3 +1,6 @@ +Valid-License-Identifier: MIT +License-Text: + The MIT License (MIT) Copyright (c) 2018 Robin Krahl diff --git a/README.md b/README.md index 0819c9d..8e1d98e 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ + + # nitrokey-rs A libnitrokey wrapper for Rust providing access to Nitrokey devices. @@ -73,6 +78,8 @@ mail to [nitrokey-rs-dev@ireas.org][]. This project is licensed under the [MIT License][]. `libnitrokey` is licensed under the [LGPL-3.0][]. +`nitrokey-rs` complies with [version 2.0 of the REUSE practices][reuse]. + [Documentation]: https://docs.rs/nitrokey [Nitrokey udev rules]: https://www.nitrokey.com/documentation/frequently-asked-questions-faq#openpgp-card-not-available [`libnitrokey`]: https://github.com/nitrokey/libnitrokey @@ -81,3 +88,4 @@ under the [LGPL-3.0][]. [pull request #114]: https://github.com/Nitrokey/libnitrokey/pull/114 [MIT license]: https://opensource.org/licenses/MIT [LGPL-3.0]: https://opensource.org/licenses/lgpl-3.0.html +[reuse]: https://reuse.software/practices/2.0/ diff --git a/TODO.md b/TODO.md index d3f0018..db45bb5 100644 --- a/TODO.md +++ b/TODO.md @@ -1,3 +1,8 @@ + + - Add support for the currently unsupported commands: - `NK_send_startup` - `NK_fill_SD_card_with_random_data` diff --git a/src/auth.rs b/src/auth.rs index b97bee6..18b6572 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2018-2019 Robin Krahl +// SPDX-License-Identifier: MIT + use std::ops::Deref; use std::os::raw::c_char; use std::os::raw::c_int; diff --git a/src/config.rs b/src/config.rs index 329f7a6..c273792 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2018-2019 Robin Krahl +// SPDX-License-Identifier: MIT + use crate::error::{Error, LibraryError}; /// The configuration for a Nitrokey. diff --git a/src/device.rs b/src/device.rs index ad75a44..c4af8a8 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2018-2019 Robin Krahl +// SPDX-License-Identifier: MIT + use std::fmt; use std::marker; diff --git a/src/error.rs b/src/error.rs index 551dd0f..9cdb932 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2019 Robin Krahl +// SPDX-License-Identifier: MIT + use std::error; use std::fmt; use std::os::raw; diff --git a/src/lib.rs b/src/lib.rs index a1edb6b..9d15d03 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2018-2019 Robin Krahl +// SPDX-License-Identifier: MIT + //! Provides access to a Nitrokey device using the native libnitrokey API. //! //! # Usage diff --git a/src/otp.rs b/src/otp.rs index 430b127..6e0379b 100644 --- a/src/otp.rs +++ b/src/otp.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2018-2019 Robin Krahl +// SPDX-License-Identifier: MIT + use std::ffi::CString; use nitrokey_sys; diff --git a/src/pws.rs b/src/pws.rs index c89b73f..fcf057b 100644 --- a/src/pws.rs +++ b/src/pws.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2018-2019 Robin Krahl +// SPDX-License-Identifier: MIT + use libc; use nitrokey_sys; diff --git a/src/util.rs b/src/util.rs index 64dde39..5f25655 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2018-2019 Robin Krahl +// SPDX-License-Identifier: MIT + use std::ffi::{CStr, CString}; use std::os::raw::{c_char, c_int}; diff --git a/tests/device.rs b/tests/device.rs index 59c9348..c790049 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2018-2019 Robin Krahl +// SPDX-License-Identifier: MIT + mod util; use std::ffi::CStr; diff --git a/tests/lib.rs b/tests/lib.rs index d298048..697024d 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2019 Robin Krahl +// SPDX-License-Identifier: MIT + #[test] fn get_library_version() { let version = nitrokey::get_library_version().unwrap(); diff --git a/tests/otp.rs b/tests/otp.rs index 96da371..fb20768 100644 --- a/tests/otp.rs +++ b/tests/otp.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2018-2019 Robin Krahl +// SPDX-License-Identifier: MIT + mod util; use std::fmt::Debug; diff --git a/tests/pws.rs b/tests/pws.rs index 8bdf532..df99e1c 100644 --- a/tests/pws.rs +++ b/tests/pws.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2018-2019 Robin Krahl +// SPDX-License-Identifier: MIT + mod util; use std::ffi::CStr; diff --git a/tests/util/mod.rs b/tests/util/mod.rs index 4a00a66..49ec13e 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2018-2019 Robin Krahl +// SPDX-License-Identifier: MIT + pub static ADMIN_PASSWORD: &str = "12345678"; pub static USER_PASSWORD: &str = "123456"; -- cgit v1.2.3 From 6dfc1a2929313e24ea03e78b486b72f7b1c1e5ec Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Fri, 25 Jan 2019 19:19:36 +0000 Subject: Add tolerance for timing issues to the TOTP tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TOTP test with the timestamp 59 often fails as the Nitrokey’s clock ticks between setting the time and generating the TOTP code. This patch also allows the TOTP code for timestamp 60 for this test case. --- CHANGELOG.md | 1 + README.md | 2 -- TODO.md | 1 - tests/otp.rs | 25 ++++++++++++++++--------- 4 files changed, 17 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 49ff8fc..b9983ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ SPDX-License-Identifier: MIT - Return `Result` instead of `Version` from `get_library_version`. - 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. # v0.3.4 (2019-01-20) - Fix authentication methods that assumed that `char` is signed. diff --git a/README.md b/README.md index 8e1d98e..069fed1 100644 --- a/README.md +++ b/README.md @@ -60,8 +60,6 @@ an AES key has been built. Some tests will overwrite the data stored on the Nitrokey device or perform a factory reset. Never execute the tests if you unless yout want to destroy all data on all connected Nitrokey devices! -The `totp_no_pin` and `totp_pin` tests can occasionally fail due to bad timing. - ## Acknowledgments Thanks to Nitrokey UG for providing a Nitrokey Storage to support the diff --git a/TODO.md b/TODO.md index db45bb5..1ff723d 100644 --- a/TODO.md +++ b/TODO.md @@ -10,7 +10,6 @@ SPDX-License-Identifier: MIT - `NK_get_progress_bar_value` - `NK_list_devices_by_cpuID` - `NK_connect_with_ID` -- Fix timing issues with the `totp_no_pin` and `totp_pin` test cases. - Clear passwords from memory. - Lock password safe in `PasswordSafe::drop()` (see [nitrokey-storage-firmware issue 65][]). diff --git a/tests/otp.rs b/tests/otp.rs index fb20768..e424673 100644 --- a/tests/otp.rs +++ b/tests/otp.rs @@ -23,13 +23,13 @@ static HOTP_CODES: &[&str] = &[ // test suite according to RFC 6238, Appendix B static TOTP_SECRET: &str = "3132333435363738393031323334353637383930"; -static TOTP_CODES: &[(u64, &str)] = &[ - (59, "94287082"), - (1111111109, "07081804"), - (1111111111, "14050471"), - (1234567890, "89005924"), - (2000000000, "69279037"), - (20000000000, "65353130"), +static TOTP_CODES: &[(u64, &[&str])] = &[ + (59, &["94287082", "37359152"]), + (1111111109, &["07081804"]), + (1111111111, &["14050471"]), + (1234567890, &["89005924"]), + (2000000000, &["69279037"]), + (20000000000, &["65353130"]), ]; #[derive(PartialEq)] @@ -159,7 +159,7 @@ fn configure_totp(admin: &ConfigureOtp, factor: u64) { } fn check_totp_codes(device: &GenerateOtp, factor: u64, timestamp_size: TotpTimestampSize) { - for (base_time, code) in TOTP_CODES { + for (base_time, codes) in TOTP_CODES { let time = base_time.checked_mul(factor).unwrap(); let is_u64 = time > u32::max_value() as u64; if is_u64 != (timestamp_size == TotpTimestampSize::U64) { @@ -167,7 +167,14 @@ fn check_totp_codes(device: &GenerateOtp, factor: u64, timestamp_size: TotpTimes } assert_ok!((), device.set_time(time, true)); - assert_ok!(code.to_string(), device.get_totp_code(1)); + let code = device.get_totp_code(1).unwrap(); + assert!( + code.contains(&code), + "Generated TOTP code {} for {}, but expected one of {}", + code, + base_time, + codes.join(", ") + ); } } -- cgit v1.2.3 From c30cbd35ba187cd6e5055d3beb8420b11fb030ec Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 27 Jan 2019 23:23:00 +0000 Subject: Always return a Result when communicating with a device Previously, we sometimes returned a value without wrapping it in a result if the API method did not indicate errors in the return value. But we can detect errors using the NK_get_last_command_status function. This patch changes the return types of these methods to Result<_, Error> and adds error checks. --- CHANGELOG.md | 1 + src/device.rs | 39 +++++++++++++++++++++++---------------- src/util.rs | 4 ++++ tests/device.rs | 9 +++++---- 4 files changed, 33 insertions(+), 20 deletions(-) (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 24c79af..25a8c31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ 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. # v0.3.4 (2019-01-20) - Fix authentication methods that assumed that `char` is signed. diff --git a/src/device.rs b/src/device.rs index 386ce94..4178922 100644 --- a/src/device.rs +++ b/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)] @@ -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) => println!("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 { + 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,12 +368,15 @@ 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) => println!("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 { + result_or_error(unsafe { nitrokey_sys::NK_get_admin_retry_count() }) } /// Returns the major part of the firmware version (should be zero). @@ -384,14 +391,14 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// let device = nitrokey::connect()?; /// println!( /// "Firmware version: {}.{}", - /// device.get_major_firmware_version(), - /// device.get_minor_firmware_version(), + /// device.get_major_firmware_version().unwrap(), + /// device.get_minor_firmware_version().unwrap(), /// ); /// # Ok(()) /// # } /// ``` - fn get_major_firmware_version(&self) -> i32 { - unsafe { nitrokey_sys::NK_get_major_firmware_version() } + fn get_major_firmware_version(&self) -> Result { + result_or_error(unsafe { nitrokey_sys::NK_get_major_firmware_version() }) } /// Returns the minor part of the firmware version (for example 8 for version 0.8). @@ -406,13 +413,13 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// let device = nitrokey::connect()?; /// println!( /// "Firmware version: {}.{}", - /// device.get_major_firmware_version(), - /// device.get_minor_firmware_version(), + /// device.get_major_firmware_version().unwrap(), + /// device.get_minor_firmware_version().unwrap(), /// ); /// # Ok(()) /// # } - fn get_minor_firmware_version(&self) -> i32 { - unsafe { nitrokey_sys::NK_get_minor_firmware_version() } + fn get_minor_firmware_version(&self) -> Result { + result_or_error(unsafe { nitrokey_sys::NK_get_minor_firmware_version() }) } /// Returns the current configuration of the Nitrokey device. diff --git a/src/util.rs b/src/util.rs index b7e8cd3..fdb73c3 100644 --- a/src/util.rs +++ b/src/util.rs @@ -53,6 +53,10 @@ pub fn result_from_string(ptr: *const c_char) -> Result { } } +pub fn result_or_error(value: T) -> Result { + get_last_result().and(Ok(value)) +} + pub fn get_command_result(value: c_int) -> Result<(), Error> { if value == 0 { Ok(()) diff --git a/tests/device.rs b/tests/device.rs index c790049..7ab4d66 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -95,9 +95,10 @@ fn get_serial_number(device: DeviceWrapper) { } #[test_device] fn get_firmware_version(device: Pro) { - assert_eq!(0, device.get_major_firmware_version()); + assert_ok!(0, device.get_major_firmware_version()); let minor = device.get_minor_firmware_version(); - assert!(minor > 0); + assert!(minor.is_ok()); + assert!(minor.unwrap() > 0); } fn admin_retry(device: T, suffix: &str, count: u8) -> T { @@ -106,7 +107,7 @@ fn admin_retry(device: T, suffix: &str, count: u8) -> 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; } @@ -116,7 +117,7 @@ fn user_retry(device: T, suffix: &str, count: u8) -> T 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; } -- cgit v1.2.3 From 1d68e24db4078ad1a004afd7bec90a81e7d31ec8 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 27 Jan 2019 23:34:04 +0000 Subject: Add get_firmware_version method This patch combines the get_{major,minor}_firmware_version methods into the new get_firmware_version method that returns a FirmwareVersion struct. Currently, this requires casting from i32 to u8. But this will be fixed with the next libnitrokey version as we change the return types for the firmware getters. --- CHANGELOG.md | 1 + src/device.rs | 45 ++++++++++++++++----------------------------- tests/device.rs | 7 +++---- 3 files changed, 20 insertions(+), 33 deletions(-) (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 25a8c31..c51727e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ SPDX-License-Identifier: MIT - 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`. # v0.3.4 (2019-01-20) - Fix authentication methods that assumed that `char` is signed. diff --git a/src/device.rs b/src/device.rs index 4178922..2fac4f2 100644 --- a/src/device.rs +++ b/src/device.rs @@ -379,7 +379,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { 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 /// @@ -389,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().unwrap(), - /// device.get_minor_firmware_version().unwrap(), - /// ); + /// match device.get_firmware_version() { + /// Ok(version) => println!("Firmware version: {}", version), + /// Err(err) => println!("Could not access firmware version: {}", err), + /// }; /// # Ok(()) /// # } /// ``` - fn get_major_firmware_version(&self) -> Result { - result_or_error(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().unwrap(), - /// device.get_minor_firmware_version().unwrap(), - /// ); - /// # Ok(()) - /// # } - fn get_minor_firmware_version(&self) -> Result { - result_or_error(unsafe { nitrokey_sys::NK_get_minor_firmware_version() }) + fn get_firmware_version(&self) -> Result { + 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. diff --git a/tests/device.rs b/tests/device.rs index 7ab4d66..d80f011 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -95,10 +95,9 @@ fn get_serial_number(device: DeviceWrapper) { } #[test_device] fn get_firmware_version(device: Pro) { - assert_ok!(0, device.get_major_firmware_version()); - let minor = device.get_minor_firmware_version(); - assert!(minor.is_ok()); - assert!(minor.unwrap() > 0); + let version = device.get_firmware_version().unwrap(); + assert_eq!(0, version.major); + assert!(version.minor > 0); } fn admin_retry(device: T, suffix: &str, count: u8) -> T { -- cgit v1.2.3 From 5d7bb087707915a78149da7492cccd772db2657e Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 27 Jan 2019 22:40:07 +0000 Subject: Accept UTF-8 errors in password safe tests After a factory reset or after building the AES key, the password safe contains garbage data. This will most likely not be valid UTF-8. Therefore we change the tests to also accept an UTF-8 error in these cases. --- tests/device.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/tests/device.rs b/tests/device.rs index d80f011..cd37869 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -264,6 +264,14 @@ fn unlock_user_pin(device: DeviceWrapper) { assert_ok!((), device.change_user_pin(USER_NEW_PASSWORD, USER_PASSWORD)); } +fn assert_utf8_err_or_ne(left: &str, right: Result) { + 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(); @@ -298,9 +306,9 @@ fn factory_reset(device: DeviceWrapper) { 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()); + 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)); assert_ok!((), device.build_aes_key(ADMIN_PASSWORD)); } @@ -320,9 +328,9 @@ fn build_aes_key(device: DeviceWrapper) { let device = device.authenticate_admin(ADMIN_PASSWORD).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()); + 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] -- cgit v1.2.3 From 52df93249f27ae803bada0451d7380bc3d596007 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Mon, 28 Jan 2019 19:40:49 +0000 Subject: Add unwrap_ok macro to replace unwrap in unit tests The unwrap error message is not very useful. This patch adds the unwrap_ok macro that is basically the same as unwrap but prints a more readable error message. --- tests/device.rs | 36 ++++++++++++++++-------------------- tests/lib.rs | 4 +++- tests/otp.rs | 21 +++++++++------------ tests/pws.rs | 23 +++++++++++------------ tests/util/mod.rs | 17 +++++++++++++++++ 5 files changed, 56 insertions(+), 45 deletions(-) (limited to 'tests') diff --git a/tests/device.rs b/tests/device.rs index cd37869..67c2713 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -87,15 +87,13 @@ 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) { - let version = device.get_firmware_version().unwrap(); + let version = unwrap_ok!(device.get_firmware_version()); assert_eq!(0, version.major); assert!(version.minor > 0); } @@ -135,7 +133,7 @@ fn get_retry_count(device: DeviceWrapper) { #[test_device] fn config(device: DeviceWrapper) { - let admin = device.authenticate_admin(ADMIN_PASSWORD).unwrap(); + let admin = unwrap_ok!(device.authenticate_admin(ADMIN_PASSWORD)); let config = Config::new(None, None, None, true); assert_ok!((), admin.write_config(config)); @@ -274,12 +272,12 @@ fn assert_utf8_err_or_ne(left: &str, right: Result) { #[test_device] fn factory_reset(device: DeviceWrapper) { - let admin = device.authenticate_admin(ADMIN_PASSWORD).unwrap(); + let admin = unwrap_ok!(device.authenticate_admin(ADMIN_PASSWORD)); 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 pws = unwrap_ok!(device.get_password_safe(USER_PASSWORD)); assert_ok!((), pws.write_slot(0, "test", "testlogin", "testpw")); drop(pws); @@ -301,11 +299,11 @@ fn factory_reset(device: DeviceWrapper) { let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap().device(); - let user = device.authenticate_user(USER_PASSWORD).unwrap(); + let user = unwrap_ok!(device.authenticate_user(USER_PASSWORD)); assert_cmd_err!(CommandError::SlotNotProgrammed, user.get_totp_slot_name(1)); let device = user.device(); - let pws = device.get_password_safe(USER_PASSWORD).unwrap(); + let pws = unwrap_ok!(device.get_password_safe(USER_PASSWORD)); 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)); @@ -315,7 +313,7 @@ fn factory_reset(device: DeviceWrapper) { #[test_device] fn build_aes_key(device: DeviceWrapper) { - let pws = device.get_password_safe(USER_PASSWORD).unwrap(); + let pws = unwrap_ok!(device.get_password_safe(USER_PASSWORD)); assert_ok!((), pws.write_slot(0, "test", "testlogin", "testpw")); drop(pws); @@ -327,7 +325,7 @@ fn build_aes_key(device: DeviceWrapper) { let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap().device(); - let pws = device.get_password_safe(USER_PASSWORD).unwrap(); + let pws = unwrap_ok!(device.get_password_safe(USER_PASSWORD)); 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)); @@ -406,10 +404,9 @@ fn lock(device: Storage) { #[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 ); } @@ -434,15 +431,14 @@ fn set_unencrypted_volume_mode(device: Storage) { #[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); @@ -455,7 +451,7 @@ 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); } @@ -469,12 +465,12 @@ fn clear_new_sd_card_warning(device: Storage) { // 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)); - let status = device.get_status().unwrap(); + let status = unwrap_ok!(device.get_status()); assert!(!status.new_sd_card_found); } diff --git a/tests/lib.rs b/tests/lib.rs index 697024d..8ab75f6 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,9 +1,11 @@ // Copyright (C) 2019 Robin Krahl // 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/tests/otp.rs b/tests/otp.rs index e424673..fc0e79e 100644 --- a/tests/otp.rs +++ b/tests/otp.rs @@ -56,8 +56,7 @@ fn configure_hotp(admin: &ConfigureOtp, counter: u8) { fn check_hotp_codes(device: &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)); } }); } @@ -93,7 +92,7 @@ fn hotp_pin(device: DeviceWrapper) { assert_ok!((), admin.write_config(config)); configure_hotp(&admin, 0); - let user = admin.device().authenticate_user(USER_PASSWORD).unwrap(); + let user = unwrap_ok!(admin.device().authenticate_user(USER_PASSWORD)); check_hotp_codes(&user, 0); assert_cmd_err!(CommandError::NotAuthorized, user.device().get_hotp_code(1)); @@ -106,10 +105,8 @@ fn hotp_slot_name(device: DeviceWrapper) { 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] @@ -149,7 +146,7 @@ fn hotp_erase(device: DeviceWrapper) { 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) { @@ -167,7 +164,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 {}", @@ -221,7 +218,7 @@ fn totp_pin(device: DeviceWrapper) { assert_ok!((), admin.write_config(config)); configure_totp(&admin, 1); - let user = admin.device().authenticate_user(USER_PASSWORD).unwrap(); + let user = unwrap_ok!(admin.device().authenticate_user(USER_PASSWORD)); check_totp_codes(&user, 1, TotpTimestampSize::U32); assert_cmd_err!(CommandError::NotAuthorized, user.device().get_totp_code(1)); @@ -235,7 +232,7 @@ fn totp_pin_64(device: Pro) { assert_ok!((), admin.write_config(config)); configure_totp(&admin, 1); - let user = admin.device().authenticate_user(USER_PASSWORD).unwrap(); + let user = unwrap_ok!(admin.device().authenticate_user(USER_PASSWORD)); check_totp_codes(&user, 1, TotpTimestampSize::U64); assert_cmd_err!(CommandError::NotAuthorized, user.device().get_totp_code(1)); @@ -291,5 +288,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/tests/pws.rs b/tests/pws.rs index df99e1c..3ec7e38 100644 --- a/tests/pws.rs +++ b/tests/pws.rs @@ -37,7 +37,7 @@ fn get_pws(device: &T) -> PasswordSafe where T: Device, { - device.get_password_safe(USER_PASSWORD).unwrap() + unwrap_ok!(device.get_password_safe(USER_PASSWORD)) } #[test_device] @@ -59,7 +59,7 @@ fn drop(device: DeviceWrapper) { { let pws = get_pws(&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); } @@ -76,11 +76,11 @@ fn get_status(device: DeviceWrapper) { 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 +88,16 @@ 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); 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 +108,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)); diff --git a/tests/util/mod.rs b/tests/util/mod.rs index 49ec13e..2bda9ba 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -1,9 +1,26 @@ // Copyright (C) 2018-2019 Robin Krahl // SPDX-License-Identifier: MIT +#[allow(dead_code)] pub static ADMIN_PASSWORD: &str = "12345678"; +#[allow(dead_code)] 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_ok { ($left:expr, $right:expr) => {{ -- cgit v1.2.3 From d1262390573b758ac4aa610eff96a1b5dcb9f3d6 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Mon, 28 Jan 2019 19:45:40 +0000 Subject: Add assert_any_ok macro to unit tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sometimes we cannot use assert_ok! as we can’t compare the Ok value (or do not want to). For these cases, this patch adds the new assert_any_ok macro to use instead of assert!(x.is_ok()). The advantage is that the error information is not discarded but printed in a helpful error message. --- tests/device.rs | 12 ++++++------ tests/pws.rs | 4 ++-- tests/util/mod.rs | 15 +++++++++++++++ 3 files changed, 23 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/tests/device.rs b/tests/device.rs index 67c2713..306b33f 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -55,9 +55,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 +65,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() { diff --git a/tests/pws.rs b/tests/pws.rs index 3ec7e38..32dc8f7 100644 --- a/tests/pws.rs +++ b/tests/pws.rs @@ -46,12 +46,12 @@ fn enable(device: DeviceWrapper) { CommandError::WrongPassword, device.get_password_safe(&(USER_PASSWORD.to_owned() + "123")) ); - assert!(device.get_password_safe(USER_PASSWORD).is_ok()); + assert_any_ok!(device.get_password_safe(USER_PASSWORD)); assert_cmd_err!( CommandError::WrongPassword, device.get_password_safe(ADMIN_PASSWORD) ); - assert!(device.get_password_safe(USER_PASSWORD).is_ok()); + assert_any_ok!(device.get_password_safe(USER_PASSWORD)); } #[test_device] diff --git a/tests/util/mod.rs b/tests/util/mod.rs index 2bda9ba..bd207a9 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -21,6 +21,21 @@ macro_rules! unwrap_ok { }}; } +#[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 { ($left:expr, $right:expr) => {{ -- cgit v1.2.3 From ad76653b3be57c0cfd31c8056a8d68537034324e Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 31 Jan 2019 11:07:50 +0000 Subject: Add set_encrypted_volume_mode method to Storage Previously, we considered this command as unsupported as it only was available with firmware version 0.49. But as discussed in nitrocli issue 80 [0], it will probably be re-enabled in future firmware versions. Therefore this patch adds the set_encrypted_volume_mode to Storage. [0] https://github.com/d-e-s-o/nitrocli/issues/80 --- CHANGELOG.md | 1 + README.md | 3 +-- src/device.rs | 47 ++++++++++++++++++++++++++++++++++++++++++++++- tests/device.rs | 30 ++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index c51727e..a9e3065 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ SPDX-License-Identifier: MIT - 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`. # v0.3.4 (2019-01-20) - Fix authentication methods that assumed that `char` is signed. diff --git a/README.md b/README.md index 069fed1..8c596eb 100644 --- a/README.md +++ b/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/src/device.rs b/src/device.rs index d199d9a..c985802 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1142,7 +1142,7 @@ impl Storage { /// /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Storage::connect()?; - /// match device.set_unencrypted_volume_mode("123456", VolumeMode::ReadWrite) { + /// match device.set_unencrypted_volume_mode("12345678", VolumeMode::ReadWrite) { /// Ok(()) => println!("Set the unencrypted volume to read-write mode."), /// Err(err) => eprintln!("Could not set the unencrypted volume to read-write mode: {}", err), /// }; @@ -1169,6 +1169,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 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( + &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 diff --git a/tests/device.rs b/tests/device.rs index 306b33f..969a7df 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -401,6 +401,36 @@ fn lock(device: Storage) { 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(()). + + assert_ok!( + (), + device.set_encrypted_volume_mode(ADMIN_PASSWORD, VolumeMode::ReadOnly) + ); + + // TODO: re-enable once the password is checked in the firmware + // assert_cmd_err!( + // CommandError::WrongPassword, + // device.set_encrypted_volume_mode(USER_PASSWORD, VolumeMode::ReadOnly) + // ); + + assert_ok!( + (), + device.set_encrypted_volume_mode(ADMIN_PASSWORD, VolumeMode::ReadOnly) + ); + assert_ok!( + (), + device.set_encrypted_volume_mode(ADMIN_PASSWORD, VolumeMode::ReadWrite) + ); + assert_ok!( + (), + device.set_encrypted_volume_mode(ADMIN_PASSWORD, VolumeMode::ReadOnly) + ); +} + #[test_device] fn set_unencrypted_volume_mode(device: Storage) { fn assert_mode(device: &Storage, mode: VolumeMode) { -- cgit v1.2.3 From f49e61589e32217f97c94aa86d826f6b65170fba Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Mon, 28 Jan 2019 12:27:15 +0000 Subject: Require mutable reference if method changes device state Previously, all methods that access a Nitrokey device took a reference to the device as input. This method changes methods that change the device state to require a mutable reference instead. In most case, this is straightforward as the method writes data to the device (for example write_config or change_user_pin). But there are two edge cases: - Authenticating with a PIN changes the device state as it may decrease the PIN retry counter if the authentication fails. - Generating an HOTP code changes the device state as it increases the HOTP counter. --- CHANGELOG.md | 1 + src/auth.rs | 14 ++++----- src/device.rs | 72 ++++++++++++++++++++++---------------------- src/lib.rs | 4 +-- src/otp.rs | 26 ++++++++-------- src/pws.rs | 33 ++++++++++---------- tests/device.rs | 46 +++++++++++++++++----------- tests/otp.rs | 93 ++++++++++++++++++++++++++++----------------------------- tests/pws.rs | 18 +++++++---- 9 files changed, 163 insertions(+), 144 deletions(-) (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 718b796..e98e857 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ SPDX-License-Identifier: MIT - Use mutability to represent changes to the device status: - Implement `DerefMut` for `User` and `Admin`. - 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/src/auth.rs b/src/auth.rs index 8cec49c..f9f50fa 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -226,7 +226,7 @@ impl ops::DerefMut for User { } impl GenerateOtp for User { - fn get_hotp_code(&self, slot: u8) -> Result { + fn get_hotp_code(&mut self, slot: u8) -> Result { result_from_string(unsafe { nitrokey_sys::NK_get_hotp_code_PIN(slot, self.temp_password_ptr()) }) @@ -290,7 +290,7 @@ impl Admin { /// 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); /// () /// }, @@ -301,7 +301,7 @@ impl Admin { /// ``` /// /// [`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( @@ -317,7 +317,7 @@ impl Admin { } impl ConfigureOtp for Admin { - 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( @@ -334,7 +334,7 @@ impl ConfigureOtp for Admin { }) } - 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( @@ -351,13 +351,13 @@ impl ConfigureOtp for Admin { }) } - 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/src/device.rs b/src/device.rs index 462e9dc..f6492cd 100644 --- a/src/device.rs +++ b/src/device.rs @@ -452,7 +452,7 @@ 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) => eprintln!("Failed to update admin PIN: {}", err), @@ -463,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 { @@ -485,7 +485,7 @@ 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) => eprintln!("Failed to update admin PIN: {}", err), @@ -496,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 { @@ -518,7 +518,7 @@ 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) => eprintln!("Failed to unlock user PIN: {}", err), @@ -529,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 { @@ -552,7 +552,7 @@ 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) => eprintln!("Could not lock the Nitrokey device: {}", err), @@ -560,7 +560,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # Ok(()) /// # } /// ``` - fn lock(&self) -> Result<(), Error> { + fn lock(&mut self) -> Result<(), Error> { get_command_result(unsafe { nitrokey_sys::NK_lock_device() }) } @@ -583,7 +583,7 @@ 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) => eprintln!("Could not perform a factory reset: {}", err), @@ -593,7 +593,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// ``` /// /// [`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()) }) } @@ -617,7 +617,7 @@ 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) => eprintln!("Could not build new AES keys: {}", err), @@ -627,7 +627,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// ``` /// /// [`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()) }) } @@ -758,8 +758,8 @@ impl GenerateOtp for DeviceWrapper { self.device().get_totp_slot_name(slot) } - fn get_hotp_code(&self, slot: u8) -> Result { - self.device().get_hotp_code(slot) + fn get_hotp_code(&mut self, slot: u8) -> Result { + self.device_mut().get_hotp_code(slot) } fn get_totp_code(&self, slot: u8) -> Result { @@ -882,7 +882,7 @@ 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) => eprintln!("Failed to update update PIN: {}", err), @@ -893,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 { @@ -919,7 +919,7 @@ 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) => eprintln!("Could not enter update mode: {}", err), @@ -930,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()) @@ -953,7 +953,7 @@ 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) => eprintln!("Could not enable the encrypted volume: {}", err), @@ -964,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()) }) } @@ -982,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."); @@ -999,7 +999,7 @@ impl Storage { /// # 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() }) } @@ -1028,7 +1028,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")?; /// match device.enable_hidden_volume("hidden-pw") { /// Ok(()) => println!("Enabled a hidden volume."), @@ -1041,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()) @@ -1061,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(()) => { @@ -1079,7 +1079,7 @@ impl Storage { /// # 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() }) } @@ -1108,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(()) @@ -1118,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, @@ -1148,7 +1148,7 @@ impl Storage { /// use nitrokey::VolumeMode; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::Storage::connect()?; + /// 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) => eprintln!("Could not set the unencrypted volume to read-write mode: {}", err), @@ -1160,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> { @@ -1193,7 +1193,7 @@ impl Storage { /// use nitrokey::VolumeMode; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::Storage::connect()?; + /// 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), @@ -1205,7 +1205,7 @@ impl Storage { /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword pub fn set_encrypted_volume_mode( - &self, + &mut self, admin_pin: &str, mode: VolumeMode, ) -> Result<(), Error> { @@ -1322,7 +1322,7 @@ 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) => eprintln!("Could not set the clear the new SD card warning: {}", err), @@ -1333,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()) @@ -1341,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() }) } @@ -1361,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/src/lib.rs b/src/lib.rs index f7da8e1..c35829c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,7 +47,7 @@ //! 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) => eprintln!("Could not write slot: {}", err), @@ -66,7 +66,7 @@ //! # 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) => eprintln!("Could not generate HOTP code: {}", err), diff --git a/src/otp.rs b/src/otp.rs index abaff66..ee142c7 100644 --- a/src/otp.rs +++ b/src/otp.rs @@ -38,7 +38,7 @@ 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) => eprintln!("Could not write slot: {}", err), @@ -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,7 +74,7 @@ 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) => eprintln!("Could not write slot: {}", err), @@ -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,7 +106,7 @@ 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) => eprintln!("Could not erase slot: {}", err), @@ -119,7 +119,7 @@ pub trait ConfigureOtp { /// ``` /// /// [`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,7 +136,7 @@ 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) => eprintln!("Could not erase slot: {}", err), @@ -149,7 +149,7 @@ pub trait ConfigureOtp { /// ``` /// /// [`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,7 +171,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) => device.set_time(time.as_secs(), false)?, @@ -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 { @@ -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 { + fn get_hotp_code(&mut self, slot: u8) -> Result { 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) => { diff --git a/src/pws.rs b/src/pws.rs index 1e27935..371de6e 100644 --- a/src/pws.rs +++ b/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) => 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, Error>; + fn get_password_safe(&mut self, user_pin: &str) -> Result, 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)?; @@ -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,8 +341,8 @@ 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) => eprintln!("Could not erase slot 0: {}", err), @@ -351,7 +352,7 @@ impl<'a> PasswordSafe<'a> { /// ``` /// /// [`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, Error> { + fn get_password_safe(&mut self, user_pin: &str) -> Result, Error> { get_password_safe(self, user_pin) } } impl GetPasswordSafe for Storage { - fn get_password_safe(&self, user_pin: &str) -> Result, Error> { + fn get_password_safe(&mut self, user_pin: &str) -> Result, Error> { get_password_safe(self, user_pin) } } impl GetPasswordSafe for DeviceWrapper { - fn get_password_safe(&self, user_pin: &str) -> Result, Error> { + fn get_password_safe(&mut self, user_pin: &str) -> Result, Error> { get_password_safe(self, user_pin) } } diff --git a/tests/device.rs b/tests/device.rs index 969a7df..7a69214 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -133,7 +133,7 @@ fn get_retry_count(device: DeviceWrapper) { #[test_device] fn config(device: DeviceWrapper) { - let admin = unwrap_ok!(device.authenticate_admin(ADMIN_PASSWORD)); + let mut admin = unwrap_ok!(device.authenticate_admin(ADMIN_PASSWORD)); let config = Config::new(None, None, None, true); assert_ok!((), admin.write_config(config)); @@ -156,6 +156,7 @@ fn change_user_pin(device: DeviceWrapper) { let device = device.authenticate_user(USER_PASSWORD).unwrap().device(); let device = device.authenticate_user(USER_NEW_PASSWORD).unwrap_err().0; + let mut device = device; assert_ok!((), device.change_user_pin(USER_PASSWORD, USER_NEW_PASSWORD)); let device = device.authenticate_user(USER_PASSWORD).unwrap_err().0; @@ -164,6 +165,7 @@ fn change_user_pin(device: DeviceWrapper) { .unwrap() .device(); + let mut device = device; let result = device.change_user_pin(USER_PASSWORD, USER_PASSWORD); assert_cmd_err!(CommandError::WrongPassword, result); @@ -176,7 +178,7 @@ fn change_user_pin(device: DeviceWrapper) { #[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 mut device = device.authenticate_admin(ADMIN_NEW_PASSWORD).unwrap_err().0; assert_ok!( (), @@ -184,7 +186,7 @@ fn change_admin_pin(device: DeviceWrapper) { ); let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap_err().0; - let device = device + let mut device = device .authenticate_admin(ADMIN_NEW_PASSWORD) .unwrap() .device(); @@ -220,7 +222,7 @@ where #[test_device] fn unlock_user_pin(device: DeviceWrapper) { - let device = device.authenticate_user(USER_PASSWORD).unwrap().device(); + let mut device = device.authenticate_user(USER_PASSWORD).unwrap().device(); assert_ok!((), device.unlock_user_pin(ADMIN_PASSWORD, USER_PASSWORD)); assert_cmd_err!( CommandError::WrongPassword, @@ -232,7 +234,7 @@ fn unlock_user_pin(device: DeviceWrapper) { 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, USER_PASSWORD, CommandError::WrongPassword); // unblock with current PIN assert_cmd_err!( @@ -246,7 +248,7 @@ fn unlock_user_pin(device: DeviceWrapper) { 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, USER_PASSWORD, CommandError::WrongPassword); // unblock with new PIN assert_cmd_err!( @@ -272,12 +274,12 @@ fn assert_utf8_err_or_ne(left: &str, right: Result) { #[test_device] fn factory_reset(device: DeviceWrapper) { - let admin = unwrap_ok!(device.authenticate_admin(ADMIN_PASSWORD)); + let mut admin = unwrap_ok!(device.authenticate_admin(ADMIN_PASSWORD)); 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 = unwrap_ok!(device.get_password_safe(USER_PASSWORD)); + let mut device = admin.device(); + let mut pws = unwrap_ok!(device.get_password_safe(USER_PASSWORD)); assert_ok!((), pws.write_slot(0, "test", "testlogin", "testpw")); drop(pws); @@ -302,18 +304,20 @@ fn factory_reset(device: DeviceWrapper) { let user = unwrap_ok!(device.authenticate_user(USER_PASSWORD)); assert_cmd_err!(CommandError::SlotNotProgrammed, user.get_totp_slot_name(1)); - let device = user.device(); + let mut device = user.device(); let pws = unwrap_ok!(device.get_password_safe(USER_PASSWORD)); 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)); } #[test_device] fn build_aes_key(device: DeviceWrapper) { - let pws = unwrap_ok!(device.get_password_safe(USER_PASSWORD)); + let mut device = device; + let mut pws = unwrap_ok!(device.get_password_safe(USER_PASSWORD)); assert_ok!((), pws.write_slot(0, "test", "testlogin", "testpw")); drop(pws); @@ -323,7 +327,7 @@ fn build_aes_key(device: DeviceWrapper) { ); assert_ok!((), device.build_aes_key(ADMIN_PASSWORD)); - let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap().device(); + let mut device = device.authenticate_admin(ADMIN_PASSWORD).unwrap().device(); let pws = unwrap_ok!(device.get_password_safe(USER_PASSWORD)); assert_utf8_err_or_ne("test", pws.get_slot_name(0)); @@ -333,6 +337,7 @@ fn build_aes_key(device: DeviceWrapper) { #[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) @@ -343,6 +348,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()); @@ -361,6 +367,7 @@ 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()); @@ -396,6 +403,7 @@ fn hidden_volume(device: Storage) { #[test_device] fn lock(device: Storage) { + let mut device = device; assert_ok!((), device.enable_encrypted_volume(USER_PASSWORD)); assert_ok!((), device.lock()); assert_eq!(1, count_nitrokey_block_devices()); @@ -405,6 +413,7 @@ fn lock(device: Storage) { 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!( (), @@ -441,12 +450,13 @@ fn set_unencrypted_volume_mode(device: Storage) { ); } - fn assert_success(device: &Storage, mode: VolumeMode) { + fn assert_success(device: &mut Storage, mode: VolumeMode) { assert_ok!((), device.set_unencrypted_volume_mode(ADMIN_PASSWORD, 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, @@ -454,9 +464,9 @@ fn set_unencrypted_volume_mode(device: Storage) { ); 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] @@ -488,6 +498,7 @@ fn get_production_info(device: Storage) { #[test_device] fn clear_new_sd_card_warning(device: Storage) { + let mut device = device; assert_ok!((), device.factory_reset(ADMIN_PASSWORD)); thread::sleep(time::Duration::from_secs(3)); assert_ok!((), device.build_aes_key(ADMIN_PASSWORD)); @@ -506,6 +517,7 @@ fn clear_new_sd_card_warning(device: Storage) { #[test_device] fn export_firmware(device: Storage) { + let mut device = device; assert_cmd_err!( CommandError::WrongPassword, device.export_firmware("someadminpn") diff --git a/tests/otp.rs b/tests/otp.rs index fc0e79e..28a8d7c 100644 --- a/tests/otp.rs +++ b/tests/otp.rs @@ -4,7 +4,7 @@ mod util; use std::fmt::Debug; -use std::ops::Deref; +use std::ops::DerefMut; use nitrokey::{ Admin, Authenticate, CommandError, Config, ConfigureOtp, Device, GenerateOtp, LibraryError, @@ -48,12 +48,12 @@ where .expect("Could not login as admin.") } -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 { assert_ok!(code.to_string(), device.get_hotp_code(1)); @@ -63,6 +63,7 @@ fn check_hotp_codes(device: &GenerateOtp, offset: u8) { #[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)); @@ -71,36 +72,36 @@ 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 = unwrap_ok!(admin.device().authenticate_user(USER_PASSWORD)); - check_hotp_codes(&user, 0); + configure_hotp(&mut admin, 0); + let mut user = unwrap_ok!(admin.device().authenticate_user(USER_PASSWORD)); + 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)); @@ -111,7 +112,7 @@ fn hotp_slot_name(device: DeviceWrapper) { #[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); @@ -130,7 +131,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); @@ -140,7 +141,7 @@ 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); @@ -149,13 +150,13 @@ fn hotp_erase(device: DeviceWrapper) { 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; @@ -177,49 +178,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 = unwrap_ok!(admin.device().authenticate_user(USER_PASSWORD)); - check_totp_codes(&user, 1, TotpTimestampSize::U32); + configure_totp(&mut admin, 1); + let mut user = unwrap_ok!(admin.device().authenticate_user(USER_PASSWORD)); + check_totp_codes(&mut user, 1, TotpTimestampSize::U32); assert_cmd_err!(CommandError::NotAuthorized, user.device().get_totp_code(1)); } @@ -227,20 +226,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 = unwrap_ok!(admin.device().authenticate_user(USER_PASSWORD)); - check_totp_codes(&user, 1, TotpTimestampSize::U64); + configure_totp(&mut admin, 1); + let mut user = unwrap_ok!(admin.device().authenticate_user(USER_PASSWORD)); + 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)); @@ -253,7 +252,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); @@ -272,7 +271,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); diff --git a/tests/pws.rs b/tests/pws.rs index 32dc8f7..7805803 100644 --- a/tests/pws.rs +++ b/tests/pws.rs @@ -33,7 +33,7 @@ fn get_slot_name_direct(slot: u8) -> Result { } } -fn get_pws(device: &T) -> PasswordSafe +fn get_pws(device: &mut T) -> PasswordSafe where T: Device, { @@ -42,6 +42,7 @@ where #[test_device] fn enable(device: DeviceWrapper) { + let mut device = device; assert_cmd_err!( CommandError::WrongPassword, device.get_password_safe(&(USER_PASSWORD.to_owned() + "123")) @@ -56,8 +57,9 @@ fn enable(device: DeviceWrapper) { #[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_ok!("name".to_string(), pws.get_slot_name(1)); let result = get_slot_name_direct(1); @@ -72,7 +74,8 @@ 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)); } @@ -93,7 +96,8 @@ fn get_status(device: DeviceWrapper) { #[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_ok!("name".to_string(), pws.get_slot_name(1)); assert_ok!("login".to_string(), pws.get_slot_login(1)); @@ -119,7 +123,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, @@ -144,7 +149,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")); -- cgit v1.2.3 From 0972bbe82623c3d9649b6023d8f50d304aa0cde6 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Mon, 28 Jan 2019 14:24:12 +0000 Subject: Refactor User and Admin to use a mutable reference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the initial nitrokey-rs implementation, the Admin and the User struct take the Device by value to make sure that the user cannot initiate a second authentication while this first is still active (which would invalidate the temporary password). Now we realized that this is not necessary – taking a mutable reference has the same effect, but leads to a much cleaner API. This patch refactors the Admin and User structs – and all dependent code – to use a mutable reference instead of a Device value. --- CHANGELOG.md | 2 + src/auth.rs | 183 ++++++++++++++++-------------------------------------- src/device.rs | 42 ++++--------- src/lib.rs | 4 +- src/otp.rs | 16 ++--- tests/device.rs | 137 ++++++++++++++++++++-------------------- tests/otp.rs | 66 +++++++++++--------- tests/util/mod.rs | 12 ++-- 8 files changed, 185 insertions(+), 277 deletions(-) (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index e98e857..8e6cb9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,8 @@ SPDX-License-Identifier: MIT - Implement `DerefMut` for `User` and `Admin`. - Add `device_mut` method to `DeviceWrapper`. - Require a mutable `Device` reference if a method changes the device state. +- Let `Admin` and `User` store a mutable reference to the `Device` instead of + the `Device` value. # v0.3.4 (2019-01-20) - Fix authentication methods that assumed that `char` is signed. diff --git a/src/auth.rs b/src/auth.rs index f9f50fa..573fed3 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -42,16 +42,10 @@ pub trait Authenticate { /// fn perform_other_task(device: &DeviceWrapper) {} /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; - /// let device = match device.authenticate_user("123456") { - /// Ok(user) => { - /// perform_user_task(&user); - /// user.device() - /// }, - /// Err((device, err)) => { - /// eprintln!("Could not authenticate as user: {}", err); - /// device - /// }, + /// let mut device = nitrokey::connect()?; + /// match device.authenticate_user("123456") { + /// Ok(user) => perform_user_task(&user), + /// Err(err) => eprintln!("Could not authenticate as user: {}", err), /// }; /// perform_other_task(&device); /// # Ok(()) @@ -61,9 +55,9 @@ pub trait Authenticate { /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`RngError`]: enum.CommandError.html#variant.RngError /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn authenticate_user(self, password: &str) -> Result, (Self, Error)> + fn authenticate_user(&mut self, password: &str) -> Result, Error> where - Self: Device + Sized; + Self: Device + std::marker::Sized; /// Performs admin authentication. This method consumes the device. If successful, an /// authenticated device is returned. Otherwise, the current unauthenticated device and the @@ -88,16 +82,10 @@ pub trait Authenticate { /// fn perform_other_task(device: &DeviceWrapper) {} /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; - /// let device = match device.authenticate_admin("123456") { - /// Ok(admin) => { - /// perform_admin_task(&admin); - /// admin.device() - /// }, - /// Err((device, err)) => { - /// eprintln!("Could not authenticate as admin: {}", err); - /// device - /// }, + /// let mut device = nitrokey::connect()?; + /// match device.authenticate_admin("123456") { + /// Ok(admin) => perform_admin_task(&admin), + /// Err(err) => eprintln!("Could not authenticate as admin: {}", err), /// }; /// perform_other_task(&device); /// # Ok(()) @@ -107,13 +95,13 @@ pub trait Authenticate { /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`RngError`]: enum.CommandError.html#variant.RngError /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> + fn authenticate_admin(&mut self, password: &str) -> Result, Error> where - Self: Device + Sized; + Self: Device + std::marker::Sized; } -trait AuthenticatedDevice { - fn new(device: T, temp_password: Vec) -> Self; +trait AuthenticatedDevice<'a, T> { + fn new(device: &'a mut T, temp_password: Vec) -> Self; fn temp_password_ptr(&self) -> *const c_char; } @@ -128,8 +116,8 @@ trait AuthenticatedDevice { /// [`authenticate_admin`]: trait.Authenticate.html#method.authenticate_admin /// [`device`]: #method.device #[derive(Debug)] -pub struct User { - device: T, +pub struct User<'a, T: Device> { + device: &'a mut T, temp_password: Vec, } @@ -143,89 +131,42 @@ pub struct User { /// [`authenticate_admin`]: trait.Authenticate.html#method.authenticate_admin /// [`device`]: #method.device #[derive(Debug)] -pub struct Admin { - device: T, +pub struct Admin<'a, T: Device> { + device: &'a mut T, temp_password: Vec, } -fn authenticate(device: D, password: &str, callback: T) -> Result +fn authenticate<'a, D, A, T>(device: &'a mut D, password: &str, callback: T) -> Result where D: Device, - A: AuthenticatedDevice, + A: AuthenticatedDevice<'a, D>, T: Fn(*const c_char, *const c_char) -> c_int, { - let temp_password = match generate_password(TEMPORARY_PASSWORD_LENGTH) { - Ok(temp_password) => temp_password, - Err(err) => return Err((device, err)), - }; - let password = match get_cstring(password) { - Ok(password) => password, - Err(err) => return Err((device, err)), - }; + let temp_password = generate_password(TEMPORARY_PASSWORD_LENGTH)?; + let password = get_cstring(password)?; let password_ptr = password.as_ptr(); let temp_password_ptr = temp_password.as_ptr() as *const c_char; match callback(password_ptr, temp_password_ptr) { 0 => Ok(A::new(device, temp_password)), - rv => Err((device, Error::from(rv))), + rv => Err(Error::from(rv)), } } -fn authenticate_user_wrapper( - device: T, - constructor: C, - password: &str, -) -> Result, (DeviceWrapper, Error)> -where - T: Device, - C: Fn(T) -> DeviceWrapper, -{ - let result = device.authenticate_user(password); - match result { - Ok(user) => Ok(User::new(constructor(user.device), user.temp_password)), - Err((device, err)) => Err((constructor(device), err)), - } -} - -fn authenticate_admin_wrapper( - device: T, - constructor: C, - password: &str, -) -> Result, (DeviceWrapper, Error)> -where - T: Device, - C: Fn(T) -> DeviceWrapper, -{ - let result = device.authenticate_admin(password); - match result { - Ok(user) => Ok(Admin::new(constructor(user.device), user.temp_password)), - Err((device, err)) => Err((constructor(device), err)), - } -} - -impl User { - /// Forgets the user authentication and returns an unauthenticated device. This method - /// consumes the authenticated device. It does not perform any actual commands on the - /// Nitrokey. - pub fn device(self) -> T { - self.device - } -} - -impl ops::Deref for User { +impl<'a, T: Device> ops::Deref for User<'a, T> { type Target = T; fn deref(&self) -> &Self::Target { - &self.device + self.device } } -impl ops::DerefMut for User { +impl<'a, T: Device> ops::DerefMut for User<'a, T> { fn deref_mut(&mut self) -> &mut T { - &mut self.device + self.device } } -impl GenerateOtp for User { +impl<'a, T: Device> GenerateOtp for User<'a, T> { fn get_hotp_code(&mut self, slot: u8) -> Result { result_from_string(unsafe { nitrokey_sys::NK_get_hotp_code_PIN(slot, self.temp_password_ptr()) @@ -239,8 +180,8 @@ impl GenerateOtp for User { } } -impl AuthenticatedDevice for User { - fn new(device: T, temp_password: Vec) -> Self { +impl<'a, T: Device> AuthenticatedDevice<'a, T> for User<'a, T> { + fn new(device: &'a mut T, temp_password: Vec) -> Self { User { device, temp_password, @@ -252,28 +193,21 @@ impl AuthenticatedDevice for User { } } -impl ops::Deref for Admin { +impl<'a, T: Device> ops::Deref for Admin<'a, T> { type Target = T; fn deref(&self) -> &Self::Target { - &self.device + self.device } } -impl ops::DerefMut for Admin { +impl<'a, T: Device> ops::DerefMut for Admin<'a, T> { fn deref_mut(&mut self) -> &mut T { - &mut self.device - } -} - -impl Admin { - /// Forgets the user authentication and returns an unauthenticated device. This method - /// consumes the authenticated device. It does not perform any actual commands on the - /// Nitrokey. - pub fn device(self) -> T { self.device } +} +impl<'a, T: Device> Admin<'a, T> { /// Writes the given configuration to the Nitrokey device. /// /// # Errors @@ -287,14 +221,11 @@ impl Admin { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// let config = Config::new(None, None, None, false); /// match device.authenticate_admin("12345678") { - /// Ok(mut admin) => { - /// admin.write_config(config); - /// () - /// }, - /// Err((_, err)) => eprintln!("Could not authenticate as admin: {}", err), + /// Ok(mut admin) => admin.write_config(config)?, + /// Err(err) => eprintln!("Could not authenticate as admin: {}", err), /// }; /// # Ok(()) /// # } @@ -316,7 +247,7 @@ impl Admin { } } -impl ConfigureOtp for Admin { +impl<'a, T: Device> ConfigureOtp for Admin<'a, T> { fn write_hotp_slot(&mut self, data: OtpSlotData, counter: u64) -> Result<(), Error> { let raw_data = RawOtpSlotData::new(data)?; get_command_result(unsafe { @@ -364,8 +295,8 @@ impl ConfigureOtp for Admin { } } -impl AuthenticatedDevice for Admin { - fn new(device: T, temp_password: Vec) -> Self { +impl<'a, T: Device> AuthenticatedDevice<'a, T> for Admin<'a, T> { + fn new(device: &'a mut T, temp_password: Vec) -> Self { Admin { device, temp_password, @@ -378,35 +309,27 @@ impl AuthenticatedDevice for Admin { } impl Authenticate for DeviceWrapper { - fn authenticate_user(self, password: &str) -> Result, (Self, Error)> { - match self { - DeviceWrapper::Storage(storage) => { - authenticate_user_wrapper(storage, DeviceWrapper::Storage, password) - } - DeviceWrapper::Pro(pro) => authenticate_user_wrapper(pro, DeviceWrapper::Pro, password), - } + fn authenticate_user(&mut self, password: &str) -> Result, Error> { + authenticate(self, password, |password_ptr, temp_password_ptr| unsafe { + nitrokey_sys::NK_user_authenticate(password_ptr, temp_password_ptr) + }) } - fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> { - match self { - DeviceWrapper::Storage(storage) => { - authenticate_admin_wrapper(storage, DeviceWrapper::Storage, password) - } - DeviceWrapper::Pro(pro) => { - authenticate_admin_wrapper(pro, DeviceWrapper::Pro, password) - } - } + fn authenticate_admin(&mut self, password: &str) -> Result, Error> { + authenticate(self, password, |password_ptr, temp_password_ptr| unsafe { + nitrokey_sys::NK_user_authenticate(password_ptr, temp_password_ptr) + }) } } impl Authenticate for Pro { - fn authenticate_user(self, password: &str) -> Result, (Self, Error)> { + fn authenticate_user(&mut self, password: &str) -> Result, Error> { authenticate(self, password, |password_ptr, temp_password_ptr| unsafe { nitrokey_sys::NK_user_authenticate(password_ptr, temp_password_ptr) }) } - fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> { + fn authenticate_admin(&mut self, password: &str) -> Result, Error> { authenticate(self, password, |password_ptr, temp_password_ptr| unsafe { nitrokey_sys::NK_first_authenticate(password_ptr, temp_password_ptr) }) @@ -414,13 +337,13 @@ impl Authenticate for Pro { } impl Authenticate for Storage { - fn authenticate_user(self, password: &str) -> Result, (Self, Error)> { + fn authenticate_user(&mut self, password: &str) -> Result, Error> { authenticate(self, password, |password_ptr, temp_password_ptr| unsafe { nitrokey_sys::NK_user_authenticate(password_ptr, temp_password_ptr) }) } - fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> { + fn authenticate_admin(&mut self, password: &str) -> Result, Error> { authenticate(self, password, |password_ptr, temp_password_ptr| unsafe { nitrokey_sys::NK_first_authenticate(password_ptr, temp_password_ptr) }) diff --git a/src/device.rs b/src/device.rs index f6492cd..a0df30e 100644 --- a/src/device.rs +++ b/src/device.rs @@ -71,16 +71,10 @@ impl fmt::Display for VolumeMode { /// fn perform_other_task(device: &DeviceWrapper) {} /// /// # fn try_main() -> Result<(), Error> { -/// let device = nitrokey::connect()?; -/// let device = match device.authenticate_user("123456") { -/// Ok(user) => { -/// perform_user_task(&user); -/// user.device() -/// }, -/// Err((device, err)) => { -/// eprintln!("Could not authenticate as user: {}", err); -/// device -/// }, +/// let mut device = nitrokey::connect()?; +/// match device.authenticate_user("123456") { +/// Ok(user) => perform_user_task(&user), +/// Err(err) => eprintln!("Could not authenticate as user: {}", err), /// }; /// perform_other_task(&device); /// # Ok(()) @@ -135,16 +129,10 @@ pub enum DeviceWrapper { /// fn perform_other_task(device: &Pro) {} /// /// # fn try_main() -> Result<(), Error> { -/// let device = nitrokey::Pro::connect()?; -/// let device = match device.authenticate_user("123456") { -/// Ok(user) => { -/// perform_user_task(&user); -/// user.device() -/// }, -/// Err((device, err)) => { -/// eprintln!("Could not authenticate as user: {}", err); -/// device -/// }, +/// let mut device = nitrokey::Pro::connect()?; +/// match device.authenticate_user("123456") { +/// Ok(user) => perform_user_task(&user), +/// Err(err) => eprintln!("Could not authenticate as user: {}", err), /// }; /// perform_other_task(&device); /// # Ok(()) @@ -181,16 +169,10 @@ pub struct Pro { /// fn perform_other_task(device: &Storage) {} /// /// # fn try_main() -> Result<(), Error> { -/// let device = nitrokey::Storage::connect()?; -/// let device = match device.authenticate_user("123456") { -/// Ok(user) => { -/// perform_user_task(&user); -/// user.device() -/// }, -/// Err((device, err)) => { -/// eprintln!("Could not authenticate as user: {}", err); -/// device -/// }, +/// let mut device = nitrokey::Storage::connect()?; +/// match device.authenticate_user("123456") { +/// Ok(user) => perform_user_task(&user), +/// Err(err) => eprintln!("Could not authenticate as user: {}", err), /// }; /// perform_other_task(&device); /// # Ok(()) diff --git a/src/lib.rs b/src/lib.rs index c35829c..d7a8c5e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,7 +44,7 @@ //! # use nitrokey::Error; //! //! # fn try_main() -> Result<(), Error> { -//! let device = nitrokey::connect()?; +//! let mut device = nitrokey::connect()?; //! let slot_data = OtpSlotData::new(1, "test", "01234567890123456689", OtpMode::SixDigits); //! match device.authenticate_admin("12345678") { //! Ok(mut admin) => { @@ -53,7 +53,7 @@ //! Err(err) => eprintln!("Could not write slot: {}", err), //! } //! }, -//! Err((_, err)) => eprintln!("Could not authenticate as admin: {}", err), +//! Err(err) => eprintln!("Could not authenticate as admin: {}", err), //! } //! # Ok(()) //! # } diff --git a/src/otp.rs b/src/otp.rs index ee142c7..a8dd20b 100644 --- a/src/otp.rs +++ b/src/otp.rs @@ -35,7 +35,7 @@ pub trait ConfigureOtp { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// let slot_data = OtpSlotData::new(1, "test", "01234567890123456689", OtpMode::SixDigits); /// match device.authenticate_admin("12345678") { /// Ok(mut admin) => { @@ -44,7 +44,7 @@ pub trait ConfigureOtp { /// Err(err) => eprintln!("Could not write slot: {}", err), /// } /// }, - /// Err((_, err)) => eprintln!("Could not authenticate as admin: {}", err), + /// Err(err) => eprintln!("Could not authenticate as admin: {}", err), /// } /// # Ok(()) /// # } @@ -71,7 +71,7 @@ pub trait ConfigureOtp { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// let slot_data = OtpSlotData::new(1, "test", "01234567890123456689", OtpMode::EightDigits); /// match device.authenticate_admin("12345678") { /// Ok(mut admin) => { @@ -80,7 +80,7 @@ pub trait ConfigureOtp { /// Err(err) => eprintln!("Could not write slot: {}", err), /// } /// }, - /// Err((_, err)) => eprintln!("Could not authenticate as admin: {}", err), + /// Err(err) => eprintln!("Could not authenticate as admin: {}", err), /// } /// # Ok(()) /// # } @@ -104,7 +104,7 @@ pub trait ConfigureOtp { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// match device.authenticate_admin("12345678") { /// Ok(mut admin) => { /// match admin.erase_hotp_slot(1) { @@ -112,7 +112,7 @@ pub trait ConfigureOtp { /// Err(err) => eprintln!("Could not erase slot: {}", err), /// } /// }, - /// Err((_, err)) => eprintln!("Could not authenticate as admin: {}", err), + /// Err(err) => eprintln!("Could not authenticate as admin: {}", err), /// } /// # Ok(()) /// # } @@ -134,7 +134,7 @@ pub trait ConfigureOtp { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// match device.authenticate_admin("12345678") { /// Ok(mut admin) => { /// match admin.erase_totp_slot(1) { @@ -142,7 +142,7 @@ pub trait ConfigureOtp { /// Err(err) => eprintln!("Could not erase slot: {}", err), /// } /// }, - /// Err((_, err)) => eprintln!("Could not authenticate as admin: {}", err), + /// Err(err) => eprintln!("Could not authenticate as admin: {}", err), /// } /// # Ok(()) /// # } diff --git a/tests/device.rs b/tests/device.rs index 7a69214..bffd767 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -98,41 +98,34 @@ fn get_firmware_version(device: Pro) { assert!(version.minor > 0); } -fn admin_retry(device: T, suffix: &str, count: u8) -> T { - let result = device.authenticate_admin(&(ADMIN_PASSWORD.to_owned() + suffix)); - let device = match result { - Ok(admin) => admin.device(), - Err((device, _)) => device, - }; +fn admin_retry(device: &mut T, suffix: &str, count: u8) { + assert_any_ok!(device.authenticate_admin(&(ADMIN_PASSWORD.to_owned() + suffix))); assert_ok!(count, device.get_admin_retry_count()); - return device; } -fn user_retry(device: T, suffix: &str, count: u8) -> T { - let result = device.authenticate_user(&(USER_PASSWORD.to_owned() + suffix)); - let device = match result { - Ok(admin) => admin.device(), - Err((device, _)) => device, - }; +fn user_retry(device: &mut T, suffix: &str, count: u8) { + assert_any_ok!(device.authenticate_user(&(USER_PASSWORD.to_owned() + suffix))); assert_ok!(count, device.get_user_retry_count()); - return device; } #[test_device] fn get_retry_count(device: DeviceWrapper) { - let device = admin_retry(device, "", 3); - let device = admin_retry(device, "123", 2); - let device = admin_retry(device, "456", 1); - let device = admin_retry(device, "", 3); - - let device = user_retry(device, "", 3); - let device = user_retry(device, "123", 2); - let device = user_retry(device, "456", 1); - user_retry(device, "", 3); + let mut device = device; + + admin_retry(&mut device, "", 3); + admin_retry(&mut device, "123", 2); + admin_retry(&mut device, "456", 1); + admin_retry(&mut device, "", 3); + + user_retry(&mut device, "", 3); + user_retry(&mut device, "123", 2); + user_retry(&mut device, "456", 1); + user_retry(&mut device, "", 3); } #[test_device] fn config(device: DeviceWrapper) { + let mut device = device; let mut admin = unwrap_ok!(device.authenticate_admin(ADMIN_PASSWORD)); let config = Config::new(None, None, None, true); @@ -153,43 +146,52 @@ 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(USER_NEW_PASSWORD).unwrap_err().0; - let mut device = device; + assert_any_ok!(device.authenticate_user(USER_PASSWORD)); + assert_cmd_err!( + CommandError::WrongPassword, + device.authenticate_user(USER_NEW_PASSWORD) + ); + assert_ok!((), device.change_user_pin(USER_PASSWORD, USER_NEW_PASSWORD)); - let device = device.authenticate_user(USER_PASSWORD).unwrap_err().0; - let device = device - .authenticate_user(USER_NEW_PASSWORD) - .unwrap() - .device(); + assert_cmd_err!( + CommandError::WrongPassword, + device.authenticate_user(USER_PASSWORD) + ); + assert_any_ok!(device.authenticate_user(USER_NEW_PASSWORD)); - let mut device = device; let result = device.change_user_pin(USER_PASSWORD, USER_PASSWORD); assert_cmd_err!(CommandError::WrongPassword, result); assert_ok!((), device.change_user_pin(USER_NEW_PASSWORD, USER_PASSWORD)); - let device = device.authenticate_user(USER_PASSWORD).unwrap().device(); - assert!(device.authenticate_user(USER_NEW_PASSWORD).is_err()); + assert_any_ok!(device.authenticate_user(USER_PASSWORD)); + assert_cmd_err!( + CommandError::WrongPassword, + device.authenticate_user(USER_NEW_PASSWORD) + ); } #[test_device] fn change_admin_pin(device: DeviceWrapper) { - let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap().device(); - let mut device = device.authenticate_admin(ADMIN_NEW_PASSWORD).unwrap_err().0; + let mut device = device; + assert_any_ok!(device.authenticate_admin(ADMIN_PASSWORD)); + assert_cmd_err!( + CommandError::WrongPassword, + device.authenticate_admin(ADMIN_NEW_PASSWORD) + ); assert_ok!( (), device.change_admin_pin(ADMIN_PASSWORD, ADMIN_NEW_PASSWORD) ); - let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap_err().0; - let mut device = device - .authenticate_admin(ADMIN_NEW_PASSWORD) - .unwrap() - .device(); + assert_cmd_err!( + CommandError::WrongPassword, + device.authenticate_admin(ADMIN_PASSWORD) + ); + assert_any_ok!(device.authenticate_admin(ADMIN_NEW_PASSWORD)); assert_cmd_err!( CommandError::WrongPassword, @@ -201,28 +203,24 @@ fn change_admin_pin(device: DeviceWrapper) { device.change_admin_pin(ADMIN_NEW_PASSWORD, ADMIN_PASSWORD) ); - let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap().device(); - device.authenticate_admin(ADMIN_NEW_PASSWORD).unwrap_err(); + assert_any_ok!(device.authenticate_admin(ADMIN_PASSWORD)); + assert_cmd_err!( + CommandError::WrongPassword, + device.authenticate_admin(ADMIN_NEW_PASSWORD) + ); } -fn require_failed_user_login(device: D, password: &str, error: CommandError) -> D -where - D: Device + Authenticate, - nitrokey::User: std::fmt::Debug, -{ - let result = device.authenticate_user(password); - assert!(result.is_err()); - let err = result.unwrap_err(); - match err.1 { - Error::CommandError(err) => assert_eq!(error, err), - _ => assert!(false), - }; - err.0 +fn require_failed_user_login(device: &mut D, password: &str) { + assert_cmd_err!( + CommandError::WrongPassword, + device.authenticate_user(password) + ); } #[test_device] fn unlock_user_pin(device: DeviceWrapper) { - let mut device = device.authenticate_user(USER_PASSWORD).unwrap().device(); + let mut device = device; + assert_any_ok!(device.authenticate_user(USER_PASSWORD)); assert_ok!((), device.unlock_user_pin(ADMIN_PASSWORD, USER_PASSWORD)); assert_cmd_err!( CommandError::WrongPassword, @@ -231,10 +229,10 @@ fn unlock_user_pin(device: DeviceWrapper) { // block user PIN let wrong_password = USER_PASSWORD.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 mut device = require_failed_user_login(device, USER_PASSWORD, CommandError::WrongPassword); + require_failed_user_login(&mut device, &wrong_password); + require_failed_user_login(&mut device, &wrong_password); + require_failed_user_login(&mut device, &wrong_password); + require_failed_user_login(&mut device, USER_PASSWORD); // unblock with current PIN assert_cmd_err!( @@ -242,13 +240,13 @@ fn unlock_user_pin(device: DeviceWrapper) { device.unlock_user_pin(USER_PASSWORD, USER_PASSWORD) ); assert_ok!((), device.unlock_user_pin(ADMIN_PASSWORD, USER_PASSWORD)); - let device = device.authenticate_user(USER_PASSWORD).unwrap().device(); + assert_any_ok!(device.authenticate_user(USER_PASSWORD)); // 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 mut device = require_failed_user_login(device, USER_PASSWORD, CommandError::WrongPassword); + require_failed_user_login(&mut device, &wrong_password); + require_failed_user_login(&mut device, &wrong_password); + require_failed_user_login(&mut device, &wrong_password); + require_failed_user_login(&mut device, USER_PASSWORD); // unblock with new PIN assert_cmd_err!( @@ -274,11 +272,11 @@ fn assert_utf8_err_or_ne(left: &str, right: Result) { #[test_device] fn factory_reset(device: DeviceWrapper) { + let mut device = device; let mut admin = unwrap_ok!(device.authenticate_admin(ADMIN_PASSWORD)); let otp_data = OtpSlotData::new(1, "test", "0123468790", OtpMode::SixDigits); assert_ok!((), admin.write_totp_slot(otp_data, 30)); - let mut device = admin.device(); let mut pws = unwrap_ok!(device.get_password_safe(USER_PASSWORD)); assert_ok!((), pws.write_slot(0, "test", "testlogin", "testpw")); drop(pws); @@ -299,12 +297,11 @@ fn factory_reset(device: DeviceWrapper) { ); assert_ok!((), device.factory_reset(ADMIN_NEW_PASSWORD)); - let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap().device(); + assert_any_ok!(device.authenticate_admin(ADMIN_PASSWORD)); let user = unwrap_ok!(device.authenticate_user(USER_PASSWORD)); assert_cmd_err!(CommandError::SlotNotProgrammed, user.get_totp_slot_name(1)); - let mut device = user.device(); let pws = unwrap_ok!(device.get_password_safe(USER_PASSWORD)); assert_utf8_err_or_ne("test", pws.get_slot_name(0)); assert_utf8_err_or_ne("testlogin", pws.get_slot_login(0)); @@ -327,7 +324,7 @@ fn build_aes_key(device: DeviceWrapper) { ); assert_ok!((), device.build_aes_key(ADMIN_PASSWORD)); - let mut device = device.authenticate_admin(ADMIN_PASSWORD).unwrap().device(); + assert_any_ok!(device.authenticate_admin(ADMIN_PASSWORD)); let pws = unwrap_ok!(device.get_password_safe(USER_PASSWORD)); assert_utf8_err_or_ne("test", pws.get_slot_name(0)); diff --git a/tests/otp.rs b/tests/otp.rs index 28a8d7c..8ca8311 100644 --- a/tests/otp.rs +++ b/tests/otp.rs @@ -3,7 +3,6 @@ mod util; -use std::fmt::Debug; use std::ops::DerefMut; use nitrokey::{ @@ -38,14 +37,11 @@ enum TotpTimestampSize { U64, } -fn make_admin_test_device(device: T) -> Admin +fn make_admin_test_device<'a, T>(device: &'a mut T) -> Admin<'a, T> where T: Device, - (T, nitrokey::Error): Debug, { - device - .authenticate_admin(ADMIN_PASSWORD) - .expect("Could not login as admin.") + unwrap_ok!(device.authenticate_admin(ADMIN_PASSWORD)) } fn configure_hotp(admin: &mut ConfigureOtp, counter: u8) { @@ -72,7 +68,8 @@ fn set_time(device: DeviceWrapper) { #[test_device] fn hotp_no_pin(device: DeviceWrapper) { - let mut admin = make_admin_test_device(device); + let mut device = device; + let mut admin = make_admin_test_device(&mut device); let config = Config::new(None, None, None, false); assert_ok!((), admin.write_config(config)); @@ -83,36 +80,38 @@ fn hotp_no_pin(device: DeviceWrapper) { check_hotp_codes(admin.deref_mut(), 5); configure_hotp(&mut admin, 0); - check_hotp_codes(&mut admin.device(), 0); + check_hotp_codes(&mut device, 0); } #[test_device] fn hotp_pin(device: DeviceWrapper) { - let mut admin = make_admin_test_device(device); + let mut device = device; + let mut admin = make_admin_test_device(&mut device); let config = Config::new(None, None, None, true); assert_ok!((), admin.write_config(config)); configure_hotp(&mut admin, 0); - let mut user = unwrap_ok!(admin.device().authenticate_user(USER_PASSWORD)); + let mut user = unwrap_ok!(device.authenticate_user(USER_PASSWORD)); check_hotp_codes(&mut user, 0); - assert_cmd_err!(CommandError::NotAuthorized, user.device().get_hotp_code(1)); + assert_cmd_err!(CommandError::NotAuthorized, user.get_hotp_code(1)); } #[test_device] fn hotp_slot_name(device: DeviceWrapper) { - let mut admin = make_admin_test_device(device); + let mut device = device; + let mut admin = make_admin_test_device(&mut 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(); 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 mut admin = make_admin_test_device(device); + let mut device = device; + let mut admin = make_admin_test_device(&mut 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); @@ -131,7 +130,8 @@ fn hotp_error(device: DeviceWrapper) { #[test_device] fn hotp_erase(device: DeviceWrapper) { - let mut admin = make_admin_test_device(device); + let mut device = device; + let mut admin = make_admin_test_device(&mut 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); @@ -141,7 +141,6 @@ fn hotp_erase(device: DeviceWrapper) { assert_ok!((), admin.erase_hotp_slot(1)); - 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); @@ -178,7 +177,8 @@ fn check_totp_codes(device: &mut GenerateOtp, factor: u64, timestamp_size: TotpT #[test_device] fn totp_no_pin(device: DeviceWrapper) { - let mut admin = make_admin_test_device(device); + let mut device = device; + let mut admin = make_admin_test_device(&mut device); let config = Config::new(None, None, None, false); assert_ok!((), admin.write_config(config)); @@ -189,14 +189,15 @@ fn totp_no_pin(device: DeviceWrapper) { check_totp_codes(admin.deref_mut(), 2, TotpTimestampSize::U32); configure_totp(&mut admin, 1); - check_totp_codes(&mut admin.device(), 1, TotpTimestampSize::U32); + check_totp_codes(&mut 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 mut admin = make_admin_test_device(device); + let mut device = device; + let mut admin = make_admin_test_device(&mut device); let config = Config::new(None, None, None, false); assert_ok!((), admin.write_config(config)); @@ -207,43 +208,45 @@ fn totp_no_pin_64(device: Pro) { check_totp_codes(admin.deref_mut(), 2, TotpTimestampSize::U64); configure_totp(&mut admin, 1); - check_totp_codes(&mut admin.device(), 1, TotpTimestampSize::U64); + check_totp_codes(&mut device, 1, TotpTimestampSize::U64); } #[test_device] fn totp_pin(device: DeviceWrapper) { - let mut admin = make_admin_test_device(device); + let mut device = device; + let mut admin = make_admin_test_device(&mut device); let config = Config::new(None, None, None, true); assert_ok!((), admin.write_config(config)); configure_totp(&mut admin, 1); - let mut user = unwrap_ok!(admin.device().authenticate_user(USER_PASSWORD)); + let mut user = unwrap_ok!(device.authenticate_user(USER_PASSWORD)); check_totp_codes(&mut user, 1, TotpTimestampSize::U32); - assert_cmd_err!(CommandError::NotAuthorized, user.device().get_totp_code(1)); + assert_cmd_err!(CommandError::NotAuthorized, user.get_totp_code(1)); } #[test_device] // See comment for totp_no_pin_64. fn totp_pin_64(device: Pro) { - let mut admin = make_admin_test_device(device); + let mut device = device; + let mut admin = make_admin_test_device(&mut device); let config = Config::new(None, None, None, true); assert_ok!((), admin.write_config(config)); configure_totp(&mut admin, 1); - let mut user = unwrap_ok!(admin.device().authenticate_user(USER_PASSWORD)); + let mut user = unwrap_ok!(admin.authenticate_user(USER_PASSWORD)); check_totp_codes(&mut user, 1, TotpTimestampSize::U64); - assert_cmd_err!(CommandError::NotAuthorized, user.device().get_totp_code(1)); + assert_cmd_err!(CommandError::NotAuthorized, device.get_totp_code(1)); } #[test_device] fn totp_slot_name(device: DeviceWrapper) { - let mut admin = make_admin_test_device(device); + let mut device = device; + let mut admin = make_admin_test_device(&mut device); let slot_data = OtpSlotData::new(1, "test-totp", TOTP_SECRET, OtpMode::EightDigits); assert_ok!((), admin.write_totp_slot(slot_data, 0)); - let device = admin.device(); let result = device.get_totp_slot_name(1); assert_ok!("test-totp", result); let result = device.get_totp_slot_name(16); @@ -252,7 +255,8 @@ fn totp_slot_name(device: DeviceWrapper) { #[test_device] fn totp_error(device: DeviceWrapper) { - let mut admin = make_admin_test_device(device); + let mut device = device; + let mut admin = make_admin_test_device(&mut 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); @@ -271,7 +275,8 @@ fn totp_error(device: DeviceWrapper) { #[test_device] fn totp_erase(device: DeviceWrapper) { - let mut admin = make_admin_test_device(device); + let mut device = device; + let mut admin = make_admin_test_device(&mut 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); @@ -281,7 +286,6 @@ fn totp_erase(device: DeviceWrapper) { assert_ok!((), admin.erase_totp_slot(1)); - let device = admin.device(); let result = device.get_totp_slot_name(1); assert_cmd_err!(CommandError::SlotNotProgrammed, result); let result = device.get_totp_code(1); diff --git a/tests/util/mod.rs b/tests/util/mod.rs index bd207a9..f80372d 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -8,7 +8,7 @@ pub static USER_PASSWORD: &str = "123456"; #[macro_export] macro_rules! unwrap_ok { - ($val:expr) => {{ + ($val:expr) => { match $val { Ok(val) => val, Err(err) => panic!( @@ -18,12 +18,12 @@ macro_rules! unwrap_ok { err ), } - }}; + }; } #[macro_export] macro_rules! assert_any_ok { - ($val:expr) => {{ + ($val:expr) => { match &$val { Ok(_) => {} Err(err) => panic!( @@ -33,12 +33,12 @@ macro_rules! assert_any_ok { err ), } - }}; + }; } #[macro_export] macro_rules! assert_ok { - ($left:expr, $right:expr) => {{ + ($left:expr, $right:expr) => { match &$right { Ok(right) => match &$left { left => { @@ -59,7 +59,7 @@ macro_rules! assert_ok { $left, right_err ), } - }}; + }; } #[macro_export] -- cgit v1.2.3 From 606177a61de39ba5e96390d63cff536f895d8c39 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Mon, 4 Feb 2019 00:29:11 +0000 Subject: Remove PIN constants from tests In a previous commit, we introduced the DEFAULT_{ADMIN,USER}_PIN constants. Therefore we no longer need in the {ADMIN,USER}_PASSWORD constants in the util module for the tests. --- tests/device.rs | 140 +++++++++++++++++++++++++++++++----------------------- tests/otp.rs | 12 ++--- tests/pws.rs | 15 +++--- tests/util/mod.rs | 5 -- 4 files changed, 92 insertions(+), 80 deletions(-) (limited to 'tests') diff --git a/tests/device.rs b/tests/device.rs index bffd767..ecc3cfa 100644 --- a/tests/device.rs +++ b/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"; @@ -99,12 +98,12 @@ fn get_firmware_version(device: Pro) { } fn admin_retry(device: &mut T, suffix: &str, count: u8) { - assert_any_ok!(device.authenticate_admin(&(ADMIN_PASSWORD.to_owned() + suffix))); + assert_any_ok!(device.authenticate_admin(&(DEFAULT_ADMIN_PIN.to_owned() + suffix))); assert_ok!(count, device.get_admin_retry_count()); } fn user_retry(device: &mut T, suffix: &str, count: u8) { - assert_any_ok!(device.authenticate_user(&(USER_PASSWORD.to_owned() + suffix))); + assert_any_ok!(device.authenticate_user(&(DEFAULT_USER_PIN.to_owned() + suffix))); assert_ok!(count, device.get_user_retry_count()); } @@ -126,7 +125,7 @@ fn get_retry_count(device: DeviceWrapper) { #[test_device] fn config(device: DeviceWrapper) { let mut device = device; - let mut admin = unwrap_ok!(device.authenticate_admin(ADMIN_PASSWORD)); + 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)); @@ -147,26 +146,32 @@ fn config(device: DeviceWrapper) { #[test_device] fn change_user_pin(device: DeviceWrapper) { let mut device = device; - assert_any_ok!(device.authenticate_user(USER_PASSWORD)); + assert_any_ok!(device.authenticate_user(DEFAULT_USER_PIN)); assert_cmd_err!( CommandError::WrongPassword, device.authenticate_user(USER_NEW_PASSWORD) ); - assert_ok!((), device.change_user_pin(USER_PASSWORD, USER_NEW_PASSWORD)); + assert_ok!( + (), + device.change_user_pin(DEFAULT_USER_PIN, USER_NEW_PASSWORD) + ); assert_cmd_err!( CommandError::WrongPassword, - device.authenticate_user(USER_PASSWORD) + device.authenticate_user(DEFAULT_USER_PIN) ); assert_any_ok!(device.authenticate_user(USER_NEW_PASSWORD)); - let result = device.change_user_pin(USER_PASSWORD, USER_PASSWORD); + 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) + ); - assert_any_ok!(device.authenticate_user(USER_PASSWORD)); + assert_any_ok!(device.authenticate_user(DEFAULT_USER_PIN)); assert_cmd_err!( CommandError::WrongPassword, device.authenticate_user(USER_NEW_PASSWORD) @@ -176,7 +181,7 @@ fn change_user_pin(device: DeviceWrapper) { #[test_device] fn change_admin_pin(device: DeviceWrapper) { let mut device = device; - assert_any_ok!(device.authenticate_admin(ADMIN_PASSWORD)); + assert_any_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN)); assert_cmd_err!( CommandError::WrongPassword, device.authenticate_admin(ADMIN_NEW_PASSWORD) @@ -184,26 +189,26 @@ fn change_admin_pin(device: DeviceWrapper) { assert_ok!( (), - device.change_admin_pin(ADMIN_PASSWORD, ADMIN_NEW_PASSWORD) + device.change_admin_pin(DEFAULT_ADMIN_PIN, ADMIN_NEW_PASSWORD) ); assert_cmd_err!( CommandError::WrongPassword, - device.authenticate_admin(ADMIN_PASSWORD) + device.authenticate_admin(DEFAULT_ADMIN_PIN) ); assert_any_ok!(device.authenticate_admin(ADMIN_NEW_PASSWORD)); 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) ); - assert_any_ok!(device.authenticate_admin(ADMIN_PASSWORD)); + assert_any_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN)); assert_cmd_err!( CommandError::WrongPassword, device.authenticate_admin(ADMIN_NEW_PASSWORD) @@ -220,46 +225,55 @@ fn require_failed_user_login(device: &mut D, password: #[test_device] fn unlock_user_pin(device: DeviceWrapper) { let mut device = device; - assert_any_ok!(device.authenticate_user(USER_PASSWORD)); - assert_ok!((), device.unlock_user_pin(ADMIN_PASSWORD, USER_PASSWORD)); + assert_any_ok!(device.authenticate_user(DEFAULT_USER_PIN)); + 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"; require_failed_user_login(&mut device, &wrong_password); require_failed_user_login(&mut device, &wrong_password); require_failed_user_login(&mut device, &wrong_password); - require_failed_user_login(&mut device, USER_PASSWORD); + require_failed_user_login(&mut device, DEFAULT_USER_PIN); // 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)); - assert_any_ok!(device.authenticate_user(USER_PASSWORD)); + assert_ok!( + (), + device.unlock_user_pin(DEFAULT_ADMIN_PIN, DEFAULT_USER_PIN) + ); + assert_any_ok!(device.authenticate_user(DEFAULT_USER_PIN)); // block user PIN require_failed_user_login(&mut device, &wrong_password); require_failed_user_login(&mut device, &wrong_password); require_failed_user_login(&mut device, &wrong_password); - require_failed_user_login(&mut device, USER_PASSWORD); + require_failed_user_login(&mut device, DEFAULT_USER_PIN); // 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) { @@ -273,18 +287,21 @@ fn assert_utf8_err_or_ne(left: &str, right: Result) { #[test_device] fn factory_reset(device: DeviceWrapper) { let mut device = device; - let mut admin = unwrap_ok!(device.authenticate_admin(ADMIN_PASSWORD)); + 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 mut pws = unwrap_ok!(device.get_password_safe(USER_PASSWORD)); + 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!( @@ -293,40 +310,40 @@ 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)); - assert_any_ok!(device.authenticate_admin(ADMIN_PASSWORD)); + assert_any_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN)); - let user = unwrap_ok!(device.authenticate_user(USER_PASSWORD)); + let user = unwrap_ok!(device.authenticate_user(DEFAULT_USER_PIN)); assert_cmd_err!(CommandError::SlotNotProgrammed, user.get_totp_slot_name(1)); - let pws = unwrap_ok!(device.get_password_safe(USER_PASSWORD)); + 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 mut device = device; - let mut pws = unwrap_ok!(device.get_password_safe(USER_PASSWORD)); + 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)); - assert_any_ok!(device.authenticate_admin(ADMIN_PASSWORD)); + assert_any_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN)); - let pws = unwrap_ok!(device.get_password_safe(USER_PASSWORD)); + 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)); @@ -356,7 +373,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()); @@ -371,7 +388,7 @@ fn hidden_volume(device: Storage) { 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? @@ -401,7 +418,7 @@ fn hidden_volume(device: Storage) { #[test_device] fn lock(device: Storage) { let mut device = device; - assert_ok!((), device.enable_encrypted_volume(USER_PASSWORD)); + assert_ok!((), device.enable_encrypted_volume(DEFAULT_USER_PIN)); assert_ok!((), device.lock()); assert_eq!(1, count_nitrokey_block_devices()); } @@ -414,26 +431,26 @@ fn set_encrypted_volume_mode(device: Storage) { assert_ok!( (), - device.set_encrypted_volume_mode(ADMIN_PASSWORD, VolumeMode::ReadOnly) + 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(USER_PASSWORD, VolumeMode::ReadOnly) + // device.set_encrypted_volume_mode(DEFAULT_USER_PIN, VolumeMode::ReadOnly) // ); assert_ok!( (), - device.set_encrypted_volume_mode(ADMIN_PASSWORD, VolumeMode::ReadOnly) + device.set_encrypted_volume_mode(DEFAULT_ADMIN_PIN, VolumeMode::ReadOnly) ); assert_ok!( (), - device.set_encrypted_volume_mode(ADMIN_PASSWORD, VolumeMode::ReadWrite) + device.set_encrypted_volume_mode(DEFAULT_ADMIN_PIN, VolumeMode::ReadWrite) ); assert_ok!( (), - device.set_encrypted_volume_mode(ADMIN_PASSWORD, VolumeMode::ReadOnly) + device.set_encrypted_volume_mode(DEFAULT_ADMIN_PIN, VolumeMode::ReadOnly) ); } @@ -448,7 +465,10 @@ fn set_unencrypted_volume_mode(device: Storage) { } fn assert_success(device: &mut Storage, mode: VolumeMode) { - assert_ok!((), device.set_unencrypted_volume_mode(ADMIN_PASSWORD, mode)); + assert_ok!( + (), + device.set_unencrypted_volume_mode(DEFAULT_ADMIN_PIN, mode) + ); assert_mode(&device, mode); } @@ -457,7 +477,7 @@ fn set_unencrypted_volume_mode(device: Storage) { 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); @@ -496,9 +516,9 @@ fn get_production_info(device: Storage) { #[test_device] fn clear_new_sd_card_warning(device: Storage) { let mut device = device; - assert_ok!((), device.factory_reset(ADMIN_PASSWORD)); + 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()); @@ -506,7 +526,7 @@ fn clear_new_sd_card_warning(device: Storage) { 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 = unwrap_ok!(device.get_status()); assert!(!status.new_sd_card_found); @@ -519,14 +539,14 @@ fn export_firmware(device: Storage) { 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/tests/otp.rs b/tests/otp.rs index 8ca8311..d55d54a 100644 --- a/tests/otp.rs +++ b/tests/otp.rs @@ -7,12 +7,10 @@ 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] = &[ @@ -41,7 +39,7 @@ fn make_admin_test_device<'a, T>(device: &'a mut T) -> Admin<'a, T> where T: Device, { - unwrap_ok!(device.authenticate_admin(ADMIN_PASSWORD)) + unwrap_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN)) } fn configure_hotp(admin: &mut ConfigureOtp, counter: u8) { @@ -91,7 +89,7 @@ fn hotp_pin(device: DeviceWrapper) { assert_ok!((), admin.write_config(config)); configure_hotp(&mut admin, 0); - let mut user = unwrap_ok!(device.authenticate_user(USER_PASSWORD)); + let mut user = unwrap_ok!(device.authenticate_user(DEFAULT_USER_PIN)); check_hotp_codes(&mut user, 0); assert_cmd_err!(CommandError::NotAuthorized, user.get_hotp_code(1)); @@ -219,7 +217,7 @@ fn totp_pin(device: DeviceWrapper) { assert_ok!((), admin.write_config(config)); configure_totp(&mut admin, 1); - let mut user = unwrap_ok!(device.authenticate_user(USER_PASSWORD)); + let mut user = unwrap_ok!(device.authenticate_user(DEFAULT_USER_PIN)); check_totp_codes(&mut user, 1, TotpTimestampSize::U32); assert_cmd_err!(CommandError::NotAuthorized, user.get_totp_code(1)); @@ -234,7 +232,7 @@ fn totp_pin_64(device: Pro) { assert_ok!((), admin.write_config(config)); configure_totp(&mut admin, 1); - let mut user = unwrap_ok!(admin.authenticate_user(USER_PASSWORD)); + let mut user = unwrap_ok!(admin.authenticate_user(DEFAULT_USER_PIN)); check_totp_codes(&mut user, 1, TotpTimestampSize::U64); assert_cmd_err!(CommandError::NotAuthorized, device.get_totp_code(1)); diff --git a/tests/pws.rs b/tests/pws.rs index 7805803..b0e5abe 100644 --- a/tests/pws.rs +++ b/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 { let ptr = unsafe { nitrokey_sys::NK_get_password_safe_slot_name(slot) }; if ptr.is_null() { @@ -37,7 +36,7 @@ fn get_pws(device: &mut T) -> PasswordSafe where T: Device, { - unwrap_ok!(device.get_password_safe(USER_PASSWORD)) + unwrap_ok!(device.get_password_safe(DEFAULT_USER_PIN)) } #[test_device] @@ -45,14 +44,14 @@ 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_any_ok!(device.get_password_safe(USER_PASSWORD)); + 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_any_ok!(device.get_password_safe(USER_PASSWORD)); + assert_any_ok!(device.get_password_safe(DEFAULT_USER_PIN)); } #[test_device] diff --git a/tests/util/mod.rs b/tests/util/mod.rs index f80372d..5bd19d1 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -1,11 +1,6 @@ // Copyright (C) 2018-2019 Robin Krahl // SPDX-License-Identifier: MIT -#[allow(dead_code)] -pub static ADMIN_PASSWORD: &str = "12345678"; -#[allow(dead_code)] -pub static USER_PASSWORD: &str = "123456"; - #[macro_export] macro_rules! unwrap_ok { ($val:expr) => { -- cgit v1.2.3 From 83641ca0518e4c766c63e40d0787e4f0b436652a Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Tue, 5 Feb 2019 12:47:24 +0000 Subject: Revert "Refactor User and Admin to use a mutable reference" This reverts commit 0972bbe82623c3d9649b6023d8f50d304aa0cde6. --- CHANGELOG.md | 4 -- src/auth.rs | 183 ++++++++++++++++++++++++++++++++++++++---------------- src/device.rs | 42 +++++++++---- src/lib.rs | 4 +- src/otp.rs | 16 ++--- tests/device.rs | 152 ++++++++++++++++++++++----------------------- tests/otp.rs | 62 +++++++++--------- tests/util/mod.rs | 12 ++-- 8 files changed, 277 insertions(+), 198 deletions(-) (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 9227510..e98e857 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,10 +38,6 @@ SPDX-License-Identifier: MIT - Implement `DerefMut` for `User` and `Admin`. - Add `device_mut` method to `DeviceWrapper`. - Require a mutable `Device` reference if a method changes the device state. -- Let `Admin` and `User` store a mutable reference to the `Device` instead of - the `Device` value. -- Let `PasswordStore` store a mutable reference to the `Device` instead of a - non-mutable reference. # v0.3.4 (2019-01-20) - Fix authentication methods that assumed that `char` is signed. diff --git a/src/auth.rs b/src/auth.rs index 573fed3..f9f50fa 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -42,10 +42,16 @@ pub trait Authenticate { /// fn perform_other_task(device: &DeviceWrapper) {} /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; - /// match device.authenticate_user("123456") { - /// Ok(user) => perform_user_task(&user), - /// Err(err) => eprintln!("Could not authenticate as user: {}", err), + /// let device = nitrokey::connect()?; + /// let device = match device.authenticate_user("123456") { + /// Ok(user) => { + /// perform_user_task(&user); + /// user.device() + /// }, + /// Err((device, err)) => { + /// eprintln!("Could not authenticate as user: {}", err); + /// device + /// }, /// }; /// perform_other_task(&device); /// # Ok(()) @@ -55,9 +61,9 @@ pub trait Authenticate { /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`RngError`]: enum.CommandError.html#variant.RngError /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn authenticate_user(&mut self, password: &str) -> Result, Error> + fn authenticate_user(self, password: &str) -> Result, (Self, Error)> where - Self: Device + std::marker::Sized; + Self: Device + Sized; /// Performs admin authentication. This method consumes the device. If successful, an /// authenticated device is returned. Otherwise, the current unauthenticated device and the @@ -82,10 +88,16 @@ pub trait Authenticate { /// fn perform_other_task(device: &DeviceWrapper) {} /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; - /// match device.authenticate_admin("123456") { - /// Ok(admin) => perform_admin_task(&admin), - /// Err(err) => eprintln!("Could not authenticate as admin: {}", err), + /// let device = nitrokey::connect()?; + /// let device = match device.authenticate_admin("123456") { + /// Ok(admin) => { + /// perform_admin_task(&admin); + /// admin.device() + /// }, + /// Err((device, err)) => { + /// eprintln!("Could not authenticate as admin: {}", err); + /// device + /// }, /// }; /// perform_other_task(&device); /// # Ok(()) @@ -95,13 +107,13 @@ pub trait Authenticate { /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`RngError`]: enum.CommandError.html#variant.RngError /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn authenticate_admin(&mut self, password: &str) -> Result, Error> + fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> where - Self: Device + std::marker::Sized; + Self: Device + Sized; } -trait AuthenticatedDevice<'a, T> { - fn new(device: &'a mut T, temp_password: Vec) -> Self; +trait AuthenticatedDevice { + fn new(device: T, temp_password: Vec) -> Self; fn temp_password_ptr(&self) -> *const c_char; } @@ -116,8 +128,8 @@ trait AuthenticatedDevice<'a, T> { /// [`authenticate_admin`]: trait.Authenticate.html#method.authenticate_admin /// [`device`]: #method.device #[derive(Debug)] -pub struct User<'a, T: Device> { - device: &'a mut T, +pub struct User { + device: T, temp_password: Vec, } @@ -131,42 +143,89 @@ pub struct User<'a, T: Device> { /// [`authenticate_admin`]: trait.Authenticate.html#method.authenticate_admin /// [`device`]: #method.device #[derive(Debug)] -pub struct Admin<'a, T: Device> { - device: &'a mut T, +pub struct Admin { + device: T, temp_password: Vec, } -fn authenticate<'a, D, A, T>(device: &'a mut D, password: &str, callback: T) -> Result +fn authenticate(device: D, password: &str, callback: T) -> Result where D: Device, - A: AuthenticatedDevice<'a, D>, + A: AuthenticatedDevice, T: Fn(*const c_char, *const c_char) -> c_int, { - let temp_password = generate_password(TEMPORARY_PASSWORD_LENGTH)?; - let password = get_cstring(password)?; + let temp_password = match generate_password(TEMPORARY_PASSWORD_LENGTH) { + Ok(temp_password) => temp_password, + Err(err) => return Err((device, err)), + }; + let password = match get_cstring(password) { + Ok(password) => password, + Err(err) => return Err((device, err)), + }; let password_ptr = password.as_ptr(); let temp_password_ptr = temp_password.as_ptr() as *const c_char; match callback(password_ptr, temp_password_ptr) { 0 => Ok(A::new(device, temp_password)), - rv => Err(Error::from(rv)), + rv => Err((device, Error::from(rv))), } } -impl<'a, T: Device> ops::Deref for User<'a, T> { +fn authenticate_user_wrapper( + device: T, + constructor: C, + password: &str, +) -> Result, (DeviceWrapper, Error)> +where + T: Device, + C: Fn(T) -> DeviceWrapper, +{ + let result = device.authenticate_user(password); + match result { + Ok(user) => Ok(User::new(constructor(user.device), user.temp_password)), + Err((device, err)) => Err((constructor(device), err)), + } +} + +fn authenticate_admin_wrapper( + device: T, + constructor: C, + password: &str, +) -> Result, (DeviceWrapper, Error)> +where + T: Device, + C: Fn(T) -> DeviceWrapper, +{ + let result = device.authenticate_admin(password); + match result { + Ok(user) => Ok(Admin::new(constructor(user.device), user.temp_password)), + Err((device, err)) => Err((constructor(device), err)), + } +} + +impl User { + /// Forgets the user authentication and returns an unauthenticated device. This method + /// consumes the authenticated device. It does not perform any actual commands on the + /// Nitrokey. + pub fn device(self) -> T { + self.device + } +} + +impl ops::Deref for User { type Target = T; fn deref(&self) -> &Self::Target { - self.device + &self.device } } -impl<'a, T: Device> ops::DerefMut for User<'a, T> { +impl ops::DerefMut for User { fn deref_mut(&mut self) -> &mut T { - self.device + &mut self.device } } -impl<'a, T: Device> GenerateOtp for User<'a, T> { +impl GenerateOtp for User { fn get_hotp_code(&mut self, slot: u8) -> Result { result_from_string(unsafe { nitrokey_sys::NK_get_hotp_code_PIN(slot, self.temp_password_ptr()) @@ -180,8 +239,8 @@ impl<'a, T: Device> GenerateOtp for User<'a, T> { } } -impl<'a, T: Device> AuthenticatedDevice<'a, T> for User<'a, T> { - fn new(device: &'a mut T, temp_password: Vec) -> Self { +impl AuthenticatedDevice for User { + fn new(device: T, temp_password: Vec) -> Self { User { device, temp_password, @@ -193,21 +252,28 @@ impl<'a, T: Device> AuthenticatedDevice<'a, T> for User<'a, T> { } } -impl<'a, T: Device> ops::Deref for Admin<'a, T> { +impl ops::Deref for Admin { type Target = T; fn deref(&self) -> &Self::Target { - self.device + &self.device } } -impl<'a, T: Device> ops::DerefMut for Admin<'a, T> { +impl ops::DerefMut for Admin { fn deref_mut(&mut self) -> &mut T { - self.device + &mut self.device } } -impl<'a, T: Device> Admin<'a, T> { +impl Admin { + /// Forgets the user authentication and returns an unauthenticated device. This method + /// consumes the authenticated device. It does not perform any actual commands on the + /// Nitrokey. + pub fn device(self) -> T { + self.device + } + /// Writes the given configuration to the Nitrokey device. /// /// # Errors @@ -221,11 +287,14 @@ impl<'a, T: Device> Admin<'a, T> { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; + /// let device = nitrokey::connect()?; /// let config = Config::new(None, None, None, false); /// match device.authenticate_admin("12345678") { - /// Ok(mut admin) => admin.write_config(config)?, - /// Err(err) => eprintln!("Could not authenticate as admin: {}", err), + /// Ok(mut admin) => { + /// admin.write_config(config); + /// () + /// }, + /// Err((_, err)) => eprintln!("Could not authenticate as admin: {}", err), /// }; /// # Ok(()) /// # } @@ -247,7 +316,7 @@ impl<'a, T: Device> Admin<'a, T> { } } -impl<'a, T: Device> ConfigureOtp for Admin<'a, T> { +impl ConfigureOtp for Admin { fn write_hotp_slot(&mut self, data: OtpSlotData, counter: u64) -> Result<(), Error> { let raw_data = RawOtpSlotData::new(data)?; get_command_result(unsafe { @@ -295,8 +364,8 @@ impl<'a, T: Device> ConfigureOtp for Admin<'a, T> { } } -impl<'a, T: Device> AuthenticatedDevice<'a, T> for Admin<'a, T> { - fn new(device: &'a mut T, temp_password: Vec) -> Self { +impl AuthenticatedDevice for Admin { + fn new(device: T, temp_password: Vec) -> Self { Admin { device, temp_password, @@ -309,27 +378,35 @@ impl<'a, T: Device> AuthenticatedDevice<'a, T> for Admin<'a, T> { } impl Authenticate for DeviceWrapper { - fn authenticate_user(&mut self, password: &str) -> Result, Error> { - authenticate(self, password, |password_ptr, temp_password_ptr| unsafe { - nitrokey_sys::NK_user_authenticate(password_ptr, temp_password_ptr) - }) + fn authenticate_user(self, password: &str) -> Result, (Self, Error)> { + match self { + DeviceWrapper::Storage(storage) => { + authenticate_user_wrapper(storage, DeviceWrapper::Storage, password) + } + DeviceWrapper::Pro(pro) => authenticate_user_wrapper(pro, DeviceWrapper::Pro, password), + } } - fn authenticate_admin(&mut self, password: &str) -> Result, Error> { - authenticate(self, password, |password_ptr, temp_password_ptr| unsafe { - nitrokey_sys::NK_user_authenticate(password_ptr, temp_password_ptr) - }) + fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> { + match self { + DeviceWrapper::Storage(storage) => { + authenticate_admin_wrapper(storage, DeviceWrapper::Storage, password) + } + DeviceWrapper::Pro(pro) => { + authenticate_admin_wrapper(pro, DeviceWrapper::Pro, password) + } + } } } impl Authenticate for Pro { - fn authenticate_user(&mut self, password: &str) -> Result, Error> { + fn authenticate_user(self, password: &str) -> Result, (Self, Error)> { authenticate(self, password, |password_ptr, temp_password_ptr| unsafe { nitrokey_sys::NK_user_authenticate(password_ptr, temp_password_ptr) }) } - fn authenticate_admin(&mut self, password: &str) -> Result, Error> { + fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> { authenticate(self, password, |password_ptr, temp_password_ptr| unsafe { nitrokey_sys::NK_first_authenticate(password_ptr, temp_password_ptr) }) @@ -337,13 +414,13 @@ impl Authenticate for Pro { } impl Authenticate for Storage { - fn authenticate_user(&mut self, password: &str) -> Result, Error> { + fn authenticate_user(self, password: &str) -> Result, (Self, Error)> { authenticate(self, password, |password_ptr, temp_password_ptr| unsafe { nitrokey_sys::NK_user_authenticate(password_ptr, temp_password_ptr) }) } - fn authenticate_admin(&mut self, password: &str) -> Result, Error> { + fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> { authenticate(self, password, |password_ptr, temp_password_ptr| unsafe { nitrokey_sys::NK_first_authenticate(password_ptr, temp_password_ptr) }) diff --git a/src/device.rs b/src/device.rs index a0df30e..f6492cd 100644 --- a/src/device.rs +++ b/src/device.rs @@ -71,10 +71,16 @@ impl fmt::Display for VolumeMode { /// fn perform_other_task(device: &DeviceWrapper) {} /// /// # fn try_main() -> Result<(), Error> { -/// let mut device = nitrokey::connect()?; -/// match device.authenticate_user("123456") { -/// Ok(user) => perform_user_task(&user), -/// Err(err) => eprintln!("Could not authenticate as user: {}", err), +/// let device = nitrokey::connect()?; +/// let device = match device.authenticate_user("123456") { +/// Ok(user) => { +/// perform_user_task(&user); +/// user.device() +/// }, +/// Err((device, err)) => { +/// eprintln!("Could not authenticate as user: {}", err); +/// device +/// }, /// }; /// perform_other_task(&device); /// # Ok(()) @@ -129,10 +135,16 @@ pub enum DeviceWrapper { /// fn perform_other_task(device: &Pro) {} /// /// # fn try_main() -> Result<(), Error> { -/// let mut device = nitrokey::Pro::connect()?; -/// match device.authenticate_user("123456") { -/// Ok(user) => perform_user_task(&user), -/// Err(err) => eprintln!("Could not authenticate as user: {}", err), +/// let device = nitrokey::Pro::connect()?; +/// let device = match device.authenticate_user("123456") { +/// Ok(user) => { +/// perform_user_task(&user); +/// user.device() +/// }, +/// Err((device, err)) => { +/// eprintln!("Could not authenticate as user: {}", err); +/// device +/// }, /// }; /// perform_other_task(&device); /// # Ok(()) @@ -169,10 +181,16 @@ pub struct Pro { /// fn perform_other_task(device: &Storage) {} /// /// # fn try_main() -> Result<(), Error> { -/// let mut device = nitrokey::Storage::connect()?; -/// match device.authenticate_user("123456") { -/// Ok(user) => perform_user_task(&user), -/// Err(err) => eprintln!("Could not authenticate as user: {}", err), +/// let device = nitrokey::Storage::connect()?; +/// let device = match device.authenticate_user("123456") { +/// Ok(user) => { +/// perform_user_task(&user); +/// user.device() +/// }, +/// Err((device, err)) => { +/// eprintln!("Could not authenticate as user: {}", err); +/// device +/// }, /// }; /// perform_other_task(&device); /// # Ok(()) diff --git a/src/lib.rs b/src/lib.rs index d7a8c5e..c35829c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,7 +44,7 @@ //! # use nitrokey::Error; //! //! # fn try_main() -> Result<(), Error> { -//! let mut device = nitrokey::connect()?; +//! let device = nitrokey::connect()?; //! let slot_data = OtpSlotData::new(1, "test", "01234567890123456689", OtpMode::SixDigits); //! match device.authenticate_admin("12345678") { //! Ok(mut admin) => { @@ -53,7 +53,7 @@ //! Err(err) => eprintln!("Could not write slot: {}", err), //! } //! }, -//! Err(err) => eprintln!("Could not authenticate as admin: {}", err), +//! Err((_, err)) => eprintln!("Could not authenticate as admin: {}", err), //! } //! # Ok(()) //! # } diff --git a/src/otp.rs b/src/otp.rs index a8dd20b..ee142c7 100644 --- a/src/otp.rs +++ b/src/otp.rs @@ -35,7 +35,7 @@ pub trait ConfigureOtp { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; + /// let device = nitrokey::connect()?; /// let slot_data = OtpSlotData::new(1, "test", "01234567890123456689", OtpMode::SixDigits); /// match device.authenticate_admin("12345678") { /// Ok(mut admin) => { @@ -44,7 +44,7 @@ pub trait ConfigureOtp { /// Err(err) => eprintln!("Could not write slot: {}", err), /// } /// }, - /// Err(err) => eprintln!("Could not authenticate as admin: {}", err), + /// Err((_, err)) => eprintln!("Could not authenticate as admin: {}", err), /// } /// # Ok(()) /// # } @@ -71,7 +71,7 @@ pub trait ConfigureOtp { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; + /// let device = nitrokey::connect()?; /// let slot_data = OtpSlotData::new(1, "test", "01234567890123456689", OtpMode::EightDigits); /// match device.authenticate_admin("12345678") { /// Ok(mut admin) => { @@ -80,7 +80,7 @@ pub trait ConfigureOtp { /// Err(err) => eprintln!("Could not write slot: {}", err), /// } /// }, - /// Err(err) => eprintln!("Could not authenticate as admin: {}", err), + /// Err((_, err)) => eprintln!("Could not authenticate as admin: {}", err), /// } /// # Ok(()) /// # } @@ -104,7 +104,7 @@ pub trait ConfigureOtp { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; + /// let device = nitrokey::connect()?; /// match device.authenticate_admin("12345678") { /// Ok(mut admin) => { /// match admin.erase_hotp_slot(1) { @@ -112,7 +112,7 @@ pub trait ConfigureOtp { /// Err(err) => eprintln!("Could not erase slot: {}", err), /// } /// }, - /// Err(err) => eprintln!("Could not authenticate as admin: {}", err), + /// Err((_, err)) => eprintln!("Could not authenticate as admin: {}", err), /// } /// # Ok(()) /// # } @@ -134,7 +134,7 @@ pub trait ConfigureOtp { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; + /// let device = nitrokey::connect()?; /// match device.authenticate_admin("12345678") { /// Ok(mut admin) => { /// match admin.erase_totp_slot(1) { @@ -142,7 +142,7 @@ pub trait ConfigureOtp { /// Err(err) => eprintln!("Could not erase slot: {}", err), /// } /// }, - /// Err(err) => eprintln!("Could not authenticate as admin: {}", err), + /// Err((_, err)) => eprintln!("Could not authenticate as admin: {}", err), /// } /// # Ok(()) /// # } diff --git a/tests/device.rs b/tests/device.rs index ecc3cfa..6a3683b 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -97,34 +97,41 @@ fn get_firmware_version(device: Pro) { assert!(version.minor > 0); } -fn admin_retry(device: &mut T, suffix: &str, count: u8) { - assert_any_ok!(device.authenticate_admin(&(DEFAULT_ADMIN_PIN.to_owned() + suffix))); +fn admin_retry(device: T, suffix: &str, count: u8) -> T { + let result = device.authenticate_admin(&(DEFAULT_ADMIN_PIN.to_owned() + suffix)); + let device = match result { + Ok(admin) => admin.device(), + Err((device, _)) => device, + }; assert_ok!(count, device.get_admin_retry_count()); + return device; } -fn user_retry(device: &mut T, suffix: &str, count: u8) { - assert_any_ok!(device.authenticate_user(&(DEFAULT_USER_PIN.to_owned() + suffix))); +fn user_retry(device: T, suffix: &str, count: u8) -> T { + let result = device.authenticate_user(&(DEFAULT_USER_PIN.to_owned() + suffix)); + let device = match result { + Ok(admin) => admin.device(), + Err((device, _)) => device, + }; assert_ok!(count, device.get_user_retry_count()); + return device; } #[test_device] fn get_retry_count(device: DeviceWrapper) { - let mut device = device; - - admin_retry(&mut device, "", 3); - admin_retry(&mut device, "123", 2); - admin_retry(&mut device, "456", 1); - admin_retry(&mut device, "", 3); - - user_retry(&mut device, "", 3); - user_retry(&mut device, "123", 2); - user_retry(&mut device, "456", 1); - user_retry(&mut device, "", 3); + let device = admin_retry(device, "", 3); + let device = admin_retry(device, "123", 2); + let device = admin_retry(device, "456", 1); + let device = admin_retry(device, "", 3); + + let device = user_retry(device, "", 3); + let device = user_retry(device, "123", 2); + let device = user_retry(device, "456", 1); + user_retry(device, "", 3); } #[test_device] fn config(device: DeviceWrapper) { - let mut device = device; let mut admin = unwrap_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN)); let config = Config::new(None, None, None, true); @@ -145,24 +152,19 @@ fn config(device: DeviceWrapper) { #[test_device] fn change_user_pin(device: DeviceWrapper) { - let mut device = device; - assert_any_ok!(device.authenticate_user(DEFAULT_USER_PIN)); - assert_cmd_err!( - CommandError::WrongPassword, - device.authenticate_user(USER_NEW_PASSWORD) - ); + 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(DEFAULT_USER_PIN, USER_NEW_PASSWORD) - ); + let mut device = device; + assert_ok!((), device.change_user_pin(DEFAULT_USER_PIN, USER_NEW_PASSWORD)); - assert_cmd_err!( - CommandError::WrongPassword, - device.authenticate_user(DEFAULT_USER_PIN) - ); - assert_any_ok!(device.authenticate_user(USER_NEW_PASSWORD)); + let device = device.authenticate_user(DEFAULT_USER_PIN).unwrap_err().0; + let device = device + .authenticate_user(USER_NEW_PASSWORD) + .unwrap() + .device(); + let mut device = device; let result = device.change_user_pin(DEFAULT_USER_PIN, DEFAULT_USER_PIN); assert_cmd_err!(CommandError::WrongPassword, result); @@ -171,32 +173,25 @@ fn change_user_pin(device: DeviceWrapper) { device.change_user_pin(USER_NEW_PASSWORD, DEFAULT_USER_PIN) ); - assert_any_ok!(device.authenticate_user(DEFAULT_USER_PIN)); - assert_cmd_err!( - CommandError::WrongPassword, - device.authenticate_user(USER_NEW_PASSWORD) - ); + 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 mut device = device; - assert_any_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN)); - assert_cmd_err!( - CommandError::WrongPassword, - device.authenticate_admin(ADMIN_NEW_PASSWORD) - ); + 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(DEFAULT_ADMIN_PIN, ADMIN_NEW_PASSWORD) ); - assert_cmd_err!( - CommandError::WrongPassword, - device.authenticate_admin(DEFAULT_ADMIN_PIN) - ); - assert_any_ok!(device.authenticate_admin(ADMIN_NEW_PASSWORD)); + 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, @@ -208,28 +203,29 @@ fn change_admin_pin(device: DeviceWrapper) { device.change_admin_pin(ADMIN_NEW_PASSWORD, DEFAULT_ADMIN_PIN) ); - assert_any_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN)); - assert_cmd_err!( - CommandError::WrongPassword, - device.authenticate_admin(ADMIN_NEW_PASSWORD) - ); + let device = device.authenticate_admin(DEFAULT_ADMIN_PIN).unwrap().device(); + device.authenticate_admin(ADMIN_NEW_PASSWORD).unwrap_err(); } -fn require_failed_user_login(device: &mut D, password: &str) { - assert_cmd_err!( - CommandError::WrongPassword, - device.authenticate_user(password) - ); +fn require_failed_user_login(device: D, password: &str, error: CommandError) -> D +where + D: Device + Authenticate, + nitrokey::User: std::fmt::Debug, +{ + let result = device.authenticate_user(password); + assert!(result.is_err()); + let err = result.unwrap_err(); + match err.1 { + Error::CommandError(err) => assert_eq!(error, err), + _ => assert!(false), + }; + err.0 } #[test_device] fn unlock_user_pin(device: DeviceWrapper) { - let mut device = device; - assert_any_ok!(device.authenticate_user(DEFAULT_USER_PIN)); - assert_ok!( - (), - device.unlock_user_pin(DEFAULT_ADMIN_PIN, DEFAULT_USER_PIN) - ); + 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(DEFAULT_USER_PIN, DEFAULT_USER_PIN) @@ -237,27 +233,24 @@ fn unlock_user_pin(device: DeviceWrapper) { // block user PIN let wrong_password = DEFAULT_USER_PIN.to_owned() + "foo"; - require_failed_user_login(&mut device, &wrong_password); - require_failed_user_login(&mut device, &wrong_password); - require_failed_user_login(&mut device, &wrong_password); - require_failed_user_login(&mut device, DEFAULT_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 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(DEFAULT_USER_PIN, DEFAULT_USER_PIN) ); - assert_ok!( - (), - device.unlock_user_pin(DEFAULT_ADMIN_PIN, DEFAULT_USER_PIN) - ); - assert_any_ok!(device.authenticate_user(DEFAULT_USER_PIN)); + 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 - require_failed_user_login(&mut device, &wrong_password); - require_failed_user_login(&mut device, &wrong_password); - require_failed_user_login(&mut device, &wrong_password); - require_failed_user_login(&mut device, DEFAULT_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 mut device = require_failed_user_login(device, DEFAULT_USER_PIN, CommandError::WrongPassword); // unblock with new PIN assert_cmd_err!( @@ -286,11 +279,11 @@ fn assert_utf8_err_or_ne(left: &str, right: Result) { #[test_device] fn factory_reset(device: DeviceWrapper) { - let mut device = device; 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 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); @@ -314,11 +307,12 @@ fn factory_reset(device: DeviceWrapper) { ); assert_ok!((), device.factory_reset(ADMIN_NEW_PASSWORD)); - assert_any_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN)); + let device = device.authenticate_admin(DEFAULT_ADMIN_PIN).unwrap().device(); let user = unwrap_ok!(device.authenticate_user(DEFAULT_USER_PIN)); assert_cmd_err!(CommandError::SlotNotProgrammed, user.get_totp_slot_name(1)); + 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)); @@ -341,7 +335,7 @@ fn build_aes_key(device: DeviceWrapper) { ); assert_ok!((), device.build_aes_key(DEFAULT_ADMIN_PIN)); - assert_any_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN)); + let mut device = device.authenticate_admin(DEFAULT_ADMIN_PIN).unwrap().device(); let pws = unwrap_ok!(device.get_password_safe(DEFAULT_USER_PIN)); assert_utf8_err_or_ne("test", pws.get_slot_name(0)); diff --git a/tests/otp.rs b/tests/otp.rs index d55d54a..c0bbecf 100644 --- a/tests/otp.rs +++ b/tests/otp.rs @@ -3,6 +3,7 @@ mod util; +use std::fmt::Debug; use std::ops::DerefMut; use nitrokey::{ @@ -35,9 +36,10 @@ enum TotpTimestampSize { U64, } -fn make_admin_test_device<'a, T>(device: &'a mut T) -> Admin<'a, T> +fn make_admin_test_device(device: T) -> Admin where T: Device, + (T, nitrokey::Error): Debug, { unwrap_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN)) } @@ -66,8 +68,7 @@ fn set_time(device: DeviceWrapper) { #[test_device] fn hotp_no_pin(device: DeviceWrapper) { - let mut device = device; - let mut admin = make_admin_test_device(&mut device); + let mut admin = make_admin_test_device(device); let config = Config::new(None, None, None, false); assert_ok!((), admin.write_config(config)); @@ -78,38 +79,36 @@ fn hotp_no_pin(device: DeviceWrapper) { check_hotp_codes(admin.deref_mut(), 5); configure_hotp(&mut admin, 0); - check_hotp_codes(&mut device, 0); + check_hotp_codes(&mut admin.device(), 0); } #[test_device] fn hotp_pin(device: DeviceWrapper) { - let mut device = device; - let mut admin = make_admin_test_device(&mut 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(&mut admin, 0); - let mut user = unwrap_ok!(device.authenticate_user(DEFAULT_USER_PIN)); + let mut user = unwrap_ok!(admin.device().authenticate_user(DEFAULT_USER_PIN)); check_hotp_codes(&mut user, 0); - assert_cmd_err!(CommandError::NotAuthorized, user.get_hotp_code(1)); + assert_cmd_err!(CommandError::NotAuthorized, user.device().get_hotp_code(1)); } #[test_device] fn hotp_slot_name(device: DeviceWrapper) { - let mut device = device; - let mut admin = make_admin_test_device(&mut 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(); 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 mut device = device; - let mut admin = make_admin_test_device(&mut 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); @@ -128,8 +127,7 @@ fn hotp_error(device: DeviceWrapper) { #[test_device] fn hotp_erase(device: DeviceWrapper) { - let mut device = device; - let mut admin = make_admin_test_device(&mut 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); @@ -139,6 +137,7 @@ fn hotp_erase(device: DeviceWrapper) { assert_ok!((), admin.erase_hotp_slot(1)); + 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); @@ -175,8 +174,7 @@ fn check_totp_codes(device: &mut GenerateOtp, factor: u64, timestamp_size: TotpT #[test_device] fn totp_no_pin(device: DeviceWrapper) { - let mut device = device; - let mut admin = make_admin_test_device(&mut device); + let mut admin = make_admin_test_device(device); let config = Config::new(None, None, None, false); assert_ok!((), admin.write_config(config)); @@ -187,15 +185,14 @@ fn totp_no_pin(device: DeviceWrapper) { check_totp_codes(admin.deref_mut(), 2, TotpTimestampSize::U32); configure_totp(&mut admin, 1); - check_totp_codes(&mut device, 1, TotpTimestampSize::U32); + 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 mut device = device; - let mut admin = make_admin_test_device(&mut device); + let mut admin = make_admin_test_device(device); let config = Config::new(None, None, None, false); assert_ok!((), admin.write_config(config)); @@ -206,45 +203,43 @@ fn totp_no_pin_64(device: Pro) { check_totp_codes(admin.deref_mut(), 2, TotpTimestampSize::U64); configure_totp(&mut admin, 1); - check_totp_codes(&mut device, 1, TotpTimestampSize::U64); + check_totp_codes(&mut admin.device(), 1, TotpTimestampSize::U64); } #[test_device] fn totp_pin(device: DeviceWrapper) { - let mut device = device; - let mut admin = make_admin_test_device(&mut 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(&mut admin, 1); - let mut user = unwrap_ok!(device.authenticate_user(DEFAULT_USER_PIN)); + 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.get_totp_code(1)); + assert_cmd_err!(CommandError::NotAuthorized, user.device().get_totp_code(1)); } #[test_device] // See comment for totp_no_pin_64. fn totp_pin_64(device: Pro) { - let mut device = device; - let mut admin = make_admin_test_device(&mut 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(&mut admin, 1); - let mut user = unwrap_ok!(admin.authenticate_user(DEFAULT_USER_PIN)); + 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, device.get_totp_code(1)); + assert_cmd_err!(CommandError::NotAuthorized, user.device().get_totp_code(1)); } #[test_device] fn totp_slot_name(device: DeviceWrapper) { - let mut device = device; - let mut admin = make_admin_test_device(&mut 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)); + let device = admin.device(); let result = device.get_totp_slot_name(1); assert_ok!("test-totp", result); let result = device.get_totp_slot_name(16); @@ -253,8 +248,7 @@ fn totp_slot_name(device: DeviceWrapper) { #[test_device] fn totp_error(device: DeviceWrapper) { - let mut device = device; - let mut admin = make_admin_test_device(&mut 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); @@ -273,8 +267,7 @@ fn totp_error(device: DeviceWrapper) { #[test_device] fn totp_erase(device: DeviceWrapper) { - let mut device = device; - let mut admin = make_admin_test_device(&mut 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); @@ -284,6 +277,7 @@ fn totp_erase(device: DeviceWrapper) { assert_ok!((), admin.erase_totp_slot(1)); + let device = admin.device(); let result = device.get_totp_slot_name(1); assert_cmd_err!(CommandError::SlotNotProgrammed, result); let result = device.get_totp_code(1); diff --git a/tests/util/mod.rs b/tests/util/mod.rs index 5bd19d1..f2b20ec 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -3,7 +3,7 @@ #[macro_export] macro_rules! unwrap_ok { - ($val:expr) => { + ($val:expr) => {{ match $val { Ok(val) => val, Err(err) => panic!( @@ -13,12 +13,12 @@ macro_rules! unwrap_ok { err ), } - }; + }}; } #[macro_export] macro_rules! assert_any_ok { - ($val:expr) => { + ($val:expr) => {{ match &$val { Ok(_) => {} Err(err) => panic!( @@ -28,12 +28,12 @@ macro_rules! assert_any_ok { err ), } - }; + }}; } #[macro_export] macro_rules! assert_ok { - ($left:expr, $right:expr) => { + ($left:expr, $right:expr) => {{ match &$right { Ok(right) => match &$left { left => { @@ -54,7 +54,7 @@ macro_rules! assert_ok { $left, right_err ), } - }; + }}; } #[macro_export] -- cgit v1.2.3 From d95355e3d76c0c0022629e635f36a2dc325c0af2 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Tue, 5 Feb 2019 12:48:01 +0000 Subject: Revert "Store mutable reference to Device in PasswordSafe" This reverts commit 13006c00dcbd570cf8347d89557834e320427377. --- TODO.md | 1 + src/pws.rs | 24 ++++++++++++------------ tests/device.rs | 41 ++++++++++++++++++++++++++++++++--------- 3 files changed, 45 insertions(+), 21 deletions(-) (limited to 'tests') diff --git a/TODO.md b/TODO.md index d6a3509..1ff723d 100644 --- a/TODO.md +++ b/TODO.md @@ -13,6 +13,7 @@ SPDX-License-Identifier: MIT - Clear passwords from memory. - Lock password safe in `PasswordSafe::drop()` (see [nitrokey-storage-firmware issue 65][]). +- Disable creation of multiple password safes at the same time. - Check timing in Storage tests. - Consider restructuring `device::StorageStatus`. diff --git a/src/pws.rs b/src/pws.rs index a5b9d33..371de6e 100644 --- a/src/pws.rs +++ b/src/pws.rs @@ -18,7 +18,8 @@ pub const SLOT_COUNT: u8 = 16; /// The password safe stores a tuple consisting of a name, a login and a password on a slot. The /// number of available slots is [`SLOT_COUNT`][]. The slots are addressed starting with zero. To /// retrieve a password safe from a Nitrokey device, use the [`get_password_safe`][] method from -/// the [`GetPasswordSafe`][] trait. +/// the [`GetPasswordSafe`][] trait. Note that the device must live at least as long as the +/// password safe. /// /// Once the password safe has been unlocked, it can be accessed without a password. Therefore it /// is mandatory to call [`lock`][] on the corresponding device after the password store is used. @@ -57,17 +58,21 @@ pub const SLOT_COUNT: u8 = 16; /// [`GetPasswordSafe`]: trait.GetPasswordSafe.html #[derive(Debug)] pub struct PasswordSafe<'a> { - device: &'a mut dyn Device, + _device: &'a dyn Device, } /// Provides access to a [`PasswordSafe`][]. /// +/// The device that implements this trait must always live at least as long as a password safe +/// retrieved from it. +/// /// [`PasswordSafe`]: struct.PasswordSafe.html pub trait GetPasswordSafe { /// Enables and returns the password safe. /// - /// It is mandatory to lock the underlying device using [`lock`][] after the password safe has - /// been used. Otherwise, other applications can access the password store without + /// The underlying device must always live at least as long as a password safe retrieved from + /// it. It is mandatory to lock the underlying device using [`lock`][] after the password safe + /// has been used. Otherwise, other applications can access the password store without /// authentication. /// /// If this method returns an `AesDecryptionFailed` (Nitrokey Pro) or `Unknown` (Nitrokey @@ -116,17 +121,12 @@ pub trait GetPasswordSafe { } fn get_password_safe<'a>( - device: &'a mut dyn Device, + device: &'a dyn Device, user_pin: &str, ) -> Result, Error> { let user_pin_string = get_cstring(user_pin)?; - let result = get_command_result(unsafe { - nitrokey_sys::NK_enable_password_safe(user_pin_string.as_ptr()) - }); - match result { - Ok(()) => Ok(PasswordSafe { device }), - Err(err) => Err(err), - } + get_command_result(unsafe { nitrokey_sys::NK_enable_password_safe(user_pin_string.as_ptr()) }) + .map(|_| PasswordSafe { _device: device }) } fn get_pws_result(s: String) -> Result { diff --git a/tests/device.rs b/tests/device.rs index 6a3683b..5c52024 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -156,7 +156,10 @@ fn change_user_pin(device: DeviceWrapper) { let device = device.authenticate_user(USER_NEW_PASSWORD).unwrap_err().0; let mut device = device; - assert_ok!((), device.change_user_pin(DEFAULT_USER_PIN, USER_NEW_PASSWORD)); + assert_ok!( + (), + device.change_user_pin(DEFAULT_USER_PIN, USER_NEW_PASSWORD) + ); let device = device.authenticate_user(DEFAULT_USER_PIN).unwrap_err().0; let device = device @@ -179,7 +182,10 @@ fn change_user_pin(device: DeviceWrapper) { #[test_device] fn change_admin_pin(device: DeviceWrapper) { - let device = device.authenticate_admin(DEFAULT_ADMIN_PIN).unwrap().device(); + let device = device + .authenticate_admin(DEFAULT_ADMIN_PIN) + .unwrap() + .device(); let mut device = device.authenticate_admin(ADMIN_NEW_PASSWORD).unwrap_err().0; assert_ok!( @@ -203,7 +209,10 @@ fn change_admin_pin(device: DeviceWrapper) { device.change_admin_pin(ADMIN_NEW_PASSWORD, DEFAULT_ADMIN_PIN) ); - let device = device.authenticate_admin(DEFAULT_ADMIN_PIN).unwrap().device(); + let device = device + .authenticate_admin(DEFAULT_ADMIN_PIN) + .unwrap() + .device(); device.authenticate_admin(ADMIN_NEW_PASSWORD).unwrap_err(); } @@ -225,7 +234,10 @@ where #[test_device] fn unlock_user_pin(device: DeviceWrapper) { let mut device = device.authenticate_user(DEFAULT_USER_PIN).unwrap().device(); - assert_ok!((), device.unlock_user_pin(DEFAULT_ADMIN_PIN, DEFAULT_USER_PIN)); + assert_ok!( + (), + device.unlock_user_pin(DEFAULT_ADMIN_PIN, DEFAULT_USER_PIN) + ); assert_cmd_err!( CommandError::WrongPassword, device.unlock_user_pin(DEFAULT_USER_PIN, DEFAULT_USER_PIN) @@ -236,21 +248,26 @@ fn unlock_user_pin(device: DeviceWrapper) { 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 mut device = require_failed_user_login(device, DEFAULT_USER_PIN, 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(DEFAULT_USER_PIN, DEFAULT_USER_PIN) ); - assert_ok!((), device.unlock_user_pin(DEFAULT_ADMIN_PIN, DEFAULT_USER_PIN)); + 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 mut device = require_failed_user_login(device, DEFAULT_USER_PIN, CommandError::WrongPassword); + let mut device = + require_failed_user_login(device, DEFAULT_USER_PIN, CommandError::WrongPassword); // unblock with new PIN assert_cmd_err!( @@ -307,7 +324,10 @@ fn factory_reset(device: DeviceWrapper) { ); assert_ok!((), device.factory_reset(ADMIN_NEW_PASSWORD)); - let device = device.authenticate_admin(DEFAULT_ADMIN_PIN).unwrap().device(); + let device = device + .authenticate_admin(DEFAULT_ADMIN_PIN) + .unwrap() + .device(); let user = unwrap_ok!(device.authenticate_user(DEFAULT_USER_PIN)); assert_cmd_err!(CommandError::SlotNotProgrammed, user.get_totp_slot_name(1)); @@ -335,7 +355,10 @@ fn build_aes_key(device: DeviceWrapper) { ); assert_ok!((), device.build_aes_key(DEFAULT_ADMIN_PIN)); - let mut device = device.authenticate_admin(DEFAULT_ADMIN_PIN).unwrap().device(); + let mut device = device + .authenticate_admin(DEFAULT_ADMIN_PIN) + .unwrap() + .device(); let pws = unwrap_ok!(device.get_password_safe(DEFAULT_USER_PIN)); assert_utf8_err_or_ne("test", pws.get_slot_name(0)); -- cgit v1.2.3 From 588066f415e956fdcd2c6f6216c52b25911a3b1d Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 27 Jan 2019 15:43:32 +0000 Subject: Add Manager struct to manage Nitrokey connections As part of the connection refactoring, we introduce the Manager struct that deals with connection management. To make sure there can be only once instance of the manager, we add a global static Mutex that holds the single Manager instance. We use the struct to ensure that the user can only connect to one device at a time. This also changes the Error::PoisonError variant to store the sync::PoisonError. This allows the user to call into_inner on the PoisonError to retrieve the MutexGuard and to ignore the error (for example useful during testing). --- CHANGELOG.md | 1 + Cargo.toml | 1 + src/error.rs | 16 +++++++-------- src/lib.rs | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/lib.rs | 16 +++++++++++++++ 5 files changed, 92 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 046b609..e67fd81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ SPDX-License-Identifier: MIT `nitrokey-test-state` dependency in version 0.1.0. - Refactor connection management: - Add `ConcurrentAccessError` and `PoisonError` `Error` variants. + - Add the `Manager` struct that manages connections to Nitrokey devices. # v0.3.4 (2019-01-20) - Fix authentication methods that assumed that `char` is signed. diff --git a/Cargo.toml b/Cargo.toml index d26bd88..7daadd1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ license = "MIT" exclude = [".builds/*"] [dependencies] +lazy_static = "1.2.0" libc = "0.2" nitrokey-sys = "3.5" rand_core = {version = "0.3", default-features = false, features = ["std"] } diff --git a/src/error.rs b/src/error.rs index c6b19db..b84f5eb 100644 --- a/src/error.rs +++ b/src/error.rs @@ -21,7 +21,7 @@ pub enum Error { /// A library usage error. LibraryError(LibraryError), /// An error that occurred due to a poisoned lock. - PoisonError, + PoisonError(sync::PoisonError>), /// An error that occurred during random number generation. RandError(Box), /// An error that is caused by an unexpected value returned by libnitrokey. @@ -70,14 +70,14 @@ impl From for Error { } } -impl From> for Error { - fn from(_error: sync::PoisonError) -> Self { - Error::PoisonError +impl From>> for Error { + fn from(error: sync::PoisonError>) -> Self { + Error::PoisonError(error) } } -impl From> for Error { - fn from(error: sync::TryLockError) -> Self { +impl From>> for Error { + fn from(error: sync::TryLockError>) -> Self { match error { sync::TryLockError::Poisoned(err) => err.into(), sync::TryLockError::WouldBlock => Error::ConcurrentAccessError, @@ -98,7 +98,7 @@ impl error::Error for Error { Error::CommunicationError(ref err) => Some(err), Error::ConcurrentAccessError => None, Error::LibraryError(ref err) => Some(err), - Error::PoisonError => None, + Error::PoisonError(ref err) => Some(err), Error::RandError(ref err) => Some(err.as_ref()), Error::UnexpectedError => None, Error::UnknownError(_) => None, @@ -114,7 +114,7 @@ impl fmt::Display for Error { Error::CommunicationError(ref err) => write!(f, "Communication error: {}", err), Error::ConcurrentAccessError => write!(f, "Internal error: concurrent access"), Error::LibraryError(ref err) => write!(f, "Library error: {}", err), - Error::PoisonError => write!(f, "Internal error: poisoned lock"), + Error::PoisonError(_) => write!(f, "Internal error: poisoned lock"), Error::RandError(ref err) => write!(f, "RNG error: {}", err), Error::UnexpectedError => write!(f, "An unexpected error occurred"), Error::UnknownError(ref err) => write!(f, "Unknown error: {}", err), diff --git a/src/lib.rs b/src/lib.rs index c35829c..573f45f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,6 +89,9 @@ #![warn(missing_docs, rust_2018_compatibility, rust_2018_idioms, unused)] +#[macro_use(lazy_static)] +extern crate lazy_static; + mod auth; mod config; mod device; @@ -98,6 +101,8 @@ mod pws; mod util; use std::fmt; +use std::marker; +use std::sync; use nitrokey_sys; @@ -117,6 +122,10 @@ pub const DEFAULT_ADMIN_PIN: &str = "12345678"; /// The default user PIN for all Nitrokey devices. pub const DEFAULT_USER_PIN: &str = "123456"; +lazy_static! { + static ref MANAGER: sync::Mutex = sync::Mutex::new(Manager::new()); +} + /// A version of the libnitrokey library. /// /// Use the [`get_library_version`](fn.get_library_version.html) function to query the library @@ -147,6 +156,63 @@ impl fmt::Display for Version { } } +/// A manager for connections to Nitrokey devices. +/// +/// Currently, libnitrokey only provides access to one Nitrokey device at the same time. This +/// manager struct makes sure that `nitrokey-rs` does not try to connect to two devices at the same +/// time. +/// +/// To obtain an instance of this manager, use the [`take`][] function. +/// +/// [`take`]: fn.take.html +#[derive(Debug)] +pub struct Manager { + marker: marker::PhantomData<()>, +} + +impl Manager { + fn new() -> Self { + Manager { + marker: marker::PhantomData, + } + } +} + +/// Take an instance of the connection manager, blocking until an instance is available. +/// +/// There may only be one [`Manager`][] instance at the same time. If there already is an +/// instance, this method blocks. If you want a non-blocking version, use [`take`][]. +/// +/// # Errors +/// +/// - [`PoisonError`][] if the lock is poisoned +/// +/// [`take`]: fn.take.html +/// [`PoisonError`]: struct.Error.html#variant.PoisonError +/// [`Manager`]: struct.Manager.html +pub fn take_blocking() -> Result, Error> { + MANAGER.lock().map_err(Into::into) +} + +/// Try to take an instance of the connection manager. +/// +/// There may only be one [`Manager`][] instance at the same time. If there already is an +/// instance, a [`ConcurrentAccessError`][] is returned. If you want a blocking version, use +/// [`take_blocking`][]. +/// +/// # Errors +/// +/// - [`ConcurrentAccessError`][] if the token for the `Manager` instance cannot be locked +/// - [`PoisonError`][] if the lock is poisoned +/// +/// [`take_blocking`]: fn.take_blocking.html +/// [`ConcurrentAccessError`]: struct.Error.html#variant.ConcurrentAccessError +/// [`PoisonError`]: struct.Error.html#variant.PoisonError +/// [`Manager`]: struct.Manager.html +pub fn take() -> Result, Error> { + MANAGER.try_lock().map_err(Into::into) +} + /// Enables or disables debug output. Calling this method with `true` is equivalent to setting the /// log level to `Debug`; calling it with `false` is equivalent to the log level `Error` (see /// [`set_log_level`][]). diff --git a/tests/lib.rs b/tests/lib.rs index 8ab75f6..25aae0f 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -10,3 +10,19 @@ fn get_library_version() { assert!(version.git.is_empty() || version.git.starts_with("v")); assert!(version.major > 0); } + +#[test] +fn take_manager() { + assert!(nitrokey::take().is_ok()); + + let result = nitrokey::take(); + assert!(result.is_ok()); + let result2 = nitrokey::take(); + match result2 { + Ok(_) => panic!("Expected error, got Ok(_)!"), + Err(nitrokey::Error::ConcurrentAccessError) => {} + Err(err) => panic!("Expected ConcurrentAccessError, got {}", err), + } + drop(result); + assert!(nitrokey::take().is_ok()); +} -- cgit v1.2.3 From bd7c7a5fdf0ae66a1ff2f00beb5ed4c2e6994ca1 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 27 Jan 2019 18:07:59 +0000 Subject: Move the connect_model function into Manager As part of the connection refactoring, this patch moves the connect_model function to the Manager struct. As the connect_model function is not used by nitrokey-test, it is removed. --- CHANGELOG.md | 1 + src/device.rs | 33 ++------------------------------- src/lib.rs | 36 ++++++++++++++++++++++++++++++++++-- tests/device.rs | 12 ++++++++---- 4 files changed, 45 insertions(+), 37 deletions(-) (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index a3a9afe..ffc52e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ SPDX-License-Identifier: MIT - Add `ConcurrentAccessError` and `PoisonError` `Error` variants. - Add the `Manager` struct that manages connections to Nitrokey devices. - Deprecate the `connect` function. + - Remove the `connect_model` function. # v0.3.4 (2019-01-20) - Fix authentication methods that assumed that `char` is signed. diff --git a/src/device.rs b/src/device.rs index 653c5d1..f28558d 100644 --- a/src/device.rs +++ b/src/device.rs @@ -652,35 +652,6 @@ pub fn connect() -> Result { crate::take()?.connect().map_err(Into::into) } -/// Connects to a Nitrokey device of the given model. -/// -/// # Errors -/// -/// - [`NotConnected`][] if no Nitrokey device of the given model is connected -/// -/// # Example -/// -/// ``` -/// use nitrokey::DeviceWrapper; -/// use nitrokey::Model; -/// -/// fn do_something(device: DeviceWrapper) {} -/// -/// match nitrokey::connect_model(Model::Pro) { -/// Ok(device) => do_something(device), -/// Err(err) => eprintln!("Could not connect to a Nitrokey Pro: {}", err), -/// } -/// ``` -/// -/// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected -pub fn connect_model(model: Model) -> Result { - if connect_enum(model) { - Ok(create_device_wrapper(model)) - } else { - Err(CommunicationError::NotConnected.into()) - } -} - fn get_connected_model() -> Option { match unsafe { nitrokey_sys::NK_get_device_model() } { nitrokey_sys::NK_device_model_NK_PRO => Some(Model::Pro), @@ -689,7 +660,7 @@ fn get_connected_model() -> Option { } } -fn create_device_wrapper(model: Model) -> DeviceWrapper { +pub(crate) fn create_device_wrapper(model: Model) -> DeviceWrapper { match model { Model::Pro => Pro::new().into(), Model::Storage => Storage::new().into(), @@ -700,7 +671,7 @@ pub(crate) fn get_connected_device() -> Option { get_connected_model().map(create_device_wrapper) } -fn connect_enum(model: Model) -> bool { +pub(crate) fn connect_enum(model: Model) -> bool { let model = match model { Model::Storage => nitrokey_sys::NK_device_model_NK_STORAGE, Model::Pro => nitrokey_sys::NK_device_model_NK_PRO, diff --git a/src/lib.rs b/src/lib.rs index 9f88be3..784dc6f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -110,8 +110,8 @@ pub use crate::auth::{Admin, Authenticate, User}; pub use crate::config::Config; #[allow(deprecated)] pub use crate::device::{ - connect, connect_model, Device, DeviceWrapper, Model, Pro, SdCardData, Storage, - StorageProductionInfo, StorageStatus, VolumeMode, VolumeStatus, + connect, Device, DeviceWrapper, Model, Pro, SdCardData, Storage, StorageProductionInfo, + StorageStatus, VolumeMode, VolumeStatus, }; pub use crate::error::{CommandError, CommunicationError, Error, LibraryError}; pub use crate::otp::{ConfigureOtp, GenerateOtp, OtpMode, OtpSlotData}; @@ -214,6 +214,38 @@ impl Manager { Err(CommunicationError::NotConnected.into()) } } + + /// Connects to a Nitrokey device of the given model. + /// + /// # Errors + /// + /// - [`NotConnected`][] if no Nitrokey device of the given model is connected + /// + /// # Example + /// + /// ``` + /// use nitrokey::DeviceWrapper; + /// use nitrokey::Model; + /// + /// fn do_something(device: DeviceWrapper) {} + /// + /// # fn main() -> Result<(), nitrokey::Error> { + /// match nitrokey::take()?.connect_model(Model::Pro) { + /// Ok(device) => do_something(device), + /// Err(err) => println!("Could not connect to a Nitrokey Pro: {}", err), + /// } + /// # Ok(()) + /// # } + /// ``` + /// + /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected + pub fn connect_model(&mut self, model: Model) -> Result { + if device::connect_enum(model) { + Ok(device::create_device_wrapper(model)) + } else { + Err(CommunicationError::NotConnected.into()) + } + } } /// Take an instance of the connection manager, blocking until an instance is available. diff --git a/tests/device.rs b/tests/device.rs index 5c52024..527d6f2 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -36,11 +36,15 @@ fn connect_no_device() { assert_cmu_err!(CommunicationError::NotConnected, nitrokey::connect()); assert_cmu_err!( CommunicationError::NotConnected, - nitrokey::connect_model(nitrokey::Model::Pro) + nitrokey::take() + .unwrap() + .connect_model(nitrokey::Model::Pro) ); assert_cmu_err!( CommunicationError::NotConnected, - nitrokey::connect_model(nitrokey::Model::Storage) + nitrokey::take() + .unwrap() + .connect_model(nitrokey::Model::Storage) ); assert_cmu_err!(CommunicationError::NotConnected, nitrokey::Pro::connect()); assert_cmu_err!( @@ -55,7 +59,7 @@ fn connect_pro(device: Pro) { drop(device); assert_any_ok!(nitrokey::connect()); - assert_any_ok!(nitrokey::connect_model(nitrokey::Model::Pro)); + assert_any_ok!(nitrokey::take().unwrap().connect_model(nitrokey::Model::Pro)); assert_any_ok!(nitrokey::Pro::connect()); } @@ -65,7 +69,7 @@ fn connect_storage(device: Storage) { drop(device); assert_any_ok!(nitrokey::connect()); - assert_any_ok!(nitrokey::connect_model(nitrokey::Model::Storage)); + assert_any_ok!(nitrokey::take().unwrap().connect_model(nitrokey::Model::Storage)); assert_any_ok!(nitrokey::Storage::connect()); } -- cgit v1.2.3 From 379bc798477a1de7ffda923c5d10ca63aebae25f Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 27 Jan 2019 18:21:08 +0000 Subject: Move {Pro, Storage}::connect into Manager As part of the connection refactoring, this patch moves the connect methods of the Pro and Storage structs into the Manager struct. To maintain compatibility with nitrokey-test, the old methods are not removed but marked as deprecated. --- CHANGELOG.md | 2 +- src/device.rs | 25 ++++++++++------------- src/lib.rs | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/device.rs | 4 ++-- 4 files changed, 75 insertions(+), 18 deletions(-) (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index ffc52e6..41b529a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,7 +44,7 @@ SPDX-License-Identifier: MIT - Refactor connection management: - Add `ConcurrentAccessError` and `PoisonError` `Error` variants. - Add the `Manager` struct that manages connections to Nitrokey devices. - - Deprecate the `connect` function. + - Deprecate `connect`, `Pro::connect` and `Storage::connect`. - Remove the `connect_model` function. # v0.3.4 (2019-01-20) diff --git a/src/device.rs b/src/device.rs index f28558d..758691d 100644 --- a/src/device.rs +++ b/src/device.rs @@ -9,7 +9,7 @@ use nitrokey_sys; use crate::auth::Authenticate; use crate::config::{Config, RawConfig}; -use crate::error::{CommunicationError, Error}; +use crate::error::Error; use crate::otp::GenerateOtp; use crate::pws::GetPasswordSafe; use crate::util::{ @@ -755,16 +755,12 @@ impl Pro { /// ``` /// /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected + #[deprecated(since = "0.4.0", note = "use `nitrokey::Manager::connect_pro` instead")] pub fn connect() -> Result { - // TODO: maybe Option instead of Result? - if connect_enum(Model::Pro) { - Ok(Pro::new()) - } else { - Err(CommunicationError::NotConnected.into()) - } + crate::take()?.connect_pro().map_err(Into::into) } - fn new() -> Pro { + pub(crate) fn new() -> Pro { Pro { marker: marker::PhantomData, } @@ -808,16 +804,15 @@ impl Storage { /// ``` /// /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected + #[deprecated( + since = "0.4.0", + note = "use `nitrokey::Manager::connect_storage` instead" + )] pub fn connect() -> Result { - // TODO: maybe Option instead of Result? - if connect_enum(Model::Storage) { - Ok(Storage::new()) - } else { - Err(CommunicationError::NotConnected.into()) - } + crate::take()?.connect_storage().map_err(Into::into) } - fn new() -> Storage { + pub(crate) fn new() -> Storage { Storage { marker: marker::PhantomData, } diff --git a/src/lib.rs b/src/lib.rs index 784dc6f..dc3432a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -246,6 +246,68 @@ impl Manager { Err(CommunicationError::NotConnected.into()) } } + + /// Connects to a Nitrokey Pro. + /// + /// # Errors + /// + /// - [`NotConnected`][] if no Nitrokey device of the given model is connected + /// + /// # Example + /// + /// ``` + /// use nitrokey::Pro; + /// + /// fn use_pro(device: Pro) {} + /// + /// # fn main() -> Result<(), nitrokey::Error> { + /// match nitrokey::take()?.connect_pro() { + /// Ok(device) => use_pro(device), + /// Err(err) => println!("Could not connect to the Nitrokey Pro: {}", err), + /// } + /// # Ok(()) + /// # } + /// ``` + /// + /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected + pub fn connect_pro(&mut self) -> Result { + if device::connect_enum(device::Model::Pro) { + Ok(device::Pro::new()) + } else { + Err(CommunicationError::NotConnected.into()) + } + } + + /// Connects to a Nitrokey Storage. + /// + /// # Errors + /// + /// - [`NotConnected`][] if no Nitrokey device of the given model is connected + /// + /// # Example + /// + /// ``` + /// use nitrokey::Storage; + /// + /// fn use_storage(device: Storage) {} + /// + /// # fn main() -> Result<(), nitrokey::Error> { + /// match nitrokey::take()?.connect_storage() { + /// Ok(device) => use_storage(device), + /// Err(err) => println!("Could not connect to the Nitrokey Storage: {}", err), + /// } + /// # Ok(()) + /// # } + /// ``` + /// + /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected + pub fn connect_storage(&mut self) -> Result { + if device::connect_enum(Model::Storage) { + Ok(Storage::new()) + } else { + Err(CommunicationError::NotConnected.into()) + } + } } /// Take an instance of the connection manager, blocking until an instance is available. diff --git a/tests/device.rs b/tests/device.rs index 527d6f2..b377f2e 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -60,7 +60,7 @@ fn connect_pro(device: Pro) { assert_any_ok!(nitrokey::connect()); assert_any_ok!(nitrokey::take().unwrap().connect_model(nitrokey::Model::Pro)); - assert_any_ok!(nitrokey::Pro::connect()); + assert_any_ok!(nitrokey::take().unwrap().connect_pro()); } #[test_device] @@ -70,7 +70,7 @@ fn connect_storage(device: Storage) { assert_any_ok!(nitrokey::connect()); assert_any_ok!(nitrokey::take().unwrap().connect_model(nitrokey::Model::Storage)); - assert_any_ok!(nitrokey::Storage::connect()); + assert_any_ok!(nitrokey::take().unwrap().connect_storage()); } fn assert_empty_serial_number() { -- cgit v1.2.3 From 12fa62483cf45d868099d5d4020333af492eebde Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Tue, 9 Jul 2019 08:09:02 +0000 Subject: Introduce into_manager for Device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To enable applications like nitrokey-test to go back to a manager instance from a Device instance, we add the into_manager function to the Device trait. To do that, we have to keep track of the Manager’s lifetime by adding a lifetime to Device (and then to some other traits that use Device). --- CHANGELOG.md | 1 + src/auth.rs | 69 +++++++++++++++++++++++++++++++-------------------------- src/device.rs | 64 +++++++++++++++++++++++++++++++++++++++++++--------- src/error.rs | 2 +- src/pws.rs | 30 ++++++++++++------------- tests/device.rs | 16 ++++++++----- tests/otp.rs | 4 ++-- tests/pws.rs | 4 ++-- 8 files changed, 123 insertions(+), 67 deletions(-) (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 88e68dd..06769bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ SPDX-License-Identifier: MIT - Add `ConcurrentAccessError` and `PoisonError` `Error` variants. - Add the `Manager` struct that manages connections to Nitrokey devices. - Remove `connect`, `connect_model`, `Pro::connect` and `Storage::connect`. + - Add the `into_manager` function to the `Device` trait. # v0.3.4 (2019-01-20) - Fix authentication methods that assumed that `char` is signed. diff --git a/src/auth.rs b/src/auth.rs index 2ed7bfc..829d083 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -1,6 +1,7 @@ // Copyright (C) 2018-2019 Robin Krahl // SPDX-License-Identifier: MIT +use std::marker; use std::ops; use std::os::raw::c_char; use std::os::raw::c_int; @@ -18,7 +19,7 @@ static TEMPORARY_PASSWORD_LENGTH: usize = 25; /// Provides methods to authenticate as a user or as an admin using a PIN. The authenticated /// methods will consume the current device instance. On success, they return the authenticated /// device. Otherwise, they return the current unauthenticated device and the error code. -pub trait Authenticate { +pub trait Authenticate<'a> { /// Performs user authentication. This method consumes the device. If successful, an /// authenticated device is returned. Otherwise, the current unauthenticated device and the /// error are returned. @@ -61,9 +62,9 @@ pub trait Authenticate { /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`RngError`]: enum.CommandError.html#variant.RngError /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn authenticate_user(self, password: &str) -> Result, (Self, Error)> + fn authenticate_user(self, password: &str) -> Result, (Self, Error)> where - Self: Device + Sized; + Self: Device<'a> + Sized; /// Performs admin authentication. This method consumes the device. If successful, an /// authenticated device is returned. Otherwise, the current unauthenticated device and the @@ -107,9 +108,9 @@ pub trait Authenticate { /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`RngError`]: enum.CommandError.html#variant.RngError /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> + fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> where - Self: Device + Sized; + Self: Device<'a> + Sized; } trait AuthenticatedDevice { @@ -128,9 +129,10 @@ trait AuthenticatedDevice { /// [`authenticate_admin`]: trait.Authenticate.html#method.authenticate_admin /// [`device`]: #method.device #[derive(Debug)] -pub struct User { +pub struct User<'a, T: Device<'a>> { device: T, temp_password: Vec, + marker: marker::PhantomData<&'a T>, } /// A Nitrokey device with admin authentication. @@ -143,14 +145,15 @@ pub struct User { /// [`authenticate_admin`]: trait.Authenticate.html#method.authenticate_admin /// [`device`]: #method.device #[derive(Debug)] -pub struct Admin { +pub struct Admin<'a, T: Device<'a>> { device: T, temp_password: Vec, + marker: marker::PhantomData<&'a T>, } -fn authenticate(device: D, password: &str, callback: T) -> Result +fn authenticate<'a, D, A, T>(device: D, password: &str, callback: T) -> Result where - D: Device, + D: Device<'a>, A: AuthenticatedDevice, T: Fn(*const c_char, *const c_char) -> c_int, { @@ -174,9 +177,9 @@ fn authenticate_user_wrapper<'a, T, C>( device: T, constructor: C, password: &str, -) -> Result>, (DeviceWrapper<'a>, Error)> +) -> Result>, (DeviceWrapper<'a>, Error)> where - T: Device, + T: Device<'a> + 'a, C: Fn(T) -> DeviceWrapper<'a>, { let result = device.authenticate_user(password); @@ -190,9 +193,9 @@ fn authenticate_admin_wrapper<'a, T, C>( device: T, constructor: C, password: &str, -) -> Result>, (DeviceWrapper<'a>, Error)> +) -> Result>, (DeviceWrapper<'a>, Error)> where - T: Device, + T: Device<'a> + 'a, C: Fn(T) -> DeviceWrapper<'a>, { let result = device.authenticate_admin(password); @@ -202,7 +205,7 @@ where } } -impl User { +impl<'a, T: Device<'a>> User<'a, 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 /// Nitrokey. @@ -211,7 +214,7 @@ impl User { } } -impl ops::Deref for User { +impl<'a, T: Device<'a>> ops::Deref for User<'a, T> { type Target = T; fn deref(&self) -> &Self::Target { @@ -219,13 +222,13 @@ impl ops::Deref for User { } } -impl ops::DerefMut for User { +impl<'a, T: Device<'a>> ops::DerefMut for User<'a, T> { fn deref_mut(&mut self) -> &mut T { &mut self.device } } -impl GenerateOtp for User { +impl<'a, T: Device<'a>> GenerateOtp for User<'a, T> { fn get_hotp_code(&mut self, slot: u8) -> Result { result_from_string(unsafe { nitrokey_sys::NK_get_hotp_code_PIN(slot, self.temp_password_ptr()) @@ -239,11 +242,12 @@ impl GenerateOtp for User { } } -impl AuthenticatedDevice for User { +impl<'a, T: Device<'a>> AuthenticatedDevice for User<'a, T> { fn new(device: T, temp_password: Vec) -> Self { User { device, temp_password, + marker: marker::PhantomData, } } @@ -252,7 +256,7 @@ impl AuthenticatedDevice for User { } } -impl ops::Deref for Admin { +impl<'a, T: Device<'a>> ops::Deref for Admin<'a, T> { type Target = T; fn deref(&self) -> &Self::Target { @@ -260,13 +264,13 @@ impl ops::Deref for Admin { } } -impl ops::DerefMut for Admin { +impl<'a, T: Device<'a>> ops::DerefMut for Admin<'a, T> { fn deref_mut(&mut self) -> &mut T { &mut self.device } } -impl Admin { +impl<'a, T: Device<'a>> Admin<'a, 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 /// Nitrokey. @@ -316,7 +320,7 @@ impl Admin { } } -impl ConfigureOtp for Admin { +impl<'a, T: Device<'a>> ConfigureOtp for Admin<'a, T> { fn write_hotp_slot(&mut self, data: OtpSlotData, counter: u64) -> Result<(), Error> { let raw_data = RawOtpSlotData::new(data)?; get_command_result(unsafe { @@ -364,11 +368,12 @@ impl ConfigureOtp for Admin { } } -impl AuthenticatedDevice for Admin { +impl<'a, T: Device<'a>> AuthenticatedDevice for Admin<'a, T> { fn new(device: T, temp_password: Vec) -> Self { Admin { device, temp_password, + marker: marker::PhantomData, } } @@ -377,8 +382,8 @@ impl AuthenticatedDevice for Admin { } } -impl<'a> Authenticate for DeviceWrapper<'a> { - fn authenticate_user(self, password: &str) -> Result, (Self, Error)> { +impl<'a> Authenticate<'a> for DeviceWrapper<'a> { + fn authenticate_user(self, password: &str) -> Result, (Self, Error)> { match self { DeviceWrapper::Storage(storage) => { authenticate_user_wrapper(storage, DeviceWrapper::Storage, password) @@ -387,7 +392,7 @@ impl<'a> Authenticate for DeviceWrapper<'a> { } } - fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> { + fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> { match self { DeviceWrapper::Storage(storage) => { authenticate_admin_wrapper(storage, DeviceWrapper::Storage, password) @@ -399,28 +404,28 @@ impl<'a> Authenticate for DeviceWrapper<'a> { } } -impl<'a> Authenticate for Pro<'a> { - fn authenticate_user(self, password: &str) -> Result, (Self, Error)> { +impl<'a> Authenticate<'a> for Pro<'a> { + fn authenticate_user(self, password: &str) -> Result, (Self, Error)> { authenticate(self, password, |password_ptr, temp_password_ptr| unsafe { nitrokey_sys::NK_user_authenticate(password_ptr, temp_password_ptr) }) } - fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> { + fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> { authenticate(self, password, |password_ptr, temp_password_ptr| unsafe { nitrokey_sys::NK_first_authenticate(password_ptr, temp_password_ptr) }) } } -impl<'a> Authenticate for Storage<'a> { - fn authenticate_user(self, password: &str) -> Result, (Self, Error)> { +impl<'a> Authenticate<'a> for Storage<'a> { + fn authenticate_user(self, password: &str) -> Result, (Self, Error)> { authenticate(self, password, |password_ptr, temp_password_ptr| unsafe { nitrokey_sys::NK_user_authenticate(password_ptr, temp_password_ptr) }) } - fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> { + fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> { authenticate(self, password, |password_ptr, temp_password_ptr| unsafe { nitrokey_sys::NK_first_authenticate(password_ptr, temp_password_ptr) }) diff --git a/src/device.rs b/src/device.rs index 50ff071..e1a71fa 100644 --- a/src/device.rs +++ b/src/device.rs @@ -156,7 +156,7 @@ pub enum DeviceWrapper<'a> { /// [`Pro::connect`]: #method.connect #[derive(Debug)] pub struct Pro<'a> { - manager: &'a mut crate::Manager, + manager: Option<&'a mut crate::Manager>, } /// A Nitrokey Storage device without user or admin authentication. @@ -200,7 +200,7 @@ pub struct Pro<'a> { /// [`Storage::connect`]: #method.connect #[derive(Debug)] pub struct Storage<'a> { - manager: &'a mut crate::Manager, + manager: Option<&'a mut crate::Manager>, } /// The status of a volume on a Nitrokey Storage device. @@ -291,7 +291,32 @@ pub struct StorageStatus { /// /// This trait provides the commands that can be executed without authentication and that are /// present on all supported Nitrokey devices. -pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { +pub trait Device<'a>: Authenticate<'a> + GetPasswordSafe<'a> + GenerateOtp + fmt::Debug { + /// Returns the [`Manager`][] instance that has been used to connect to this device. + /// + /// # Example + /// + /// ``` + /// use nitrokey::{Device, DeviceWrapper}; + /// + /// fn do_something(device: DeviceWrapper) { + /// // reconnect to any device + /// let manager = device.into_manager(); + /// let device = manager.connect(); + /// // do something with the device + /// // ... + /// } + /// + /// # fn main() -> Result<(), nitrokey::Error> { + /// match nitrokey::take()?.connect() { + /// Ok(device) => do_something(device), + /// Err(err) => println!("Could not connect to a Nitrokey: {}", err), + /// } + /// # Ok(()) + /// # } + /// ``` + fn into_manager(self) -> &'a mut crate::Manager; + /// Returns the model of the connected Nitrokey device. /// /// # Example @@ -657,14 +682,14 @@ pub(crate) fn connect_enum(model: Model) -> bool { } impl<'a> DeviceWrapper<'a> { - fn device(&self) -> &dyn Device { + fn device(&self) -> &dyn Device<'a> { match *self { DeviceWrapper::Storage(ref storage) => storage, DeviceWrapper::Pro(ref pro) => pro, } } - fn device_mut(&mut self) -> &mut dyn Device { + fn device_mut(&mut self) -> &mut dyn Device<'a> { match *self { DeviceWrapper::Storage(ref mut storage) => storage, DeviceWrapper::Pro(ref mut pro) => pro, @@ -702,7 +727,14 @@ impl<'a> GenerateOtp for DeviceWrapper<'a> { } } -impl<'a> Device for DeviceWrapper<'a> { +impl<'a> Device<'a> for DeviceWrapper<'a> { + fn into_manager(self) -> &'a mut crate::Manager { + match self { + DeviceWrapper::Pro(dev) => dev.into_manager(), + DeviceWrapper::Storage(dev) => dev.into_manager(), + } + } + fn get_model(&self) -> Model { match *self { DeviceWrapper::Pro(_) => Model::Pro, @@ -713,7 +745,9 @@ impl<'a> Device for DeviceWrapper<'a> { impl<'a> Pro<'a> { pub(crate) fn new(manager: &'a mut crate::Manager) -> Pro<'a> { - Pro { manager } + Pro { + manager: Some(manager), + } } } @@ -725,7 +759,11 @@ impl<'a> Drop for Pro<'a> { } } -impl<'a> Device for Pro<'a> { +impl<'a> Device<'a> for Pro<'a> { + fn into_manager(mut self) -> &'a mut crate::Manager { + self.manager.take().unwrap() + } + fn get_model(&self) -> Model { Model::Pro } @@ -735,7 +773,9 @@ impl<'a> GenerateOtp for Pro<'a> {} impl<'a> Storage<'a> { pub(crate) fn new(manager: &'a mut crate::Manager) -> Storage<'a> { - Storage { manager } + Storage { + manager: Some(manager), + } } /// Changes the update PIN. @@ -1248,7 +1288,11 @@ impl<'a> Drop for Storage<'a> { } } -impl<'a> Device for Storage<'a> { +impl<'a> Device<'a> for Storage<'a> { + fn into_manager(mut self) -> &'a mut crate::Manager { + self.manager.take().unwrap() + } + fn get_model(&self) -> Model { Model::Storage } diff --git a/src/error.rs b/src/error.rs index b84f5eb..9e6adc0 100644 --- a/src/error.rs +++ b/src/error.rs @@ -85,7 +85,7 @@ impl From>> for Err } } -impl From<(T, Error)> for Error { +impl<'a, T: device::Device<'a>> From<(T, Error)> for Error { fn from((_, err): (T, Error)) -> Self { err } diff --git a/src/pws.rs b/src/pws.rs index cf2dd42..778765d 100644 --- a/src/pws.rs +++ b/src/pws.rs @@ -57,8 +57,8 @@ pub const SLOT_COUNT: u8 = 16; /// [`lock`]: trait.Device.html#method.lock /// [`GetPasswordSafe`]: trait.GetPasswordSafe.html #[derive(Debug)] -pub struct PasswordSafe<'a> { - _device: &'a dyn Device, +pub struct PasswordSafe<'a, 'b> { + _device: &'a dyn Device<'b>, } /// Provides access to a [`PasswordSafe`][]. @@ -67,7 +67,7 @@ pub struct PasswordSafe<'a> { /// retrieved from it. /// /// [`PasswordSafe`]: struct.PasswordSafe.html -pub trait GetPasswordSafe { +pub trait GetPasswordSafe<'a> { /// Enables and returns the password safe. /// /// The underlying device must always live at least as long as a password safe retrieved from @@ -117,13 +117,13 @@ 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(&mut self, user_pin: &str) -> Result, Error>; + fn get_password_safe(&mut self, user_pin: &str) -> Result, Error>; } -fn get_password_safe<'a>( - device: &'a dyn Device, +fn get_password_safe<'a, 'b>( + device: &'a dyn Device<'b>, user_pin: &str, -) -> Result, Error> { +) -> Result, Error> { let user_pin_string = get_cstring(user_pin)?; get_command_result(unsafe { nitrokey_sys::NK_enable_password_safe(user_pin_string.as_ptr()) }) .map(|_| PasswordSafe { _device: device }) @@ -137,7 +137,7 @@ fn get_pws_result(s: String) -> Result { } } -impl<'a> PasswordSafe<'a> { +impl<'a, 'b> PasswordSafe<'a, 'b> { /// Returns the status of all password slots. /// /// The status indicates whether a slot is programmed or not. @@ -357,27 +357,27 @@ impl<'a> PasswordSafe<'a> { } } -impl<'a> Drop for PasswordSafe<'a> { +impl<'a, 'b> Drop for PasswordSafe<'a, 'b> { fn drop(&mut self) { // TODO: disable the password safe -- NK_lock_device has side effects on the Nitrokey // Storage, see https://github.com/Nitrokey/nitrokey-storage-firmware/issues/65 } } -impl<'a> GetPasswordSafe for Pro<'a> { - fn get_password_safe(&mut self, user_pin: &str) -> Result, Error> { +impl<'a> GetPasswordSafe<'a> for Pro<'a> { + fn get_password_safe(&mut self, user_pin: &str) -> Result, Error> { get_password_safe(self, user_pin) } } -impl<'a> GetPasswordSafe for Storage<'a> { - fn get_password_safe(&mut self, user_pin: &str) -> Result, Error> { +impl<'a> GetPasswordSafe<'a> for Storage<'a> { + fn get_password_safe(&mut self, user_pin: &str) -> Result, Error> { get_password_safe(self, user_pin) } } -impl<'a> GetPasswordSafe for DeviceWrapper<'a> { - fn get_password_safe(&mut self, user_pin: &str) -> Result, Error> { +impl<'a> GetPasswordSafe<'a> for DeviceWrapper<'a> { + fn get_password_safe(&mut self, user_pin: &str) -> Result, Error> { get_password_safe(self, user_pin) } } diff --git a/tests/device.rs b/tests/device.rs index b377f2e..76f38e6 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -101,7 +101,10 @@ fn get_firmware_version(device: Pro) { assert!(version.minor > 0); } -fn admin_retry(device: T, suffix: &str, count: u8) -> T { +fn admin_retry<'a, T>(device: T, suffix: &str, count: u8) -> T +where + T: Authenticate<'a> + Device<'a> + 'a, +{ let result = device.authenticate_admin(&(DEFAULT_ADMIN_PIN.to_owned() + suffix)); let device = match result { Ok(admin) => admin.device(), @@ -111,7 +114,10 @@ fn admin_retry(device: T, suffix: &str, count: u8) -> return device; } -fn user_retry(device: T, suffix: &str, count: u8) -> T { +fn user_retry<'a, T>(device: T, suffix: &str, count: u8) -> T +where + T: Authenticate<'a> + Device<'a> + 'a, +{ let result = device.authenticate_user(&(DEFAULT_USER_PIN.to_owned() + suffix)); let device = match result { Ok(admin) => admin.device(), @@ -220,10 +226,10 @@ fn change_admin_pin(device: DeviceWrapper) { device.authenticate_admin(ADMIN_NEW_PASSWORD).unwrap_err(); } -fn require_failed_user_login(device: D, password: &str, error: CommandError) -> D +fn require_failed_user_login<'a, D>(device: D, password: &str, error: CommandError) -> D where - D: Device + Authenticate, - nitrokey::User: std::fmt::Debug, + D: Device<'a> + Authenticate<'a> + 'a, + nitrokey::User<'a, D>: std::fmt::Debug, { let result = device.authenticate_user(password); assert!(result.is_err()); diff --git a/tests/otp.rs b/tests/otp.rs index c0bbecf..aafda59 100644 --- a/tests/otp.rs +++ b/tests/otp.rs @@ -36,9 +36,9 @@ enum TotpTimestampSize { U64, } -fn make_admin_test_device(device: T) -> Admin +fn make_admin_test_device<'a, T>(device: T) -> Admin<'a, T> where - T: Device, + T: Device<'a>, (T, nitrokey::Error): Debug, { unwrap_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN)) diff --git a/tests/pws.rs b/tests/pws.rs index b0e5abe..7169695 100644 --- a/tests/pws.rs +++ b/tests/pws.rs @@ -32,9 +32,9 @@ fn get_slot_name_direct(slot: u8) -> Result { } } -fn get_pws(device: &mut T) -> PasswordSafe +fn get_pws<'a, T>(device: &mut T) -> PasswordSafe<'_, 'a> where - T: Device, + T: Device<'a>, { unwrap_ok!(device.get_password_safe(DEFAULT_USER_PIN)) } -- cgit v1.2.3 From 0ac9c401c9968d39581fe2bc3a6610cb9a7a22d8 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 27 Jan 2019 19:14:51 +0000 Subject: Use Manager in device connection tests The previous patches refactored the connection handling to use the Manager struct. This patch changes the tests to use the new Manager methods instead of the deprecated functions. --- tests/device.rs | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) (limited to 'tests') diff --git a/tests/device.rs b/tests/device.rs index 76f38e6..bb71eb4 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -33,24 +33,19 @@ fn count_nitrokey_block_devices() -> usize { #[test_device] fn connect_no_device() { - assert_cmu_err!(CommunicationError::NotConnected, nitrokey::connect()); - assert_cmu_err!( - CommunicationError::NotConnected, - nitrokey::take() - .unwrap() - .connect_model(nitrokey::Model::Pro) - ); + let mut manager = nitrokey::take().unwrap(); + + assert_cmu_err!(CommunicationError::NotConnected, manager.connect()); assert_cmu_err!( CommunicationError::NotConnected, - nitrokey::take() - .unwrap() - .connect_model(nitrokey::Model::Storage) + manager.connect_model(nitrokey::Model::Pro) ); - assert_cmu_err!(CommunicationError::NotConnected, nitrokey::Pro::connect()); assert_cmu_err!( CommunicationError::NotConnected, - nitrokey::Storage::connect() + manager.connect_model(nitrokey::Model::Storage) ); + assert_cmu_err!(CommunicationError::NotConnected, manager.connect_pro()); + assert_cmu_err!(CommunicationError::NotConnected, manager.connect_storage()); } #[test_device] @@ -58,9 +53,10 @@ fn connect_pro(device: Pro) { assert_eq!(device.get_model(), nitrokey::Model::Pro); drop(device); - assert_any_ok!(nitrokey::connect()); - assert_any_ok!(nitrokey::take().unwrap().connect_model(nitrokey::Model::Pro)); - assert_any_ok!(nitrokey::take().unwrap().connect_pro()); + let mut manager = nitrokey::take().unwrap(); + assert_any_ok!(manager.connect()); + assert_any_ok!(manager.connect_model(nitrokey::Model::Pro)); + assert_any_ok!(manager.connect_pro()); } #[test_device] @@ -68,9 +64,10 @@ fn connect_storage(device: Storage) { assert_eq!(device.get_model(), nitrokey::Model::Storage); drop(device); - assert_any_ok!(nitrokey::connect()); - assert_any_ok!(nitrokey::take().unwrap().connect_model(nitrokey::Model::Storage)); - assert_any_ok!(nitrokey::take().unwrap().connect_storage()); + let mut manager = nitrokey::take().unwrap(); + assert_any_ok!(manager.connect()); + assert_any_ok!(manager.connect_model(nitrokey::Model::Storage)); + assert_any_ok!(manager.connect_storage()); } fn assert_empty_serial_number() { -- cgit v1.2.3 From a0ebd37765027121b85dfd6b78b453a50adc69c9 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Tue, 9 Jul 2019 11:03:01 +0000 Subject: Use into_manager in device connection tests To avoid a ConcurrentAccessError, we have to use the Device::into_manager function instead of calling take to obtain a Manager instance. --- tests/device.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/tests/device.rs b/tests/device.rs index bb71eb4..a2bdfb5 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -33,7 +33,7 @@ fn count_nitrokey_block_devices() -> usize { #[test_device] fn connect_no_device() { - let mut manager = nitrokey::take().unwrap(); + let mut manager = unwrap_ok!(nitrokey::take()); assert_cmu_err!(CommunicationError::NotConnected, manager.connect()); assert_cmu_err!( @@ -51,9 +51,8 @@ fn connect_no_device() { #[test_device] fn connect_pro(device: Pro) { assert_eq!(device.get_model(), nitrokey::Model::Pro); - drop(device); - let mut manager = nitrokey::take().unwrap(); + let manager = device.into_manager(); assert_any_ok!(manager.connect()); assert_any_ok!(manager.connect_model(nitrokey::Model::Pro)); assert_any_ok!(manager.connect_pro()); @@ -62,9 +61,8 @@ fn connect_pro(device: Pro) { #[test_device] fn connect_storage(device: Storage) { assert_eq!(device.get_model(), nitrokey::Model::Storage); - drop(device); - let mut manager = nitrokey::take().unwrap(); + let manager = device.into_manager(); assert_any_ok!(manager.connect()); assert_any_ok!(manager.connect_model(nitrokey::Model::Storage)); assert_any_ok!(manager.connect_storage()); -- cgit v1.2.3 From fe8161ba51d65382a20650a75f06e1cc7b753e68 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Tue, 9 Jul 2019 11:34:53 +0000 Subject: Check retry count before building AES key in test Due to a timing issue, some calls to the build_aes_key function may fail after a factory reset. As a workaround for this firmware bug, we check the user retry count before building the aes key in the factory_reset test. For details, see the upstream issue: https://github.com/Nitrokey/nitrokey-pro-firmware/issues/57 --- tests/device.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'tests') diff --git a/tests/device.rs b/tests/device.rs index a2bdfb5..e367558 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -344,6 +344,7 @@ fn factory_reset(device: DeviceWrapper) { assert_utf8_err_or_ne("testpw", pws.get_slot_password(0)); drop(pws); + assert_ok!(3, device.get_user_retry_count()); assert_ok!((), device.build_aes_key(DEFAULT_ADMIN_PIN)); } -- cgit v1.2.3 From 0e07d9cdd6b333ebd16160d673f8735940af3a43 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 29 Dec 2019 23:00:09 +0100 Subject: Use dyn keyword for trait arguments in tests/otp.rs To fix a compiler warning, we use the dyn keyword for trait arguments in the otp.rs instead of using the trait directly. --- tests/otp.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/tests/otp.rs b/tests/otp.rs index aafda59..38cd8a9 100644 --- a/tests/otp.rs +++ b/tests/otp.rs @@ -44,12 +44,12 @@ where unwrap_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN)) } -fn configure_hotp(admin: &mut ConfigureOtp, counter: u8) { +fn configure_hotp(admin: &mut dyn 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: &mut GenerateOtp, offset: u8) { +fn check_hotp_codes(device: &mut dyn GenerateOtp, offset: u8) { HOTP_CODES.iter().enumerate().for_each(|(i, code)| { if i >= offset as usize { assert_ok!(code.to_string(), device.get_hotp_code(1)); @@ -146,13 +146,13 @@ fn hotp_erase(device: DeviceWrapper) { assert_ok!("test2".to_string(), device.get_hotp_slot_name(2)); } -fn configure_totp(admin: &mut ConfigureOtp, factor: u64) { +fn configure_totp(admin: &mut dyn 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: &mut GenerateOtp, factor: u64, timestamp_size: TotpTimestampSize) { +fn check_totp_codes(device: &mut dyn 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; -- cgit v1.2.3