diff options
Diffstat (limited to 'nitrokey')
-rw-r--r-- | nitrokey/.builds/archlinux-use-system-lib.yaml | 22 | ||||
-rw-r--r-- | nitrokey/.builds/archlinux.yml | 21 | ||||
-rw-r--r-- | nitrokey/CHANGELOG.md | 9 | ||||
-rw-r--r-- | nitrokey/Cargo.toml | 5 | ||||
-rw-r--r-- | nitrokey/TODO.md | 3 | ||||
-rw-r--r-- | nitrokey/src/auth.rs | 5 | ||||
-rw-r--r-- | nitrokey/src/device.rs | 130 | ||||
-rw-r--r-- | nitrokey/src/lib.rs | 9 | ||||
-rw-r--r-- | nitrokey/src/pws.rs | 29 | ||||
-rw-r--r-- | nitrokey/src/util.rs | 26 | ||||
-rw-r--r-- | nitrokey/tests/device.rs | 57 | ||||
-rw-r--r-- | nitrokey/tests/lib.rs | 3 | ||||
-rw-r--r-- | nitrokey/tests/pws.rs | 23 |
13 files changed, 299 insertions, 43 deletions
diff --git a/nitrokey/.builds/archlinux-use-system-lib.yaml b/nitrokey/.builds/archlinux-use-system-lib.yaml new file mode 100644 index 0000000..6fba33a --- /dev/null +++ b/nitrokey/.builds/archlinux-use-system-lib.yaml @@ -0,0 +1,22 @@ +image: archlinux +packages: + - rust + - libnitrokey +environment: + USE_SYSTEM_LIBNITROKEY: "1" +sources: + - https://git.sr.ht/~ireas/nitrokey-rs +tasks: + - build: | + cd nitrokey-rs + cargo build --release + - test: | + cd nitrokey-rs + cargo test + - format: | + cd nitrokey-rs + cargo fmt -- --check +triggers: + - action: email + condition: failure + to: nitrokey-rs-dev <nitrokey-rs-dev@ireas.org> diff --git a/nitrokey/.builds/archlinux.yml b/nitrokey/.builds/archlinux.yml new file mode 100644 index 0000000..9d45386 --- /dev/null +++ b/nitrokey/.builds/archlinux.yml @@ -0,0 +1,21 @@ +image: archlinux +packages: + - rust + - hidapi + - gcc +sources: + - https://git.sr.ht/~ireas/nitrokey-rs +tasks: + - build: | + cd nitrokey-rs + cargo build --release + - test: | + cd nitrokey-rs + cargo test + - format: | + cd nitrokey-rs + cargo fmt -- --check +triggers: + - action: email + condition: failure + to: nitrokey-rs-dev <nitrokey-rs-dev@ireas.org> diff --git a/nitrokey/CHANGELOG.md b/nitrokey/CHANGELOG.md index 72e6986..3845aaf 100644 --- a/nitrokey/CHANGELOG.md +++ b/nitrokey/CHANGELOG.md @@ -1,3 +1,12 @@ +# v0.3.3 (2019-01-16) +- Add the `get_production_info` and `clear_new_sd_card_warning` methods to the + `Storage` struct. +- Use `rand_os` instead of `rand` for random data creation. + - (Re-)add `CommandError::RngError` variant. +- Account for the possibility that an empty string returned by libnitrokey can + not only indicate an error but also be a valid return value. +- Make test cases more robust and avoid side effects on other test cases. + # v0.3.2 (2019-01-12) - Make three additional error codes known: `CommandError::StringTooLong`, `CommandError::InvalidHexString` and `CommandError::TargetBufferTooSmall`. diff --git a/nitrokey/Cargo.toml b/nitrokey/Cargo.toml index 09811f0..802d022 100644 --- a/nitrokey/Cargo.toml +++ b/nitrokey/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nitrokey" -version = "0.3.2" +version = "0.3.3" authors = ["Robin Krahl <robin.krahl@ireas.org>"] edition = "2018" homepage = "https://code.ireas.org/nitrokey-rs/" @@ -19,7 +19,8 @@ test-storage = [] [dependencies] libc = "0.2" nitrokey-sys = "3.4" -rand = "0.6" +rand_core = {version = "0.3", default-features = false} +rand_os = {version = "0.1"} [dev-dependencies] nitrokey-test = {version = "0.1"} diff --git a/nitrokey/TODO.md b/nitrokey/TODO.md index 7c8c5e6..28bd3b8 100644 --- a/nitrokey/TODO.md +++ b/nitrokey/TODO.md @@ -1,19 +1,16 @@ - Add support for the currently unsupported commands: - `NK_is_AES_supported` - `NK_send_startup` - - `NK_clear_new_sd_card_warning` - `NK_fill_SD_card_with_random_data` - `NK_get_SD_usage_data_as_string` - `NK_get_progress_bar_value` - `NK_list_devices_by_cpuID` - `NK_connect_with_ID` - - `NK_get_storage_production_info` - 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. - Prevent construction of internal types. - More specific error checking in the tests. -- Differentiate empty strings and errors (see `result_from_string`). - Check integer conversions. - Consider implementing `Into<CommandError>` for `(Device, CommandError)` - Lock password safe in `PasswordSafe::drop()` (see [nitrokey-storage-firmware diff --git a/nitrokey/src/auth.rs b/nitrokey/src/auth.rs index a129bd8..3280924 100644 --- a/nitrokey/src/auth.rs +++ b/nitrokey/src/auth.rs @@ -149,7 +149,10 @@ where A: AuthenticatedDevice<D>, T: Fn(*const i8, *const i8) -> c_int, { - let temp_password = generate_password(TEMPORARY_PASSWORD_LENGTH); + 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)), diff --git a/nitrokey/src/device.rs b/nitrokey/src/device.rs index f247f58..9813c50 100644 --- a/nitrokey/src/device.rs +++ b/nitrokey/src/device.rs @@ -208,6 +208,38 @@ pub struct VolumeStatus { pub active: bool, } +/// Information about the SD card in a Storage device. +#[derive(Debug)] +pub struct SdCardData { + /// The serial number of the SD card. + pub serial_number: u32, + /// The size of the SD card in GB. + pub size: u8, + /// The year the card was manufactured, e. g. 17 for 2017. + pub manufacturing_year: u8, + /// The month the card was manufactured. + pub manufacturing_month: u8, + /// The OEM ID. + pub oem: u16, + /// The manufacturer ID. + pub manufacturer: u8, +} + +#[derive(Debug)] +/// Production information for a Storage device. +pub struct StorageProductionInfo { + /// The major firmware version, e. g. 0 in v0.40. + pub firmware_version_major: u8, + /// The minor firmware version, e. g. 40 in v0.40. + pub firmware_version_minor: u8, + /// The internal firmware version. + pub firmware_version_internal: u8, + /// The serial number of the CPU. + pub serial_number_cpu: u32, + /// Information about the SD card. + pub sd_card: SdCardData, +} + /// The status of a Nitrokey Storage device. #[derive(Debug)] pub struct StorageStatus { @@ -566,7 +598,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// The AES key is used to encrypt the password safe and the encrypted volume. You may need /// to call this method after a factory reset, either using [`factory_reset`][] or using `gpg - /// --card-edit`. You can also use it to destory the data stored in the password safe or on + /// --card-edit`. You can also use it to destroy the data stored in the password safe or on /// the encrypted volume. /// /// # Errors @@ -1166,6 +1198,83 @@ impl Storage { result.and(Ok(StorageStatus::from(raw_status))) } + /// Returns the production information for the connected storage device. + /// + /// # Example + /// + /// ```no_run + /// # use nitrokey::CommandError; + /// + /// fn use_volume() {} + /// + /// # fn try_main() -> Result<(), CommandError> { + /// let device = nitrokey::Storage::connect()?; + /// match device.get_production_info() { + /// Ok(data) => { + /// println!("SD card ID: {:#x}", data.sd_card.serial_number); + /// println!("SD card size: {} GB", data.sd_card.size); + /// }, + /// Err(err) => println!("Could not get Storage production info: {}", err), + /// }; + /// # Ok(()) + /// # } + /// ``` + pub fn get_production_info(&self) -> Result<StorageProductionInfo, CommandError> { + let mut raw_data = nitrokey_sys::NK_storage_ProductionTest { + FirmwareVersion_au8: [0, 2], + FirmwareVersionInternal_u8: 0, + SD_Card_Size_u8: 0, + CPU_CardID_u32: 0, + SmartCardID_u32: 0, + SD_CardID_u32: 0, + SC_UserPwRetryCount: 0, + SC_AdminPwRetryCount: 0, + SD_Card_ManufacturingYear_u8: 0, + SD_Card_ManufacturingMonth_u8: 0, + SD_Card_OEM_u16: 0, + SD_WriteSpeed_u16: 0, + SD_Card_Manufacturer_u8: 0, + }; + let raw_result = unsafe { nitrokey_sys::NK_get_storage_production_info(&mut raw_data) }; + let result = get_command_result(raw_result); + result.and(Ok(StorageProductionInfo::from(raw_data))) + } + + /// Clears the warning for a new SD card. + /// + /// The Storage status contains a field for a new SD card warning. After a factory reset, the + /// field is set to true. After filling the SD card with random data, it is set to false. + /// This method can be used to set it to false without filling the SD card with random data. + /// + /// # Errors + /// + /// - [`InvalidString`][] if the provided password contains a null byte + /// - [`WrongPassword`][] if the provided admin password is wrong + /// + /// # Example + /// + /// ```no_run + /// # use nitrokey::CommandError; + /// + /// # fn try_main() -> Result<(), CommandError> { + /// let device = nitrokey::Storage::connect()?; + /// match device.clear_new_sd_card_warning("12345678") { + /// Ok(()) => println!("Cleared the new SD card warning."), + /// Err(err) => println!("Could not set the clear the new SD card warning: {}", err), + /// }; + /// # Ok(()) + /// # } + /// ``` + /// + /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword + pub fn clear_new_sd_card_warning(&self, admin_pin: &str) -> Result<(), CommandError> { + let admin_pin = get_cstring(admin_pin)?; + get_command_result(unsafe { + nitrokey_sys::NK_clear_new_sd_card_warning(admin_pin.as_ptr()) + }) + } + /// Blinks the red and green LED alternatively and infinitely until the device is reconnected. pub fn wink(&self) -> Result<(), CommandError> { get_command_result(unsafe { nitrokey_sys::NK_wink() }) @@ -1209,6 +1318,25 @@ impl Device for Storage { impl GenerateOtp for Storage {} +impl From<nitrokey_sys::NK_storage_ProductionTest> for StorageProductionInfo { + fn from(data: nitrokey_sys::NK_storage_ProductionTest) -> Self { + Self { + firmware_version_major: data.FirmwareVersion_au8[0], + firmware_version_minor: data.FirmwareVersion_au8[1], + firmware_version_internal: data.FirmwareVersionInternal_u8, + serial_number_cpu: data.CPU_CardID_u32, + sd_card: SdCardData { + serial_number: data.SD_CardID_u32, + size: data.SD_Card_Size_u8, + manufacturing_year: data.SD_Card_ManufacturingYear_u8, + manufacturing_month: data.SD_Card_ManufacturingMonth_u8, + oem: data.SD_Card_OEM_u16, + manufacturer: data.SD_Card_Manufacturer_u8, + }, + } + } +} + impl From<nitrokey_sys::NK_storage_status> for StorageStatus { fn from(status: nitrokey_sys::NK_storage_status) -> Self { StorageStatus { diff --git a/nitrokey/src/lib.rs b/nitrokey/src/lib.rs index c50b713..02a622b 100644 --- a/nitrokey/src/lib.rs +++ b/nitrokey/src/lib.rs @@ -98,8 +98,8 @@ use nitrokey_sys; pub use crate::auth::{Admin, Authenticate, User}; pub use crate::config::Config; pub use crate::device::{ - connect, connect_model, Device, DeviceWrapper, Model, Pro, Storage, StorageStatus, VolumeMode, - VolumeStatus, + connect, connect_model, Device, DeviceWrapper, Model, Pro, SdCardData, Storage, + StorageProductionInfo, StorageStatus, VolumeMode, VolumeStatus, }; pub use crate::otp::{ConfigureOtp, GenerateOtp, OtpMode, OtpSlotData}; pub use crate::pws::{GetPasswordSafe, PasswordSafe, SLOT_COUNT}; @@ -111,12 +111,13 @@ pub use crate::util::{CommandError, LogLevel}; /// version. #[derive(Clone, Debug, PartialEq)] pub struct Version { - /// The library version as a string. + /// The Git library version as a string. /// /// The library version is the output of `git describe --always` at compile time, for example /// `v3.3` or `v3.4.1`. If the library has not been built from a release, the version string /// contains the number of commits since the last release and the hash of the current commit, for - /// example `v3.3-19-gaee920b`. + /// example `v3.3-19-gaee920b`. If the library has not been built from a Git checkout, this + /// string may be empty. pub git: String, /// The major library version. pub major: u32, diff --git a/nitrokey/src/pws.rs b/nitrokey/src/pws.rs index ebd5fcd..28f0681 100644 --- a/nitrokey/src/pws.rs +++ b/nitrokey/src/pws.rs @@ -129,6 +129,14 @@ fn get_password_safe<'a>( result.map(|()| PasswordSafe { _device: device }) } +fn get_pws_result(s: String) -> Result<String, CommandError> { + if s.is_empty() { + Err(CommandError::SlotNotProgrammed) + } else { + Ok(s) + } +} + impl<'a> PasswordSafe<'a> { /// Returns the status of all password slots. /// @@ -172,10 +180,12 @@ impl<'a> PasswordSafe<'a> { /// Returns the name of the given slot (if it is programmed). /// + /// This method also returns a `SlotNotProgrammed` error if the name is empty. + /// /// # Errors /// /// - [`InvalidSlot`][] if the given slot is out of range - /// - [`Undefined`][] if the slot is not programmed + /// - [`SlotNotProgrammed`][] if the slot is not programmed /// /// # Example /// @@ -199,17 +209,20 @@ impl<'a> PasswordSafe<'a> { /// ``` /// /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot - /// [`Undefined`]: enum.CommandError.html#variant.Undefined + /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed pub fn get_slot_name(&self, slot: u8) -> Result<String, CommandError> { unsafe { result_from_string(nitrokey_sys::NK_get_password_safe_slot_name(slot)) } + .and_then(get_pws_result) } /// Returns the login for the given slot (if it is programmed). /// + /// This method also returns a `SlotNotProgrammed` error if the login is empty. + /// /// # Errors /// /// - [`InvalidSlot`][] if the given slot is out of range - /// - [`Undefined`][] if the slot is not programmed + /// - [`SlotNotProgrammed`][] if the slot is not programmed /// /// # Example /// @@ -229,17 +242,20 @@ impl<'a> PasswordSafe<'a> { /// ``` /// /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot - /// [`Undefined`]: enum.CommandError.html#variant.Undefined + /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed pub fn get_slot_login(&self, slot: u8) -> Result<String, CommandError> { unsafe { result_from_string(nitrokey_sys::NK_get_password_safe_slot_login(slot)) } + .and_then(get_pws_result) } /// Returns the password for the given slot (if it is programmed). /// + /// This method also returns a `SlotNotProgrammed` error if the password is empty. + /// /// # Errors /// /// - [`InvalidSlot`][] if the given slot is out of range - /// - [`Undefined`][] if the slot is not programmed + /// - [`SlotNotProgrammed`][] if the slot is not programmed /// /// # Example /// @@ -259,9 +275,10 @@ impl<'a> PasswordSafe<'a> { /// ``` /// /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot - /// [`Undefined`]: enum.CommandError.html#variant.Undefined + /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed pub fn get_slot_password(&self, slot: u8) -> Result<String, CommandError> { unsafe { result_from_string(nitrokey_sys::NK_get_password_safe_slot_password(slot)) } + .and_then(get_pws_result) } /// Writes the given slot with the given name, login and password. diff --git a/nitrokey/src/util.rs b/nitrokey/src/util.rs index cb109d0..567c478 100644 --- a/nitrokey/src/util.rs +++ b/nitrokey/src/util.rs @@ -4,7 +4,8 @@ use std::fmt; use std::os::raw::{c_char, c_int}; use libc::{c_void, free}; -use rand::Rng; +use rand_core::RngCore; +use rand_os::OsRng; /// Error types returned by Nitrokey device or by the library. #[derive(Clone, Copy, Debug, PartialEq)] @@ -44,6 +45,8 @@ pub enum CommandError { InvalidHexString, /// The target buffer was smaller than the source. TargetBufferTooSmall, + /// An error occurred during random number generation. + RngError, } /// Log level for libnitrokey. @@ -80,10 +83,13 @@ pub fn result_from_string(ptr: *const c_char) -> Result<String, CommandError> { unsafe { let s = owned_str_from_ptr(ptr); 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. if s.is_empty() { - return Err(get_last_error()); + get_last_result().map(|_| s) + } else { + Ok(s) } - return Ok(s); } } @@ -106,10 +112,11 @@ pub fn get_last_error() -> CommandError { }; } -pub fn generate_password(length: usize) -> Vec<u8> { +pub fn generate_password(length: usize) -> Result<Vec<u8>, CommandError> { + let mut rng = OsRng::new()?; let mut data = vec![0u8; length]; - rand::thread_rng().fill(&mut data[..]); - return data; + rng.fill_bytes(&mut data[..]); + Ok(data) } pub fn get_cstring<T: Into<Vec<u8>>>(s: T) -> Result<CString, CommandError> { @@ -146,6 +153,7 @@ impl CommandError { "The supplied string is not in hexadecimal format".into() } CommandError::TargetBufferTooSmall => "The target buffer is too small".into(), + CommandError::RngError => "An error occurred during random number generation".into(), } } } @@ -178,6 +186,12 @@ impl From<c_int> for CommandError { } } +impl From<rand_core::Error> for CommandError { + fn from(_error: rand_core::Error) -> Self { + CommandError::RngError + } +} + impl Into<i32> for LogLevel { fn into(self) -> i32 { match self { diff --git a/nitrokey/tests/device.rs b/nitrokey/tests/device.rs index e40ae12..849d2ff 100644 --- a/nitrokey/tests/device.rs +++ b/nitrokey/tests/device.rs @@ -260,6 +260,15 @@ fn unlock_user_pin(device: DeviceWrapper) { #[test_device] 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)); + + let device = admin.device(); + let pws = device.get_password_safe(USER_PASSWORD).unwrap(); + assert_eq!(Ok(()), pws.write_slot(0, "test", "testlogin", "testpw")); + drop(pws); + assert_eq!( Ok(()), device.change_user_pin(USER_PASSWORD, USER_NEW_PASSWORD) @@ -269,15 +278,6 @@ fn factory_reset(device: DeviceWrapper) { device.change_admin_pin(ADMIN_PASSWORD, ADMIN_NEW_PASSWORD) ); - let admin = device.authenticate_admin(ADMIN_NEW_PASSWORD).unwrap(); - let otp_data = OtpSlotData::new(1, "test", "0123468790", OtpMode::SixDigits); - assert_eq!(Ok(()), admin.write_totp_slot(otp_data, 30)); - - let device = admin.device(); - let pws = device.get_password_safe(USER_NEW_PASSWORD).unwrap(); - assert_eq!(Ok(()), pws.write_slot(0, "test", "testlogin", "testpw")); - drop(pws); - assert_eq!( Err(CommandError::WrongPassword), device.factory_reset(USER_NEW_PASSWORD) @@ -439,6 +439,45 @@ 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!(info.serial_number_cpu != 0); + assert!(info.sd_card.serial_number != 0); + assert!(info.sd_card.size > 0); + assert!(info.sd_card.manufacturing_year > 10); + assert!(info.sd_card.manufacturing_year < 100); + // TODO: month value is not valid atm + // assert!(info.sd_card.manufacturing_month < 12); + assert!(info.sd_card.oem != 0); + 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.serial_number_sd_card, info.sd_card.serial_number); +} + +#[test_device] +fn clear_new_sd_card_warning(device: Storage) { + assert_eq!(Ok(()), device.factory_reset(ADMIN_PASSWORD)); + thread::sleep(time::Duration::from_secs(3)); + assert_eq!(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()); + + let status = device.get_status().unwrap(); + assert!(status.new_sd_card_found); + + assert_eq!(Ok(()), device.clear_new_sd_card_warning(ADMIN_PASSWORD)); + + let status = device.get_status().unwrap(); + assert!(!status.new_sd_card_found); +} + +#[test_device] fn export_firmware(device: Storage) { assert_eq!( Err(CommandError::WrongPassword), diff --git a/nitrokey/tests/lib.rs b/nitrokey/tests/lib.rs index 06de0ad..c92e224 100644 --- a/nitrokey/tests/lib.rs +++ b/nitrokey/tests/lib.rs @@ -2,7 +2,6 @@ fn get_library_version() { let version = nitrokey::get_library_version(); - assert!(!version.git.is_empty()); - assert!(version.git.starts_with("v")); + assert!(version.git.is_empty() || version.git.starts_with("v")); assert!(version.major > 0); } diff --git a/nitrokey/tests/pws.rs b/nitrokey/tests/pws.rs index b349558..fbcc0c1 100644 --- a/nitrokey/tests/pws.rs +++ b/nitrokey/tests/pws.rs @@ -20,7 +20,7 @@ fn get_slot_name_direct(slot: u8) -> Result<String, CommandError> { true => { let error = unsafe { nitrokey_sys::NK_get_last_command_status() } as c_int; match error { - 0 => Err(CommandError::Undefined), + 0 => Ok(s), other => Err(CommandError::from(other)), } } @@ -92,10 +92,12 @@ fn get_data(device: DeviceWrapper) { assert_eq!("password", pws.get_slot_password(1).unwrap()); assert_eq!(Ok(()), pws.erase_slot(1)); - // TODO: check error codes - assert_eq!(Err(CommandError::Undefined), pws.get_slot_name(1)); - assert_eq!(Err(CommandError::Undefined), pws.get_slot_login(1)); - assert_eq!(Err(CommandError::Undefined), pws.get_slot_password(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) + ); let name = "with å"; let login = "pär@test.com"; @@ -129,19 +131,22 @@ fn write(device: DeviceWrapper) { ); assert_eq!(Ok(()), pws.write_slot(0, "", "login", "password")); - assert_eq!(Err(CommandError::Undefined), pws.get_slot_name(0)); + 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::Undefined), pws.get_slot_login(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::Undefined), pws.get_slot_password(0)); + assert_eq!( + Err(CommandError::SlotNotProgrammed), + pws.get_slot_password(0) + ); } #[test_device] @@ -152,5 +157,5 @@ fn erase(device: DeviceWrapper) { 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::Undefined), pws.get_slot_name(0)); + assert_eq!(Err(CommandError::SlotNotProgrammed), pws.get_slot_name(0)); } |