summaryrefslogtreecommitdiff
path: root/nitrokey
diff options
context:
space:
mode:
Diffstat (limited to 'nitrokey')
-rw-r--r--nitrokey/.builds/archlinux-use-system-lib.yaml5
-rw-r--r--nitrokey/.builds/archlinux.yml5
-rw-r--r--nitrokey/.builds/lint.yml27
-rw-r--r--nitrokey/CHANGELOG.md35
-rw-r--r--nitrokey/Cargo.toml15
-rw-r--r--nitrokey/LICENSE3
-rw-r--r--nitrokey/README.md12
-rw-r--r--nitrokey/TODO.md12
-rw-r--r--nitrokey/src/auth.rs137
-rw-r--r--nitrokey/src/config.rs29
-rw-r--r--nitrokey/src/device.rs457
-rw-r--r--nitrokey/src/error.rs245
-rw-r--r--nitrokey/src/lib.rs53
-rw-r--r--nitrokey/src/otp.rs98
-rw-r--r--nitrokey/src/pws.rs107
-rw-r--r--nitrokey/src/util.rs177
-rw-r--r--nitrokey/tests/device.rs239
-rw-r--r--nitrokey/tests/lib.rs5
-rw-r--r--nitrokey/tests/otp.rs139
-rw-r--r--nitrokey/tests/pws.rs116
-rw-r--r--nitrokey/tests/util/mod.rs84
21 files changed, 1161 insertions, 839 deletions
diff --git a/nitrokey/.builds/archlinux-use-system-lib.yaml b/nitrokey/.builds/archlinux-use-system-lib.yaml
index 01705c1..ac0fc0f 100644
--- a/nitrokey/.builds/archlinux-use-system-lib.yaml
+++ b/nitrokey/.builds/archlinux-use-system-lib.yaml
@@ -1,3 +1,5 @@
+# Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org>
+# SPDX-License-Identifier: MIT
image: archlinux
packages:
- rust
@@ -13,6 +15,3 @@ tasks:
- test: |
cd nitrokey-rs
cargo test
- - format: |
- cd nitrokey-rs
- cargo fmt -- --check
diff --git a/nitrokey/.builds/archlinux.yml b/nitrokey/.builds/archlinux.yml
index 543e93c..dfe2639 100644
--- a/nitrokey/.builds/archlinux.yml
+++ b/nitrokey/.builds/archlinux.yml
@@ -1,3 +1,5 @@
+# Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org>
+# SPDX-License-Identifier: MIT
image: archlinux
packages:
- rust
@@ -12,6 +14,3 @@ tasks:
- test: |
cd nitrokey-rs
cargo test
- - format: |
- cd nitrokey-rs
- cargo fmt -- --check
diff --git a/nitrokey/.builds/lint.yml b/nitrokey/.builds/lint.yml
new file mode 100644
index 0000000..86a27cd
--- /dev/null
+++ b/nitrokey/.builds/lint.yml
@@ -0,0 +1,27 @@
+# Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org>
+# SPDX-License-Identifier: MIT
+image: archlinux
+packages:
+ - rustup
+ - python
+ - python-pip
+ - python-pygit2
+sources:
+ - https://git.ireas.org/nitrokey-rs
+tasks:
+ - setup: |
+ pip install --user fsfe-reuse
+ rustup update stable
+ rustup self upgrade-data
+ rustup default stable
+ rustup component add rustfmt
+ rustup component add clippy
+ - format: |
+ cd nitrokey-rs
+ cargo fmt -- --check
+ - reuse: |
+ cd nitrokey-rs
+ ~/.local/bin/reuse lint
+ - clippy: |
+ cd nitrokey-rs
+ cargo clippy -- -D warnings
diff --git a/nitrokey/CHANGELOG.md b/nitrokey/CHANGELOG.md
index 04da117..24c79af 100644
--- a/nitrokey/CHANGELOG.md
+++ b/nitrokey/CHANGELOG.md
@@ -1,5 +1,36 @@
-# v0.3.5 (2019-12-16)
-- Update the nitrokey-sys dependency version specification to ~3.4.
+<!---
+Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org>
+SPDX-License-Identifier: MIT
+-->
+
+# Unreleased
+- Remove the `test-pro` and `test-storage` features.
+- Implement `Display` for `Version`.
+- Introduce `DEFAULT_ADMIN_PIN` and `DEFAULT_USER_PIN` constants.
+- Refactor the error handling code:
+ - Implement `std::error::Error` for `CommandError`.
+ - Add the `Error` enum.
+ - Add the `LibraryError` enum and move the library error variants from
+ `CommandError` to `LibraryError`.
+ - Add the `CommunicationError` enum and move the communication error variants
+ from `CommandError` to `CommunicationError`.
+ - Return `Error` instead of `CommandError` in all public functions.
+ - Move the `CommandError::RngError` variant to `Error::RandError` and the
+ `CommandError::Unknown` variant to `Error::UnknownError`.
+ - Return `CommunicationError::NotConnected` instead of
+ `CommandError::Undefined` from the connect functions.
+ - Remove the `CommandError::Undefined` variant.
+- Add a private `PhantomData` field to `Pro` and `Storage` to make direct
+ instantiation impossible.
+- Refactor and clean up internal code:
+ - Prefer using the `Into` trait over numeric casting.
+ - Add `Pro::new` and `Storage::new` functions.
+- Implement `From<Pro>` and `From<Storage>` for `DeviceWrapper`.
+- Add `Error::Utf8Error` variant.
+ - Return `Result<Version>` 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/nitrokey/Cargo.toml b/nitrokey/Cargo.toml
index 238cc02..084bcea 100644
--- a/nitrokey/Cargo.toml
+++ b/nitrokey/Cargo.toml
@@ -1,6 +1,9 @@
+# Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org>
+# SPDX-License-Identifier: MIT
+
[package]
name = "nitrokey"
-version = "0.3.5"
+version = "0.4.0-alpha.0"
authors = ["Robin Krahl <robin.krahl@ireas.org>"]
edition = "2018"
homepage = "https://code.ireas.org/nitrokey-rs/"
@@ -12,15 +15,11 @@ categories = ["api-bindings"]
readme = "README.md"
license = "MIT"
-[features]
-test-pro = []
-test-storage = []
-
[dependencies]
libc = "0.2"
-nitrokey-sys = "~3.4"
-rand_core = {version = "0.3", default-features = false}
+nitrokey-sys = "3.4"
+rand_core = {version = "0.3", default-features = false, features = ["std"] }
rand_os = {version = "0.1"}
[dev-dependencies]
-nitrokey-test = {version = "0.1"}
+nitrokey-test = {version = "0.2"}
diff --git a/nitrokey/LICENSE b/nitrokey/LICENSE
index 1a3601d..6c67cd5 100644
--- a/nitrokey/LICENSE
+++ b/nitrokey/LICENSE
@@ -1,3 +1,6 @@
+Valid-License-Identifier: MIT
+License-Text:
+
The MIT License (MIT)
Copyright (c) 2018 Robin Krahl <robin.krahl@ireas.org>
diff --git a/nitrokey/README.md b/nitrokey/README.md
index 567ae58..069fed1 100644
--- a/nitrokey/README.md
+++ b/nitrokey/README.md
@@ -1,3 +1,8 @@
+<!---
+Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org>
+SPDX-License-Identifier: MIT
+-->
+
# nitrokey-rs
A libnitrokey wrapper for Rust providing access to Nitrokey devices.
@@ -30,6 +35,8 @@ supported by `nitrokey-rs`:
configuration).
- `NK_get_status_storage_as_string`. This method only provides an incomplete
string representation of the data returned by `NK_get_status_storage`.
+- `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
@@ -53,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
@@ -71,6 +76,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
@@ -79,3 +86,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/nitrokey/TODO.md b/nitrokey/TODO.md
index 28bd3b8..1ff723d 100644
--- a/nitrokey/TODO.md
+++ b/nitrokey/TODO.md
@@ -1,18 +1,16 @@
+<!---
+Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org>
+SPDX-License-Identifier: MIT
+-->
+
- Add support for the currently unsupported commands:
- - `NK_is_AES_supported`
- `NK_send_startup`
- `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`
-- 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.
-- Check integer conversions.
-- Consider implementing `Into<CommandError>` for `(Device, CommandError)`
- 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/nitrokey/src/auth.rs b/nitrokey/src/auth.rs
index 2d61d4b..18b6572 100644
--- a/nitrokey/src/auth.rs
+++ b/nitrokey/src/auth.rs
@@ -1,3 +1,6 @@
+// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org>
+// SPDX-License-Identifier: MIT
+
use std::ops::Deref;
use std::os::raw::c_char;
use std::os::raw::c_int;
@@ -6,10 +9,9 @@ use nitrokey_sys;
use crate::config::{Config, RawConfig};
use crate::device::{Device, DeviceWrapper, Pro, Storage};
+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, CommandError,
-};
+use crate::util::{generate_password, get_command_result, get_cstring, result_from_string};
static TEMPORARY_PASSWORD_LENGTH: usize = 25;
@@ -34,12 +36,12 @@ pub trait Authenticate {
///
/// ```no_run
/// use nitrokey::{Authenticate, DeviceWrapper, User};
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
/// fn perform_user_task(device: &User<DeviceWrapper>) {}
/// fn perform_other_task(device: &DeviceWrapper) {}
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// let device = match device.authenticate_user("123456") {
/// Ok(user) => {
@@ -56,10 +58,10 @@ 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<User<Self>, (Self, CommandError)>
+ fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, Error)>
where
Self: Device + Sized;
@@ -80,12 +82,12 @@ pub trait Authenticate {
///
/// ```no_run
/// use nitrokey::{Authenticate, Admin, DeviceWrapper};
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
/// fn perform_admin_task(device: &Admin<DeviceWrapper>) {}
/// fn perform_other_task(device: &DeviceWrapper) {}
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// let device = match device.authenticate_admin("123456") {
/// Ok(admin) => {
@@ -102,16 +104,18 @@ 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<Admin<Self>, (Self, CommandError)>
+ fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, Error)>
where
Self: Device + Sized;
}
trait AuthenticatedDevice<T> {
fn new(device: T, temp_password: Vec<u8>) -> Self;
+
+ fn temp_password_ptr(&self) -> *const c_char;
}
/// A Nitrokey device with user authentication.
@@ -144,7 +148,7 @@ pub struct Admin<T: Device> {
temp_password: Vec<u8>,
}
-fn authenticate<D, A, T>(device: D, password: &str, callback: T) -> Result<A, (D, CommandError)>
+fn authenticate<D, A, T>(device: D, password: &str, callback: T) -> Result<A, (D, Error)>
where
D: Device,
A: AuthenticatedDevice<D>,
@@ -160,17 +164,17 @@ where
};
let password_ptr = password.as_ptr();
let temp_password_ptr = temp_password.as_ptr() as *const c_char;
- return match callback(password_ptr, temp_password_ptr) {
+ match callback(password_ptr, temp_password_ptr) {
0 => Ok(A::new(device, temp_password)),
- rv => Err((device, CommandError::from(rv))),
- };
+ rv => Err((device, Error::from(rv))),
+ }
}
fn authenticate_user_wrapper<T, C>(
device: T,
constructor: C,
password: &str,
-) -> Result<User<DeviceWrapper>, (DeviceWrapper, CommandError)>
+) -> Result<User<DeviceWrapper>, (DeviceWrapper, Error)>
where
T: Device,
C: Fn(T) -> DeviceWrapper,
@@ -186,7 +190,7 @@ fn authenticate_admin_wrapper<T, C>(
device: T,
constructor: C,
password: &str,
-) -> Result<Admin<DeviceWrapper>, (DeviceWrapper, CommandError)>
+) -> Result<Admin<DeviceWrapper>, (DeviceWrapper, Error)>
where
T: Device,
C: Fn(T) -> DeviceWrapper,
@@ -216,24 +220,16 @@ impl<T: Device> Deref for User<T> {
}
impl<T: Device> GenerateOtp for User<T> {
- fn get_hotp_code(&self, slot: u8) -> Result<String, CommandError> {
- unsafe {
- let temp_password_ptr = self.temp_password.as_ptr() as *const c_char;
- return result_from_string(nitrokey_sys::NK_get_hotp_code_PIN(slot, temp_password_ptr));
- }
+ fn get_hotp_code(&self, slot: u8) -> Result<String, Error> {
+ result_from_string(unsafe {
+ nitrokey_sys::NK_get_hotp_code_PIN(slot, self.temp_password_ptr())
+ })
}
- fn get_totp_code(&self, slot: u8) -> Result<String, CommandError> {
- unsafe {
- let temp_password_ptr = self.temp_password.as_ptr() as *const c_char;
- return result_from_string(nitrokey_sys::NK_get_totp_code_PIN(
- slot,
- 0,
- 0,
- 0,
- temp_password_ptr,
- ));
- }
+ fn get_totp_code(&self, slot: u8) -> Result<String, Error> {
+ result_from_string(unsafe {
+ nitrokey_sys::NK_get_totp_code_PIN(slot, 0, 0, 0, self.temp_password_ptr())
+ })
}
}
@@ -244,6 +240,10 @@ impl<T: Device> AuthenticatedDevice<T> for User<T> {
temp_password,
}
}
+
+ fn temp_password_ptr(&self) -> *const c_char {
+ self.temp_password.as_ptr() as *const c_char
+ }
}
impl<T: Device> Deref for Admin<T> {
@@ -272,9 +272,9 @@ impl<T: Device> Admin<T> {
///
/// ```no_run
/// use nitrokey::{Authenticate, Config};
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// let config = Config::new(None, None, None, false);
/// match device.authenticate_admin("12345678") {
@@ -288,34 +288,26 @@ impl<T: Device> Admin<T> {
/// # }
/// ```
///
- /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot
- pub fn write_config(&self, config: Config) -> Result<(), CommandError> {
+ /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot
+ pub fn write_config(&self, config: Config) -> Result<(), Error> {
let raw_config = RawConfig::try_from(config)?;
- unsafe {
- get_command_result(nitrokey_sys::NK_write_config(
+ get_command_result(unsafe {
+ nitrokey_sys::NK_write_config(
raw_config.numlock,
raw_config.capslock,
raw_config.scrollock,
raw_config.user_password,
false,
- self.temp_password.as_ptr() as *const c_char,
- ))
- }
- }
-
- fn write_otp_slot<C>(&self, data: OtpSlotData, callback: C) -> Result<(), CommandError>
- where
- C: Fn(RawOtpSlotData, *const c_char) -> c_int,
- {
- let raw_data = RawOtpSlotData::new(data)?;
- let temp_password_ptr = self.temp_password.as_ptr() as *const c_char;
- get_command_result(callback(raw_data, temp_password_ptr))
+ self.temp_password_ptr(),
+ )
+ })
}
}
impl<T: Device> ConfigureOtp for Admin<T> {
- fn write_hotp_slot(&self, data: OtpSlotData, counter: u64) -> Result<(), CommandError> {
- self.write_otp_slot(data, |raw_data: RawOtpSlotData, temp_password_ptr| unsafe {
+ fn write_hotp_slot(&self, data: OtpSlotData, counter: u64) -> Result<(), Error> {
+ let raw_data = RawOtpSlotData::new(data)?;
+ get_command_result(unsafe {
nitrokey_sys::NK_write_hotp_slot(
raw_data.number,
raw_data.name.as_ptr(),
@@ -325,13 +317,14 @@ impl<T: Device> ConfigureOtp for Admin<T> {
raw_data.use_enter,
raw_data.use_token_id,
raw_data.token_id.as_ptr(),
- temp_password_ptr,
+ self.temp_password_ptr(),
)
})
}
- fn write_totp_slot(&self, data: OtpSlotData, time_window: u16) -> Result<(), CommandError> {
- self.write_otp_slot(data, |raw_data: RawOtpSlotData, temp_password_ptr| unsafe {
+ fn write_totp_slot(&self, data: OtpSlotData, time_window: u16) -> Result<(), Error> {
+ let raw_data = RawOtpSlotData::new(data)?;
+ get_command_result(unsafe {
nitrokey_sys::NK_write_totp_slot(
raw_data.number,
raw_data.name.as_ptr(),
@@ -341,19 +334,21 @@ impl<T: Device> ConfigureOtp for Admin<T> {
raw_data.use_enter,
raw_data.use_token_id,
raw_data.token_id.as_ptr(),
- temp_password_ptr,
+ self.temp_password_ptr(),
)
})
}
- fn erase_hotp_slot(&self, slot: u8) -> Result<(), CommandError> {
- let temp_password_ptr = self.temp_password.as_ptr() as *const c_char;
- unsafe { get_command_result(nitrokey_sys::NK_erase_hotp_slot(slot, temp_password_ptr)) }
+ fn erase_hotp_slot(&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<(), CommandError> {
- let temp_password_ptr = self.temp_password.as_ptr() as *const c_char;
- unsafe { get_command_result(nitrokey_sys::NK_erase_totp_slot(slot, temp_password_ptr)) }
+ fn erase_totp_slot(&self, slot: u8) -> Result<(), Error> {
+ get_command_result(unsafe {
+ nitrokey_sys::NK_erase_totp_slot(slot, self.temp_password_ptr())
+ })
}
}
@@ -364,10 +359,14 @@ impl<T: Device> AuthenticatedDevice<T> for Admin<T> {
temp_password,
}
}
+
+ fn temp_password_ptr(&self) -> *const c_char {
+ self.temp_password.as_ptr() as *const c_char
+ }
}
impl Authenticate for DeviceWrapper {
- fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, CommandError)> {
+ fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, Error)> {
match self {
DeviceWrapper::Storage(storage) => {
authenticate_user_wrapper(storage, DeviceWrapper::Storage, password)
@@ -376,7 +375,7 @@ impl Authenticate for DeviceWrapper {
}
}
- fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, CommandError)> {
+ fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, Error)> {
match self {
DeviceWrapper::Storage(storage) => {
authenticate_admin_wrapper(storage, DeviceWrapper::Storage, password)
@@ -389,13 +388,13 @@ impl Authenticate for DeviceWrapper {
}
impl Authenticate for Pro {
- fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, CommandError)> {
+ fn authenticate_user(self, password: &str) -> Result<User<Self>, (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<Admin<Self>, (Self, CommandError)> {
+ fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, Error)> {
authenticate(self, password, |password_ptr, temp_password_ptr| unsafe {
nitrokey_sys::NK_first_authenticate(password_ptr, temp_password_ptr)
})
@@ -403,13 +402,13 @@ impl Authenticate for Pro {
}
impl Authenticate for Storage {
- fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, CommandError)> {
+ fn authenticate_user(self, password: &str) -> Result<User<Self>, (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<Admin<Self>, (Self, CommandError)> {
+ fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, Error)> {
authenticate(self, password, |password_ptr, temp_password_ptr| unsafe {
nitrokey_sys::NK_first_authenticate(password_ptr, temp_password_ptr)
})
diff --git a/nitrokey/src/config.rs b/nitrokey/src/config.rs
index 2ce6f77..c273792 100644
--- a/nitrokey/src/config.rs
+++ b/nitrokey/src/config.rs
@@ -1,4 +1,7 @@
-use crate::util::CommandError;
+// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org>
+// SPDX-License-Identifier: MIT
+
+use crate::error::{Error, LibraryError};
/// The configuration for a Nitrokey.
#[derive(Clone, Copy, Debug, PartialEq)]
@@ -30,21 +33,21 @@ pub struct RawConfig {
fn config_otp_slot_to_option(value: u8) -> Option<u8> {
if value < 3 {
- return Some(value);
+ Some(value)
+ } else {
+ None
}
- None
}
-fn option_to_config_otp_slot(value: Option<u8>) -> Result<u8, CommandError> {
- match value {
- Some(value) => {
- if value < 3 {
- Ok(value)
- } else {
- Err(CommandError::InvalidSlot)
- }
+fn option_to_config_otp_slot(value: Option<u8>) -> Result<u8, Error> {
+ if let Some(value) = value {
+ if value < 3 {
+ Ok(value)
+ } else {
+ Err(LibraryError::InvalidSlot.into())
}
- None => Ok(255),
+ } else {
+ Ok(255)
}
}
@@ -66,7 +69,7 @@ impl Config {
}
impl RawConfig {
- pub fn try_from(config: Config) -> Result<RawConfig, CommandError> {
+ pub fn try_from(config: Config) -> Result<RawConfig, Error> {
Ok(RawConfig {
numlock: option_to_config_otp_slot(config.numlock)?,
capslock: option_to_config_otp_slot(config.capslock)?,
diff --git a/nitrokey/src/device.rs b/nitrokey/src/device.rs
index 9813c50..386ce94 100644
--- a/nitrokey/src/device.rs
+++ b/nitrokey/src/device.rs
@@ -1,15 +1,18 @@
+// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org>
+// SPDX-License-Identifier: MIT
+
use std::fmt;
+use std::marker;
use libc;
use nitrokey_sys;
use crate::auth::Authenticate;
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, CommandError,
-};
+use crate::util::{get_command_result, get_cstring, get_last_error, result_from_string};
/// Available Nitrokey models.
#[derive(Clone, Copy, Debug, PartialEq)]
@@ -22,14 +25,10 @@ pub enum Model {
impl fmt::Display for Model {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(
- f,
- "{}",
- match *self {
- Model::Pro => "Pro",
- Model::Storage => "Storage",
- }
- )
+ f.write_str(match *self {
+ Model::Pro => "Pro",
+ Model::Storage => "Storage",
+ })
}
}
@@ -44,10 +43,10 @@ pub enum VolumeMode {
impl fmt::Display for VolumeMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match *self {
- VolumeMode::ReadOnly => f.write_str("read-only"),
- VolumeMode::ReadWrite => f.write_str("read-write"),
- }
+ f.write_str(match *self {
+ VolumeMode::ReadOnly => "read-only",
+ VolumeMode::ReadWrite => "read-write",
+ })
}
}
@@ -64,12 +63,12 @@ impl fmt::Display for VolumeMode {
///
/// ```no_run
/// use nitrokey::{Authenticate, DeviceWrapper, User};
-/// # use nitrokey::CommandError;
+/// # use nitrokey::Error;
///
/// fn perform_user_task(device: &User<DeviceWrapper>) {}
/// fn perform_other_task(device: &DeviceWrapper) {}
///
-/// # fn try_main() -> Result<(), CommandError> {
+/// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// let device = match device.authenticate_user("123456") {
/// Ok(user) => {
@@ -90,12 +89,12 @@ impl fmt::Display for VolumeMode {
///
/// ```no_run
/// use nitrokey::{DeviceWrapper, Storage};
-/// # use nitrokey::CommandError;
+/// # use nitrokey::Error;
///
/// fn perform_common_task(device: &DeviceWrapper) {}
/// fn perform_storage_task(device: &Storage) {}
///
-/// # fn try_main() -> Result<(), CommandError> {
+/// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// perform_common_task(&device);
/// match device {
@@ -128,12 +127,12 @@ pub enum DeviceWrapper {
///
/// ```no_run
/// use nitrokey::{Authenticate, User, Pro};
-/// # use nitrokey::CommandError;
+/// # use nitrokey::Error;
///
/// fn perform_user_task(device: &User<Pro>) {}
/// fn perform_other_task(device: &Pro) {}
///
-/// # fn try_main() -> Result<(), CommandError> {
+/// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::Pro::connect()?;
/// let device = match device.authenticate_user("123456") {
/// Ok(user) => {
@@ -155,7 +154,11 @@ pub enum DeviceWrapper {
/// [`connect`]: fn.connect.html
/// [`Pro::connect`]: #method.connect
#[derive(Debug)]
-pub struct Pro {}
+pub struct Pro {
+ // make sure that users cannot directly instantiate this type
+ #[doc(hidden)]
+ marker: marker::PhantomData<()>,
+}
/// A Nitrokey Storage device without user or admin authentication.
///
@@ -170,12 +173,12 @@ pub struct Pro {}
///
/// ```no_run
/// use nitrokey::{Authenticate, User, Storage};
-/// # use nitrokey::CommandError;
+/// # use nitrokey::Error;
///
/// fn perform_user_task(device: &User<Storage>) {}
/// fn perform_other_task(device: &Storage) {}
///
-/// # fn try_main() -> Result<(), CommandError> {
+/// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::Storage::connect()?;
/// let device = match device.authenticate_user("123456") {
/// Ok(user) => {
@@ -197,7 +200,11 @@ pub struct Pro {}
/// [`connect`]: fn.connect.html
/// [`Storage::connect`]: #method.connect
#[derive(Debug)]
-pub struct Storage {}
+pub struct Storage {
+ // make sure that users cannot directly instantiate this type
+ #[doc(hidden)]
+ marker: marker::PhantomData<()>,
+}
/// The status of a volume on a Nitrokey Storage device.
#[derive(Debug)]
@@ -225,13 +232,26 @@ pub struct SdCardData {
pub manufacturer: u8,
}
-#[derive(Debug)]
-/// Production information for a Storage device.
-pub struct StorageProductionInfo {
+/// A firmware version for a Nitrokey device.
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub struct FirmwareVersion {
/// The major firmware version, e. g. 0 in v0.40.
- pub firmware_version_major: u8,
+ pub major: u8,
/// The minor firmware version, e. g. 40 in v0.40.
- pub firmware_version_minor: u8,
+ pub minor: u8,
+}
+
+impl fmt::Display for FirmwareVersion {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "v{}.{}", self.major, self.minor)
+ }
+}
+
+/// Production information for a Storage device.
+#[derive(Debug)]
+pub struct StorageProductionInfo {
+ /// The firmware version.
+ pub firmware_version: FirmwareVersion,
/// The internal firmware version.
pub firmware_version_internal: u8,
/// The serial number of the CPU.
@@ -249,10 +269,8 @@ pub struct StorageStatus {
pub encrypted_volume: VolumeStatus,
/// The status of the hidden volume.
pub hidden_volume: VolumeStatus,
- /// 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 firmware version.
+ pub firmware_version: FirmwareVersion,
/// Indicates whether the firmware is locked.
pub firmware_locked: bool,
/// The serial number of the SD card in the Storage stick.
@@ -276,16 +294,16 @@ 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
///
/// ```no_run
/// use nitrokey::Device;
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// println!("Connected to a Nitrokey {}", device.get_model());
/// # Ok(())
@@ -299,9 +317,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {
///
/// ```no_run
/// use nitrokey::Device;
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// match device.get_serial_number() {
/// Ok(number) => println!("serial no: {}", number),
@@ -310,8 +328,8 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {
/// # Ok(())
/// # }
/// ```
- fn get_serial_number(&self) -> Result<String, CommandError> {
- unsafe { result_from_string(nitrokey_sys::NK_device_serial_number()) }
+ fn get_serial_number(&self) -> Result<String, Error> {
+ result_from_string(unsafe { nitrokey_sys::NK_device_serial_number() })
}
/// Returns the number of remaining authentication attempts for the user. The total number of
@@ -321,9 +339,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {
///
/// ```no_run
/// use nitrokey::Device;
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// let count = device.get_user_retry_count();
/// println!("{} remaining authentication attempts (user)", count);
@@ -341,9 +359,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {
///
/// ```no_run
/// use nitrokey::Device;
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// let count = device.get_admin_retry_count();
/// println!("{} remaining authentication attempts (admin)", count);
@@ -360,9 +378,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {
///
/// ```no_run
/// use nitrokey::Device;
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// println!(
/// "Firmware version: {}.{}",
@@ -382,9 +400,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {
///
/// ```no_run
/// use nitrokey::Device;
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// println!(
/// "Firmware version: {}.{}",
@@ -403,9 +421,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {
///
/// ```no_run
/// use nitrokey::Device;
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// let config = device.get_config()?;
/// println!("numlock binding: {:?}", config.numlock);
@@ -415,17 +433,15 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {
/// # Ok(())
/// # }
/// ```
- fn get_config(&self) -> Result<Config, CommandError> {
- unsafe {
- let config_ptr = nitrokey_sys::NK_read_config();
- if config_ptr.is_null() {
- return Err(get_last_error());
- }
- let config_array_ptr = config_ptr as *const [u8; 5];
- let raw_config = RawConfig::from(*config_array_ptr);
- libc::free(config_ptr as *mut libc::c_void);
- return Ok(raw_config.into());
+ fn get_config(&self) -> Result<Config, Error> {
+ let config_ptr = unsafe { nitrokey_sys::NK_read_config() };
+ if config_ptr.is_null() {
+ return Err(get_last_error());
}
+ let config_array_ptr = config_ptr as *const [u8; 5];
+ let raw_config = unsafe { RawConfig::from(*config_array_ptr) };
+ unsafe { libc::free(config_ptr as *mut libc::c_void) };
+ Ok(raw_config.into())
}
/// Changes the administrator PIN.
@@ -439,9 +455,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {
///
/// ```no_run
/// use nitrokey::Device;
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// match device.change_admin_pin("12345678", "12345679") {
/// Ok(()) => println!("Updated admin PIN."),
@@ -451,17 +467,14 @@ 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<(), CommandError> {
+ fn change_admin_pin(&self, current: &str, new: &str) -> Result<(), Error> {
let current_string = get_cstring(current)?;
let new_string = get_cstring(new)?;
- unsafe {
- get_command_result(nitrokey_sys::NK_change_admin_PIN(
- current_string.as_ptr(),
- new_string.as_ptr(),
- ))
- }
+ get_command_result(unsafe {
+ nitrokey_sys::NK_change_admin_PIN(current_string.as_ptr(), new_string.as_ptr())
+ })
}
/// Changes the user PIN.
@@ -475,9 +488,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {
///
/// ```no_run
/// use nitrokey::Device;
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// match device.change_user_pin("123456", "123457") {
/// Ok(()) => println!("Updated admin PIN."),
@@ -487,17 +500,14 @@ 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<(), CommandError> {
+ fn change_user_pin(&self, current: &str, new: &str) -> Result<(), Error> {
let current_string = get_cstring(current)?;
let new_string = get_cstring(new)?;
- unsafe {
- get_command_result(nitrokey_sys::NK_change_user_PIN(
- current_string.as_ptr(),
- new_string.as_ptr(),
- ))
- }
+ get_command_result(unsafe {
+ nitrokey_sys::NK_change_user_PIN(current_string.as_ptr(), new_string.as_ptr())
+ })
}
/// Unlocks the user PIN after three failed login attempts and sets it to the given value.
@@ -511,9 +521,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {
///
/// ```no_run
/// use nitrokey::Device;
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// match device.unlock_user_pin("12345678", "123456") {
/// Ok(()) => println!("Unlocked user PIN."),
@@ -523,17 +533,17 @@ 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<(), CommandError> {
+ fn unlock_user_pin(&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)?;
- unsafe {
- get_command_result(nitrokey_sys::NK_unlock_user_password(
+ get_command_result(unsafe {
+ nitrokey_sys::NK_unlock_user_password(
admin_pin_string.as_ptr(),
user_pin_string.as_ptr(),
- ))
- }
+ )
+ })
}
/// Locks the Nitrokey device.
@@ -545,9 +555,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {
///
/// ```no_run
/// use nitrokey::Device;
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// match device.lock() {
/// Ok(()) => println!("Locked the Nitrokey device."),
@@ -556,8 +566,8 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {
/// # Ok(())
/// # }
/// ```
- fn lock(&self) -> Result<(), CommandError> {
- unsafe { get_command_result(nitrokey_sys::NK_lock_device()) }
+ fn lock(&self) -> Result<(), Error> {
+ get_command_result(unsafe { nitrokey_sys::NK_lock_device() })
}
/// Performs a factory reset on the Nitrokey device.
@@ -576,9 +586,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {
///
/// ```no_run
/// use nitrokey::Device;
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// match device.factory_reset("12345678") {
/// Ok(()) => println!("Performed a factory reset."),
@@ -589,9 +599,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {
/// ```
///
/// [`build_aes_key`]: #method.build_aes_key
- fn factory_reset(&self, admin_pin: &str) -> Result<(), CommandError> {
+ fn factory_reset(&self, admin_pin: &str) -> Result<(), Error> {
let admin_pin_string = get_cstring(admin_pin)?;
- unsafe { get_command_result(nitrokey_sys::NK_factory_reset(admin_pin_string.as_ptr())) }
+ get_command_result(unsafe { nitrokey_sys::NK_factory_reset(admin_pin_string.as_ptr()) })
}
/// Builds a new AES key on the Nitrokey.
@@ -610,9 +620,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {
///
/// ```no_run
/// use nitrokey::Device;
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// match device.build_aes_key("12345678") {
/// Ok(()) => println!("New AES keys have been built."),
@@ -623,9 +633,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {
/// ```
///
/// [`factory_reset`]: #method.factory_reset
- fn build_aes_key(&self, admin_pin: &str) -> Result<(), CommandError> {
+ fn build_aes_key(&self, admin_pin: &str) -> Result<(), Error> {
let admin_pin_string = get_cstring(admin_pin)?;
- unsafe { get_command_result(nitrokey_sys::NK_build_aes_key(admin_pin_string.as_ptr())) }
+ get_command_result(unsafe { nitrokey_sys::NK_build_aes_key(admin_pin_string.as_ptr()) })
}
}
@@ -634,7 +644,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {
///
/// # Errors
///
-/// - [`Undefined`][] if no Nitrokey device is connected
+/// - [`NotConnected`][] if no Nitrokey device is connected
///
/// # Example
///
@@ -649,16 +659,15 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {
/// }
/// ```
///
-/// [`Undefined`]: enum.CommandError.html#variant.Undefined
-pub fn connect() -> Result<DeviceWrapper, CommandError> {
- unsafe {
- match nitrokey_sys::NK_login_auto() {
- 1 => match get_connected_device() {
- Some(wrapper) => Ok(wrapper),
- None => Err(CommandError::Undefined),
- },
- _ => Err(CommandError::Undefined),
+/// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected
+pub fn connect() -> Result<DeviceWrapper, Error> {
+ if unsafe { nitrokey_sys::NK_login_auto() } == 1 {
+ match get_connected_device() {
+ Some(wrapper) => Ok(wrapper),
+ None => Err(CommunicationError::NotConnected.into()),
}
+ } else {
+ Err(CommunicationError::NotConnected.into())
}
}
@@ -666,7 +675,7 @@ pub fn connect() -> Result<DeviceWrapper, CommandError> {
///
/// # Errors
///
-/// - [`Undefined`][] if no Nitrokey device of the given model is connected
+/// - [`NotConnected`][] if no Nitrokey device of the given model is connected
///
/// # Example
///
@@ -682,29 +691,27 @@ pub fn connect() -> Result<DeviceWrapper, CommandError> {
/// }
/// ```
///
-/// [`Undefined`]: enum.CommandError.html#variant.Undefined
-pub fn connect_model(model: Model) -> Result<DeviceWrapper, CommandError> {
+/// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected
+pub fn connect_model(model: Model) -> Result<DeviceWrapper, Error> {
if connect_enum(model) {
Ok(create_device_wrapper(model))
} else {
- Err(CommandError::Undefined)
+ Err(CommunicationError::NotConnected.into())
}
}
fn get_connected_model() -> Option<Model> {
- unsafe {
- match nitrokey_sys::NK_get_device_model() {
- nitrokey_sys::NK_device_model_NK_PRO => Some(Model::Pro),
- nitrokey_sys::NK_device_model_NK_STORAGE => Some(Model::Storage),
- _ => None,
- }
+ match unsafe { nitrokey_sys::NK_get_device_model() } {
+ nitrokey_sys::NK_device_model_NK_PRO => Some(Model::Pro),
+ nitrokey_sys::NK_device_model_NK_STORAGE => Some(Model::Storage),
+ _ => None,
}
}
fn create_device_wrapper(model: Model) -> DeviceWrapper {
match model {
- Model::Pro => DeviceWrapper::Pro(Pro {}),
- Model::Storage => DeviceWrapper::Storage(Storage {}),
+ Model::Pro => Pro::new().into(),
+ Model::Storage => Storage::new().into(),
}
}
@@ -729,20 +736,32 @@ impl DeviceWrapper {
}
}
+impl From<Pro> for DeviceWrapper {
+ fn from(device: Pro) -> Self {
+ DeviceWrapper::Pro(device)
+ }
+}
+
+impl From<Storage> for DeviceWrapper {
+ fn from(device: Storage) -> Self {
+ DeviceWrapper::Storage(device)
+ }
+}
+
impl GenerateOtp for DeviceWrapper {
- fn get_hotp_slot_name(&self, slot: u8) -> Result<String, CommandError> {
+ fn get_hotp_slot_name(&self, slot: u8) -> Result<String, Error> {
self.device().get_hotp_slot_name(slot)
}
- fn get_totp_slot_name(&self, slot: u8) -> Result<String, CommandError> {
+ fn get_totp_slot_name(&self, slot: u8) -> Result<String, Error> {
self.device().get_totp_slot_name(slot)
}
- fn get_hotp_code(&self, slot: u8) -> Result<String, CommandError> {
+ fn get_hotp_code(&self, slot: u8) -> Result<String, Error> {
self.device().get_hotp_code(slot)
}
- fn get_totp_code(&self, slot: u8) -> Result<String, CommandError> {
+ fn get_totp_code(&self, slot: u8) -> Result<String, Error> {
self.device().get_totp_code(slot)
}
}
@@ -761,7 +780,7 @@ impl Pro {
///
/// # Errors
///
- /// - [`Undefined`][] if no Nitrokey device of the given model is connected
+ /// - [`NotConnected`][] if no Nitrokey device of the given model is connected
///
/// # Example
///
@@ -776,12 +795,19 @@ impl Pro {
/// }
/// ```
///
- /// [`Undefined`]: enum.CommandError.html#variant.Undefined
- pub fn connect() -> Result<Pro, CommandError> {
+ /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected
+ pub fn connect() -> Result<Pro, Error> {
// TODO: maybe Option instead of Result?
- match connect_enum(Model::Pro) {
- true => Ok(Pro {}),
- false => Err(CommandError::Undefined),
+ if connect_enum(Model::Pro) {
+ Ok(Pro::new())
+ } else {
+ Err(CommunicationError::NotConnected.into())
+ }
+ }
+
+ fn new() -> Pro {
+ Pro {
+ marker: marker::PhantomData,
}
}
}
@@ -807,7 +833,7 @@ impl Storage {
///
/// # Errors
///
- /// - [`Undefined`][] if no Nitrokey device of the given model is connected
+ /// - [`NotConnected`][] if no Nitrokey device of the given model is connected
///
/// # Example
///
@@ -822,12 +848,19 @@ impl Storage {
/// }
/// ```
///
- /// [`Undefined`]: enum.CommandError.html#variant.Undefined
- pub fn connect() -> Result<Storage, CommandError> {
+ /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected
+ pub fn connect() -> Result<Storage, Error> {
// TODO: maybe Option instead of Result?
- match connect_enum(Model::Storage) {
- true => Ok(Storage {}),
- false => Err(CommandError::Undefined),
+ if connect_enum(Model::Storage) {
+ Ok(Storage::new())
+ } else {
+ Err(CommunicationError::NotConnected.into())
+ }
+ }
+
+ fn new() -> Storage {
+ Storage {
+ marker: marker::PhantomData,
}
}
@@ -845,9 +878,9 @@ impl Storage {
/// # Example
///
/// ```no_run
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::Storage::connect()?;
/// match device.change_update_pin("12345678", "87654321") {
/// Ok(()) => println!("Updated update PIN."),
@@ -857,17 +890,14 @@ 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<(), CommandError> {
+ pub fn change_update_pin(&self, current: &str, new: &str) -> Result<(), Error> {
let current_string = get_cstring(current)?;
let new_string = get_cstring(new)?;
- unsafe {
- get_command_result(nitrokey_sys::NK_change_update_password(
- current_string.as_ptr(),
- new_string.as_ptr(),
- ))
- }
+ get_command_result(unsafe {
+ nitrokey_sys::NK_change_update_password(current_string.as_ptr(), new_string.as_ptr())
+ })
}
/// Enables the firmware update mode.
@@ -885,9 +915,9 @@ impl Storage {
/// # Example
///
/// ```no_run
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::Storage::connect()?;
/// match device.enable_firmware_update("12345678") {
/// Ok(()) => println!("Nitrokey entered update mode."),
@@ -897,15 +927,13 @@ 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<(), CommandError> {
+ pub fn enable_firmware_update(&self, update_pin: &str) -> Result<(), Error> {
let update_pin_string = get_cstring(update_pin)?;
- unsafe {
- get_command_result(nitrokey_sys::NK_enable_firmware_update(
- update_pin_string.as_ptr(),
- ))
- }
+ get_command_result(unsafe {
+ nitrokey_sys::NK_enable_firmware_update(update_pin_string.as_ptr())
+ })
}
/// Enables the encrypted storage volume.
@@ -921,9 +949,9 @@ impl Storage {
/// # Example
///
/// ```no_run
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::Storage::connect()?;
/// match device.enable_encrypted_volume("123456") {
/// Ok(()) => println!("Enabled the encrypted volume."),
@@ -933,11 +961,11 @@ 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<(), CommandError> {
+ pub fn enable_encrypted_volume(&self, user_pin: &str) -> Result<(), Error> {
let user_pin = get_cstring(user_pin)?;
- unsafe { get_command_result(nitrokey_sys::NK_unlock_encrypted_volume(user_pin.as_ptr())) }
+ get_command_result(unsafe { nitrokey_sys::NK_unlock_encrypted_volume(user_pin.as_ptr()) })
}
/// Disables the encrypted storage volume.
@@ -948,11 +976,11 @@ impl Storage {
/// # Example
///
/// ```no_run
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
/// fn use_volume() {}
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::Storage::connect()?;
/// match device.enable_encrypted_volume("123456") {
/// Ok(()) => {
@@ -970,8 +998,8 @@ impl Storage {
/// # Ok(())
/// # }
/// ```
- pub fn disable_encrypted_volume(&self) -> Result<(), CommandError> {
- unsafe { get_command_result(nitrokey_sys::NK_lock_encrypted_volume()) }
+ pub fn disable_encrypted_volume(&self) -> Result<(), Error> {
+ get_command_result(unsafe { nitrokey_sys::NK_lock_encrypted_volume() })
}
/// Enables a hidden storage volume.
@@ -996,9 +1024,9 @@ impl Storage {
/// # Example
///
/// ```no_run
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::Storage::connect()?;
/// device.enable_encrypted_volume("123445")?;
/// match device.enable_hidden_volume("hidden-pw") {
@@ -1011,14 +1039,12 @@ impl Storage {
///
/// [`enable_encrypted_volume`]: #method.enable_encrypted_volume
/// [`AesDecryptionFailed`]: enum.CommandError.html#variant.AesDecryptionFailed
- /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString
- pub fn enable_hidden_volume(&self, volume_password: &str) -> Result<(), CommandError> {
+ /// [`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 {
- get_command_result(nitrokey_sys::NK_unlock_hidden_volume(
- volume_password.as_ptr(),
- ))
- }
+ get_command_result(unsafe {
+ nitrokey_sys::NK_unlock_hidden_volume(volume_password.as_ptr())
+ })
}
/// Disables a hidden storage volume.
@@ -1029,11 +1055,11 @@ impl Storage {
/// # Example
///
/// ```no_run
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
/// fn use_volume() {}
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::Storage::connect()?;
/// device.enable_encrypted_volume("123445")?;
/// match device.enable_hidden_volume("hidden-pw") {
@@ -1052,8 +1078,8 @@ impl Storage {
/// # Ok(())
/// # }
/// ```
- pub fn disable_hidden_volume(&self) -> Result<(), CommandError> {
- unsafe { get_command_result(nitrokey_sys::NK_lock_hidden_volume()) }
+ pub fn disable_hidden_volume(&self) -> Result<(), Error> {
+ get_command_result(unsafe { nitrokey_sys::NK_lock_hidden_volume() })
}
/// Creates a hidden volume.
@@ -1078,9 +1104,9 @@ impl Storage {
/// # Example
///
/// ```no_run
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::Storage::connect()?;
/// device.enable_encrypted_volume("123445")?;
/// device.create_hidden_volume(0, 0, 100, "hidden-pw")?;
@@ -1089,23 +1115,18 @@ 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,
start: u8,
end: u8,
password: &str,
- ) -> Result<(), CommandError> {
+ ) -> Result<(), Error> {
let password = get_cstring(password)?;
- unsafe {
- get_command_result(nitrokey_sys::NK_create_hidden_volume(
- slot,
- start,
- end,
- password.as_ptr(),
- ))
- }
+ get_command_result(unsafe {
+ nitrokey_sys::NK_create_hidden_volume(slot, start, end, password.as_ptr())
+ })
}
/// Sets the access mode of the unencrypted volume.
@@ -1122,10 +1143,10 @@ impl Storage {
/// # Example
///
/// ```no_run
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
/// use nitrokey::VolumeMode;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::Storage::connect()?;
/// match device.set_unencrypted_volume_mode("123456", VolumeMode::ReadWrite) {
/// Ok(()) => println!("Set the unencrypted volume to read-write mode."),
@@ -1135,13 +1156,13 @@ 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,
admin_pin: &str,
mode: VolumeMode,
- ) -> Result<(), CommandError> {
+ ) -> Result<(), Error> {
let admin_pin = get_cstring(admin_pin)?;
let result = match mode {
VolumeMode::ReadOnly => unsafe {
@@ -1159,11 +1180,11 @@ impl Storage {
/// # Example
///
/// ```no_run
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
/// fn use_volume() {}
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::Storage::connect()?;
/// match device.get_status() {
/// Ok(status) => {
@@ -1174,7 +1195,7 @@ impl Storage {
/// # Ok(())
/// # }
/// ```
- pub fn get_status(&self) -> Result<StorageStatus, CommandError> {
+ pub fn get_status(&self) -> Result<StorageStatus, Error> {
let mut raw_status = nitrokey_sys::NK_storage_status {
unencrypted_volume_read_only: false,
unencrypted_volume_active: false,
@@ -1194,8 +1215,7 @@ impl Storage {
stick_initialized: false,
};
let raw_result = unsafe { nitrokey_sys::NK_get_status_storage(&mut raw_status) };
- let result = get_command_result(raw_result);
- result.and(Ok(StorageStatus::from(raw_status)))
+ get_command_result(raw_result).map(|_| StorageStatus::from(raw_status))
}
/// Returns the production information for the connected storage device.
@@ -1203,11 +1223,11 @@ impl Storage {
/// # Example
///
/// ```no_run
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
/// fn use_volume() {}
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::Storage::connect()?;
/// match device.get_production_info() {
/// Ok(data) => {
@@ -1219,7 +1239,7 @@ impl Storage {
/// # Ok(())
/// # }
/// ```
- pub fn get_production_info(&self) -> Result<StorageProductionInfo, CommandError> {
+ pub fn get_production_info(&self) -> Result<StorageProductionInfo, Error> {
let mut raw_data = nitrokey_sys::NK_storage_ProductionTest {
FirmwareVersion_au8: [0, 2],
FirmwareVersionInternal_u8: 0,
@@ -1236,8 +1256,7 @@ impl Storage {
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)))
+ get_command_result(raw_result).map(|_| StorageProductionInfo::from(raw_data))
}
/// Clears the warning for a new SD card.
@@ -1254,9 +1273,9 @@ impl Storage {
/// # Example
///
/// ```no_run
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::Storage::connect()?;
/// match device.clear_new_sd_card_warning("12345678") {
/// Ok(()) => println!("Cleared the new SD card warning."),
@@ -1266,9 +1285,9 @@ 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<(), CommandError> {
+ pub fn clear_new_sd_card_warning(&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())
@@ -1276,7 +1295,7 @@ impl Storage {
}
/// Blinks the red and green LED alternatively and infinitely until the device is reconnected.
- pub fn wink(&self) -> Result<(), CommandError> {
+ pub fn wink(&self) -> Result<(), Error> {
get_command_result(unsafe { nitrokey_sys::NK_wink() })
}
@@ -1294,9 +1313,9 @@ 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<(), CommandError> {
+ pub fn export_firmware(&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()) })
}
@@ -1321,8 +1340,10 @@ 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: FirmwareVersion {
+ major: data.FirmwareVersion_au8[0],
+ minor: data.FirmwareVersion_au8[1],
+ },
firmware_version_internal: data.FirmwareVersionInternal_u8,
serial_number_cpu: data.CPU_CardID_u32,
sd_card: SdCardData {
@@ -1352,8 +1373,10 @@ impl From<nitrokey_sys::NK_storage_status> for StorageStatus {
read_only: status.hidden_volume_read_only,
active: status.hidden_volume_active,
},
- firmware_version_major: status.firmware_version_major,
- firmware_version_minor: status.firmware_version_minor,
+ firmware_version: FirmwareVersion {
+ major: status.firmware_version_major,
+ minor: status.firmware_version_minor,
+ },
firmware_locked: status.firmware_locked,
serial_number_sd_card: status.serial_number_sd_card,
serial_number_smart_card: status.serial_number_smart_card,
diff --git a/nitrokey/src/error.rs b/nitrokey/src/error.rs
new file mode 100644
index 0000000..1730171
--- /dev/null
+++ b/nitrokey/src/error.rs
@@ -0,0 +1,245 @@
+// Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org>
+// SPDX-License-Identifier: MIT
+
+use std::error;
+use std::fmt;
+use std::os::raw;
+use std::str;
+
+use crate::device;
+
+/// An error returned by the nitrokey crate.
+#[derive(Debug)]
+pub enum Error {
+ /// An error reported by the Nitrokey device in the response packet.
+ CommandError(CommandError),
+ /// A device communication.
+ CommunicationError(CommunicationError),
+ /// A library usage error.
+ LibraryError(LibraryError),
+ /// An error that occured during random number generation.
+ RandError(Box<dyn error::Error>),
+ /// An error that is caused by an unexpected value returned by libnitrokey.
+ UnexpectedError,
+ /// An unknown error returned by libnitrokey.
+ UnknownError(i64),
+ /// An error occurred when interpreting a UTF-8 string.
+ Utf8Error(str::Utf8Error),
+}
+
+impl From<raw::c_int> 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) = CommunicationError::try_from(256 - code) {
+ Error::CommunicationError(err)
+ } else if let Some(err) = LibraryError::try_from(code) {
+ Error::LibraryError(err)
+ } else {
+ Error::UnknownError(code.into())
+ }
+ }
+}
+
+impl From<CommandError> for Error {
+ fn from(err: CommandError) -> Self {
+ Error::CommandError(err)
+ }
+}
+
+impl From<CommunicationError> for Error {
+ fn from(err: CommunicationError) -> Self {
+ Error::CommunicationError(err)
+ }
+}
+
+impl From<LibraryError> for Error {
+ fn from(err: LibraryError) -> Self {
+ Error::LibraryError(err)
+ }
+}
+
+impl From<str::Utf8Error> for Error {
+ fn from(error: str::Utf8Error) -> Self {
+ Error::Utf8Error(error)
+ }
+}
+
+impl<T: device::Device> From<(T, Error)> for Error {
+ fn from((_, err): (T, Error)) -> Self {
+ err
+ }
+}
+
+impl error::Error for Error {
+ fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+ match *self {
+ Error::CommandError(ref err) => Some(err),
+ Error::CommunicationError(ref err) => Some(err),
+ Error::LibraryError(ref err) => Some(err),
+ Error::RandError(ref err) => Some(err.as_ref()),
+ Error::UnexpectedError => None,
+ Error::UnknownError(_) => None,
+ Error::Utf8Error(ref err) => Some(err),
+ }
+ }
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match *self {
+ Error::CommandError(ref err) => write!(f, "Command error: {}", err),
+ 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::UnknownError(ref err) => write!(f, "Unknown error: {}", err),
+ Error::Utf8Error(ref err) => write!(f, "UTF-8 error: {}", err),
+ }
+ }
+}
+
+/// An error reported by the Nitrokey device in the response packet.
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum CommandError {
+ /// A packet with a wrong checksum has been sent or received.
+ WrongCrc,
+ /// A command tried to access an OTP slot that does not exist.
+ WrongSlot,
+ /// A command tried to generate an OTP on a slot that is not configured.
+ SlotNotProgrammed,
+ /// The provided password is wrong.
+ WrongPassword,
+ /// You are not authorized for this command or provided a wrong temporary
+ /// password.
+ NotAuthorized,
+ /// An error occurred when getting or setting the time.
+ Timestamp,
+ /// You did not provide a name for the OTP slot.
+ NoName,
+ /// This command is not supported by this device.
+ NotSupported,
+ /// This command is unknown.
+ UnknownCommand,
+ /// AES decryption failed.
+ AesDecryptionFailed,
+}
+
+impl CommandError {
+ fn try_from(value: raw::c_int) -> Option<Self> {
+ 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),
+ _ => None,
+ }
+ }
+}
+
+impl error::Error for CommandError {}
+
+impl fmt::Display for CommandError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(match *self {
+ CommandError::WrongCrc => "A packet with a wrong checksum has been sent or received",
+ CommandError::WrongSlot => "The given slot does not exist",
+ CommandError::SlotNotProgrammed => "The given slot is not programmed",
+ CommandError::WrongPassword => "The given password is wrong",
+ CommandError::NotAuthorized => {
+ "You are not authorized for this command or provided a wrong temporary \
+ password"
+ }
+ CommandError::Timestamp => "An error occurred when getting or setting the time",
+ CommandError::NoName => "You did not provide a name for the slot",
+ CommandError::NotSupported => "This command is not supported by this device",
+ CommandError::UnknownCommand => "This command is unknown",
+ CommandError::AesDecryptionFailed => "AES decryption failed",
+ })
+ }
+}
+
+/// A device communication error.
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum CommunicationError {
+ /// Could not connect to a Nitrokey device.
+ NotConnected,
+ /// Sending a packet failed.
+ SendingFailure,
+ /// Receiving a packet failed.
+ ReceivingFailure,
+ /// A packet with a wrong checksum was received.
+ InvalidCrc,
+}
+
+impl CommunicationError {
+ fn try_from(value: raw::c_int) -> Option<Self> {
+ match value {
+ 2 => Some(CommunicationError::NotConnected),
+ 3 => Some(CommunicationError::SendingFailure),
+ 4 => Some(CommunicationError::ReceivingFailure),
+ 5 => Some(CommunicationError::InvalidCrc),
+ _ => None,
+ }
+ }
+}
+
+impl error::Error for CommunicationError {}
+
+impl fmt::Display for CommunicationError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(match *self {
+ CommunicationError::NotConnected => "Could not connect to a Nitrokey device",
+ CommunicationError::SendingFailure => "Sending a packet failed",
+ CommunicationError::ReceivingFailure => "Receiving a packet failed",
+ CommunicationError::InvalidCrc => "A packet with a wrong checksum was received",
+ })
+ }
+}
+
+/// 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<Self> {
+ 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/nitrokey/src/lib.rs b/nitrokey/src/lib.rs
index 02a622b..f2d524e 100644
--- a/nitrokey/src/lib.rs
+++ b/nitrokey/src/lib.rs
@@ -1,3 +1,6 @@
+// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org>
+// SPDX-License-Identifier: MIT
+
//! Provides access to a Nitrokey device using the native libnitrokey API.
//!
//! # Usage
@@ -25,9 +28,9 @@
//!
//! ```no_run
//! use nitrokey::Device;
-//! # use nitrokey::CommandError;
+//! # use nitrokey::Error;
//!
-//! # fn try_main() -> Result<(), CommandError> {
+//! # fn try_main() -> Result<(), Error> {
//! let device = nitrokey::connect()?;
//! println!("{}", device.get_serial_number()?);
//! # Ok(())
@@ -38,9 +41,9 @@
//!
//! ```no_run
//! use nitrokey::{Authenticate, ConfigureOtp, OtpMode, OtpSlotData};
-//! # use nitrokey::CommandError;
+//! # use nitrokey::Error;
//!
-//! # fn try_main() -> Result<(), (CommandError)> {
+//! # fn try_main() -> Result<(), Error> {
//! let device = nitrokey::connect()?;
//! let slot_data = OtpSlotData::new(1, "test", "01234567890123456689", OtpMode::SixDigits);
//! match device.authenticate_admin("12345678") {
@@ -60,9 +63,9 @@
//!
//! ```no_run
//! use nitrokey::{Device, GenerateOtp};
-//! # use nitrokey::CommandError;
+//! # use nitrokey::Error;
//!
-//! # fn try_main() -> Result<(), (CommandError)> {
+//! # fn try_main() -> Result<(), Error> {
//! let device = nitrokey::connect()?;
//! match device.get_hotp_code(1) {
//! Ok(code) => println!("Generated HOTP code: {}", code),
@@ -89,10 +92,13 @@
mod auth;
mod config;
mod device;
+mod error;
mod otp;
mod pws;
mod util;
+use std::fmt;
+
use nitrokey_sys;
pub use crate::auth::{Admin, Authenticate, User};
@@ -101,9 +107,15 @@ pub use crate::device::{
connect, connect_model, 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};
pub use crate::pws::{GetPasswordSafe, PasswordSafe, SLOT_COUNT};
-pub use crate::util::{CommandError, LogLevel};
+pub use crate::util::LogLevel;
+
+/// The default admin PIN for all Nitrokey devices.
+pub const DEFAULT_ADMIN_PIN: &str = "12345678";
+/// The default user PIN for all Nitrokey devices.
+pub const DEFAULT_USER_PIN: &str = "123456";
/// A version of the libnitrokey library.
///
@@ -125,6 +137,16 @@ pub struct Version {
pub minor: u32,
}
+impl fmt::Display for Version {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if self.git.is_empty() {
+ write!(f, "v{}.{}", self.major, self.minor)
+ } else {
+ f.write_str(&self.git)
+ }
+ }
+}
+
/// 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`][]).
@@ -149,21 +171,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<Version, Error> {
// 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/nitrokey/src/otp.rs b/nitrokey/src/otp.rs
index 901bef9..6e0379b 100644
--- a/nitrokey/src/otp.rs
+++ b/nitrokey/src/otp.rs
@@ -1,8 +1,12 @@
+// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org>
+// SPDX-License-Identifier: MIT
+
use std::ffi::CString;
use nitrokey_sys;
-use crate::util::{get_command_result, get_cstring, result_from_string, CommandError};
+use crate::error::Error;
+use crate::util::{get_command_result, get_cstring, result_from_string};
/// Modes for one-time password generation.
#[derive(Clone, Copy, Debug, PartialEq)]
@@ -28,9 +32,9 @@ pub trait ConfigureOtp {
///
/// ```no_run
/// use nitrokey::{Authenticate, ConfigureOtp, OtpMode, OtpSlotData};
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), (CommandError)> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// let slot_data = OtpSlotData::new(1, "test", "01234567890123456689", OtpMode::SixDigits);
/// match device.authenticate_admin("12345678") {
@@ -46,10 +50,10 @@ 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<(), CommandError>;
+ fn write_hotp_slot(&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).
@@ -64,9 +68,9 @@ pub trait ConfigureOtp {
///
/// ```no_run
/// use nitrokey::{Authenticate, ConfigureOtp, OtpMode, OtpSlotData};
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), (CommandError)> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// let slot_data = OtpSlotData::new(1, "test", "01234567890123456689", OtpMode::EightDigits);
/// match device.authenticate_admin("12345678") {
@@ -82,10 +86,10 @@ 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<(), CommandError>;
+ fn write_totp_slot(&self, data: OtpSlotData, time_window: u16) -> Result<(), Error>;
/// Erases an HOTP slot.
///
@@ -97,9 +101,9 @@ pub trait ConfigureOtp {
///
/// ```no_run
/// use nitrokey::{Authenticate, ConfigureOtp};
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), (CommandError)> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// match device.authenticate_admin("12345678") {
/// Ok(admin) => {
@@ -114,8 +118,8 @@ pub trait ConfigureOtp {
/// # }
/// ```
///
- /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot
- fn erase_hotp_slot(&self, slot: u8) -> Result<(), CommandError>;
+ /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot
+ fn erase_hotp_slot(&self, slot: u8) -> Result<(), Error>;
/// Erases a TOTP slot.
///
@@ -127,9 +131,9 @@ pub trait ConfigureOtp {
///
/// ```no_run
/// use nitrokey::{Authenticate, ConfigureOtp};
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), (CommandError)> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// match device.authenticate_admin("12345678") {
/// Ok(admin) => {
@@ -144,8 +148,8 @@ pub trait ConfigureOtp {
/// # }
/// ```
///
- /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot
- fn erase_totp_slot(&self, slot: u8) -> Result<(), CommandError>;
+ /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot
+ fn erase_totp_slot(&self, slot: u8) -> Result<(), Error>;
}
/// Provides methods to generate OTP codes and to query OTP slots on a Nitrokey
@@ -164,9 +168,9 @@ pub trait GenerateOtp {
/// ```no_run
/// use std::time;
/// use nitrokey::GenerateOtp;
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// let time = time::SystemTime::now().duration_since(time::UNIX_EPOCH);
/// match time {
@@ -183,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<(), CommandError> {
+ fn set_time(&self, time: u64, force: bool) -> Result<(), Error> {
let result = if force {
unsafe { nitrokey_sys::NK_totp_set_time(time) }
} else {
@@ -202,23 +206,23 @@ pub trait GenerateOtp {
/// # Example
///
/// ```no_run
- /// use nitrokey::{CommandError, GenerateOtp};
+ /// use nitrokey::{CommandError, Error, GenerateOtp};
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// match device.get_hotp_slot_name(1) {
/// Ok(name) => println!("HOTP slot 1: {}", name),
- /// Err(CommandError::SlotNotProgrammed) => println!("HOTP slot 1 not programmed"),
+ /// Err(Error::CommandError(CommandError::SlotNotProgrammed)) => println!("HOTP slot 1 not programmed"),
/// Err(err) => println!("Could not get slot name: {}", err),
/// };
/// # Ok(())
/// # }
/// ```
///
- /// [`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<String, CommandError> {
- unsafe { result_from_string(nitrokey_sys::NK_get_hotp_slot_name(slot)) }
+ fn get_hotp_slot_name(&self, slot: u8) -> Result<String, Error> {
+ result_from_string(unsafe { nitrokey_sys::NK_get_hotp_slot_name(slot) })
}
/// Returns the name of the given TOTP slot.
@@ -231,23 +235,23 @@ pub trait GenerateOtp {
/// # Example
///
/// ```no_run
- /// use nitrokey::{CommandError, GenerateOtp};
+ /// use nitrokey::{CommandError, Error, GenerateOtp};
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// match device.get_totp_slot_name(1) {
/// Ok(name) => println!("TOTP slot 1: {}", name),
- /// Err(CommandError::SlotNotProgrammed) => println!("TOTP slot 1 not programmed"),
+ /// Err(Error::CommandError(CommandError::SlotNotProgrammed)) => println!("TOTP slot 1 not programmed"),
/// Err(err) => println!("Could not get slot name: {}", err),
/// };
/// # Ok(())
/// # }
/// ```
///
- /// [`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<String, CommandError> {
- unsafe { result_from_string(nitrokey_sys::NK_get_totp_slot_name(slot)) }
+ fn get_totp_slot_name(&self, slot: u8) -> Result<String, Error> {
+ result_from_string(unsafe { nitrokey_sys::NK_get_totp_slot_name(slot) })
}
/// Generates an HOTP code on the given slot. This operation may require user authorization,
@@ -263,9 +267,9 @@ pub trait GenerateOtp {
///
/// ```no_run
/// use nitrokey::GenerateOtp;
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// let code = device.get_hotp_code(1)?;
/// println!("Generated HOTP code on slot 1: {}", code);
@@ -274,13 +278,11 @@ 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<String, CommandError> {
- unsafe {
- return result_from_string(nitrokey_sys::NK_get_hotp_code(slot));
- }
+ fn get_hotp_code(&self, slot: u8) -> Result<String, Error> {
+ result_from_string(unsafe { nitrokey_sys::NK_get_hotp_code(slot) })
}
/// Generates a TOTP code on the given slot. This operation may require user authorization,
@@ -300,9 +302,9 @@ pub trait GenerateOtp {
/// ```no_run
/// use std::time;
/// use nitrokey::GenerateOtp;
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// let time = time::SystemTime::now().duration_since(time::UNIX_EPOCH);
/// match time {
@@ -319,13 +321,11 @@ 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<String, CommandError> {
- unsafe {
- return result_from_string(nitrokey_sys::NK_get_totp_code(slot, 0, 0, 0));
- }
+ fn get_totp_code(&self, slot: u8) -> Result<String, Error> {
+ result_from_string(unsafe { nitrokey_sys::NK_get_totp_code(slot, 0, 0, 0) })
}
}
@@ -395,7 +395,7 @@ impl OtpSlotData {
}
impl RawOtpSlotData {
- pub fn new(data: OtpSlotData) -> Result<RawOtpSlotData, CommandError> {
+ pub fn new(data: OtpSlotData) -> Result<RawOtpSlotData, Error> {
let name = get_cstring(data.name)?;
let secret = get_cstring(data.secret)?;
let use_token_id = data.token_id.is_some();
diff --git a/nitrokey/src/pws.rs b/nitrokey/src/pws.rs
index 28f0681..fcf057b 100644
--- a/nitrokey/src/pws.rs
+++ b/nitrokey/src/pws.rs
@@ -1,10 +1,12 @@
+// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org>
+// SPDX-License-Identifier: MIT
+
use libc;
use nitrokey_sys;
use crate::device::{Device, DeviceWrapper, Pro, Storage};
-use crate::util::{
- get_command_result, get_cstring, get_last_error, result_from_string, CommandError,
-};
+use crate::error::{CommandError, Error};
+use crate::util::{get_command_result, get_cstring, get_last_error, result_from_string};
/// The number of slots in a [`PasswordSafe`][].
///
@@ -30,9 +32,9 @@ pub const SLOT_COUNT: u8 = 16;
///
/// ```no_run
/// use nitrokey::{Device, GetPasswordSafe, PasswordSafe};
-/// # use nitrokey::CommandError;
+/// # use nitrokey::Error;
///
-/// fn use_password_safe(pws: &PasswordSafe) -> Result<(), CommandError> {
+/// fn use_password_safe(pws: &PasswordSafe) -> Result<(), Error> {
/// let name = pws.get_slot_name(0)?;
/// let login = pws.get_slot_login(0)?;
/// let password = pws.get_slot_login(0)?;
@@ -40,7 +42,7 @@ pub const SLOT_COUNT: u8 = 16;
/// Ok(())
/// }
///
-/// # fn try_main() -> Result<(), CommandError> {
+/// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// let pws = device.get_password_safe("123456")?;
/// use_password_safe(&pws);
@@ -53,6 +55,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,
}
@@ -89,11 +92,11 @@ pub trait GetPasswordSafe {
///
/// ```no_run
/// use nitrokey::{Device, GetPasswordSafe, PasswordSafe};
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
/// fn use_password_safe(pws: &PasswordSafe) {}
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// match device.get_password_safe("123456") {
/// Ok(pws) => {
@@ -110,28 +113,24 @@ 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<PasswordSafe<'_>, CommandError>;
+ fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe<'_>, Error>;
}
fn get_password_safe<'a>(
device: &'a dyn Device,
user_pin: &str,
-) -> Result<PasswordSafe<'a>, CommandError> {
+) -> Result<PasswordSafe<'a>, Error> {
let user_pin_string = get_cstring(user_pin)?;
- let result = unsafe {
- get_command_result(nitrokey_sys::NK_enable_password_safe(
- user_pin_string.as_ptr(),
- ))
- };
- result.map(|()| PasswordSafe { _device: device })
+ 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<String, CommandError> {
+fn get_pws_result(s: String) -> Result<String, Error> {
if s.is_empty() {
- Err(CommandError::SlotNotProgrammed)
+ Err(CommandError::SlotNotProgrammed.into())
} else {
Ok(s)
}
@@ -146,9 +145,9 @@ impl<'a> PasswordSafe<'a> {
///
/// ```no_run
/// use nitrokey::{GetPasswordSafe, SLOT_COUNT};
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// let pws = device.get_password_safe("123456")?;
/// pws.get_slot_status()?.iter().enumerate().for_each(|(slot, programmed)| {
@@ -161,7 +160,7 @@ impl<'a> PasswordSafe<'a> {
/// # Ok(())
/// # }
/// ```
- pub fn get_slot_status(&self) -> Result<[bool; SLOT_COUNT as usize], CommandError> {
+ pub fn get_slot_status(&self) -> Result<[bool; SLOT_COUNT as usize], Error> {
let status_ptr = unsafe { nitrokey_sys::NK_get_password_safe_slot_status() };
if status_ptr.is_null() {
return Err(get_last_error());
@@ -191,9 +190,9 @@ impl<'a> PasswordSafe<'a> {
///
/// ```no_run
/// use nitrokey::GetPasswordSafe;
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// match device.get_password_safe("123456") {
/// Ok(pws) => {
@@ -208,10 +207,10 @@ 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<String, CommandError> {
- unsafe { result_from_string(nitrokey_sys::NK_get_password_safe_slot_name(slot)) }
+ pub fn get_slot_name(&self, slot: u8) -> Result<String, Error> {
+ result_from_string(unsafe { nitrokey_sys::NK_get_password_safe_slot_name(slot) })
.and_then(get_pws_result)
}
@@ -228,9 +227,9 @@ impl<'a> PasswordSafe<'a> {
///
/// ```no_run
/// use nitrokey::GetPasswordSafe;
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// let pws = device.get_password_safe("123456")?;
/// let name = pws.get_slot_name(0)?;
@@ -241,10 +240,10 @@ 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<String, CommandError> {
- unsafe { result_from_string(nitrokey_sys::NK_get_password_safe_slot_login(slot)) }
+ pub fn get_slot_login(&self, slot: u8) -> Result<String, Error> {
+ result_from_string(unsafe { nitrokey_sys::NK_get_password_safe_slot_login(slot) })
.and_then(get_pws_result)
}
@@ -261,9 +260,9 @@ impl<'a> PasswordSafe<'a> {
///
/// ```no_run
/// use nitrokey::GetPasswordSafe;
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// let pws = device.get_password_safe("123456")?;
/// let name = pws.get_slot_name(0)?;
@@ -274,10 +273,10 @@ 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<String, CommandError> {
- unsafe { result_from_string(nitrokey_sys::NK_get_password_safe_slot_password(slot)) }
+ pub fn get_slot_password(&self, slot: u8) -> Result<String, Error> {
+ result_from_string(unsafe { nitrokey_sys::NK_get_password_safe_slot_password(slot) })
.and_then(get_pws_result)
}
@@ -292,9 +291,9 @@ impl<'a> PasswordSafe<'a> {
///
/// ```no_run
/// use nitrokey::GetPasswordSafe;
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// let pws = device.get_password_safe("123456")?;
/// let name = pws.get_slot_name(0)?;
@@ -305,26 +304,26 @@ 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,
name: &str,
login: &str,
password: &str,
- ) -> Result<(), CommandError> {
+ ) -> Result<(), Error> {
let name_string = get_cstring(name)?;
let login_string = get_cstring(login)?;
let password_string = get_cstring(password)?;
- unsafe {
- get_command_result(nitrokey_sys::NK_write_password_safe_slot(
+ get_command_result(unsafe {
+ nitrokey_sys::NK_write_password_safe_slot(
slot,
name_string.as_ptr(),
login_string.as_ptr(),
password_string.as_ptr(),
- ))
- }
+ )
+ })
}
/// Erases the given slot. Erasing clears the stored name, login and password (if the slot was
@@ -338,9 +337,9 @@ impl<'a> PasswordSafe<'a> {
///
/// ```no_run
/// use nitrokey::GetPasswordSafe;
- /// # use nitrokey::CommandError;
+ /// # use nitrokey::Error;
///
- /// # fn try_main() -> Result<(), CommandError> {
+ /// # fn try_main() -> Result<(), Error> {
/// let device = nitrokey::connect()?;
/// let pws = device.get_password_safe("123456")?;
/// match pws.erase_slot(0) {
@@ -351,9 +350,9 @@ impl<'a> PasswordSafe<'a> {
/// # }
/// ```
///
- /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot
- pub fn erase_slot(&self, slot: u8) -> Result<(), CommandError> {
- unsafe { get_command_result(nitrokey_sys::NK_erase_password_safe_slot(slot)) }
+ /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot
+ pub fn erase_slot(&self, slot: u8) -> Result<(), Error> {
+ get_command_result(unsafe { nitrokey_sys::NK_erase_password_safe_slot(slot) })
}
}
@@ -365,19 +364,19 @@ impl<'a> Drop for PasswordSafe<'a> {
}
impl GetPasswordSafe for Pro {
- fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe<'_>, CommandError> {
+ fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe<'_>, Error> {
get_password_safe(self, user_pin)
}
}
impl GetPasswordSafe for Storage {
- fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe<'_>, CommandError> {
+ fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe<'_>, Error> {
get_password_safe(self, user_pin)
}
}
impl GetPasswordSafe for DeviceWrapper {
- fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe<'_>, CommandError> {
+ fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe<'_>, Error> {
get_password_safe(self, user_pin)
}
}
diff --git a/nitrokey/src/util.rs b/nitrokey/src/util.rs
index 567c478..b7e8cd3 100644
--- a/nitrokey/src/util.rs
+++ b/nitrokey/src/util.rs
@@ -1,53 +1,14 @@
-use std::borrow;
+// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org>
+// SPDX-License-Identifier: MIT
+
use std::ffi::{CStr, CString};
-use std::fmt;
use std::os::raw::{c_char, c_int};
use libc::{c_void, free};
use rand_core::RngCore;
use rand_os::OsRng;
-/// Error types returned by Nitrokey device or by the library.
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub enum CommandError {
- /// A packet with a wrong checksum has been sent or received.
- WrongCrc,
- /// A command tried to access an OTP slot that does not exist.
- WrongSlot,
- /// A command tried to generate an OTP on a slot that is not configured.
- SlotNotProgrammed,
- /// The provided password is wrong.
- WrongPassword,
- /// You are not authorized for this command or provided a wrong temporary
- /// password.
- NotAuthorized,
- /// An error occurred when getting or setting the time.
- Timestamp,
- /// You did not provide a name for the OTP slot.
- NoName,
- /// This command is not supported by this device.
- NotSupported,
- /// This command is unknown.
- UnknownCommand,
- /// AES decryption failed.
- AesDecryptionFailed,
- /// An unknown error occurred.
- Unknown(i64),
- /// 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,
- /// An error occurred during random number generation.
- RngError,
-}
+use crate::error::{Error, LibraryError};
/// Log level for libnitrokey.
///
@@ -70,126 +31,56 @@ pub enum LogLevel {
DebugL2,
}
-pub fn owned_str_from_ptr(ptr: *const c_char) -> String {
- unsafe {
- return CStr::from_ptr(ptr).to_string_lossy().into_owned();
- }
+pub fn owned_str_from_ptr(ptr: *const c_char) -> Result<String, Error> {
+ unsafe { CStr::from_ptr(ptr) }
+ .to_str()
+ .map(String::from)
+ .map_err(Error::from)
}
-pub fn result_from_string(ptr: *const c_char) -> Result<String, CommandError> {
+pub fn result_from_string(ptr: *const c_char) -> Result<String, Error> {
if ptr.is_null() {
- return Err(CommandError::Undefined);
+ return Err(Error::UnexpectedError);
}
- 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() {
- get_last_result().map(|_| s)
- } else {
- Ok(s)
- }
+ 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.
+ if s.is_empty() {
+ get_last_result().map(|_| s)
+ } else {
+ Ok(s)
}
}
-pub fn get_command_result(value: c_int) -> Result<(), CommandError> {
- match value {
- 0 => Ok(()),
- other => Err(CommandError::from(other)),
+pub fn get_command_result(value: c_int) -> Result<(), Error> {
+ if value == 0 {
+ Ok(())
+ } else {
+ Err(Error::from(value))
}
}
-pub fn get_last_result() -> Result<(), CommandError> {
- let value = unsafe { nitrokey_sys::NK_get_last_command_status() } as c_int;
- get_command_result(value)
+pub fn get_last_result() -> Result<(), Error> {
+ get_command_result(unsafe { nitrokey_sys::NK_get_last_command_status() }.into())
}
-pub fn get_last_error() -> CommandError {
- return match get_last_result() {
- Ok(()) => CommandError::Undefined,
+pub fn get_last_error() -> Error {
+ match get_last_result() {
+ Ok(()) => Error::UnexpectedError,
Err(err) => err,
- };
+ }
}
-pub fn generate_password(length: usize) -> Result<Vec<u8>, CommandError> {
- let mut rng = OsRng::new()?;
+pub fn generate_password(length: usize) -> Result<Vec<u8>, Error> {
+ let mut rng = OsRng::new().map_err(|err| Error::RandError(Box::new(err)))?;
let mut data = vec![0u8; length];
rng.fill_bytes(&mut data[..]);
Ok(data)
}
-pub fn get_cstring<T: Into<Vec<u8>>>(s: T) -> Result<CString, CommandError> {
- CString::new(s).or(Err(CommandError::InvalidString))
-}
-
-impl CommandError {
- fn as_str(&self) -> borrow::Cow<'static, str> {
- match *self {
- CommandError::WrongCrc => {
- "A packet with a wrong checksum has been sent or received".into()
- }
- CommandError::WrongSlot => "The given OTP slot does not exist".into(),
- CommandError::SlotNotProgrammed => "The given OTP slot is not programmed".into(),
- CommandError::WrongPassword => "The given password is wrong".into(),
- CommandError::NotAuthorized => {
- "You are not authorized for this command or provided a wrong temporary \
- password"
- .into()
- }
- CommandError::Timestamp => "An error occurred when getting or setting the time".into(),
- CommandError::NoName => "You did not provide a name for the OTP slot".into(),
- 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(),
- 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(),
- CommandError::RngError => "An error occurred during random number generation".into(),
- }
- }
-}
-
-impl fmt::Display for CommandError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{}", self.as_str())
- }
-}
-
-impl From<c_int> for CommandError {
- fn from(value: 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()),
- }
- }
-}
-
-impl From<rand_core::Error> for CommandError {
- fn from(_error: rand_core::Error) -> Self {
- CommandError::RngError
- }
+pub fn get_cstring<T: Into<Vec<u8>>>(s: T) -> Result<CString, Error> {
+ CString::new(s).or_else(|_| Err(LibraryError::InvalidString.into()))
}
impl Into<i32> for LogLevel {
diff --git a/nitrokey/tests/device.rs b/nitrokey/tests/device.rs
index 849d2ff..c790049 100644
--- a/nitrokey/tests/device.rs
+++ b/nitrokey/tests/device.rs
@@ -1,3 +1,6 @@
+// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org>
+// SPDX-License-Identifier: MIT
+
mod util;
use std::ffi::CStr;
@@ -5,8 +8,8 @@ use std::process::Command;
use std::{thread, time};
use nitrokey::{
- Authenticate, CommandError, Config, ConfigureOtp, Device, GenerateOtp, GetPasswordSafe,
- 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 +34,20 @@ 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]
@@ -124,23 +136,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_eq!(Ok(()), admin.write_config(config));
- let get_config = admin.get_config().unwrap();
- assert_eq!(config, get_config);
+ assert_ok!((), admin.write_config(config));
+ assert_ok!(config, admin.get_config());
let config = Config::new(None, Some(9), None, true);
- assert_eq!(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_eq!(Ok(()), admin.write_config(config));
- let get_config = admin.get_config().unwrap();
- assert_eq!(config, get_config);
+ assert_ok!((), admin.write_config(config));
+ assert_ok!(config, admin.get_config());
let config = Config::new(None, None, None, false);
- assert_eq!(Ok(()), admin.write_config(config));
- let get_config = admin.get_config().unwrap();
- assert_eq!(config, get_config);
+ assert_ok!((), admin.write_config(config));
+ assert_ok!(config, admin.get_config());
}
#[test_device]
@@ -148,9 +158,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
@@ -159,11 +167,9 @@ 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)
- .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 +180,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!(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
@@ -184,14 +191,15 @@ 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)
);
- 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();
@@ -205,18 +213,19 @@ 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
}
#[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_eq!(
- Err(CommandError::WrongPassword),
+ assert_ok!((), device.unlock_user_pin(ADMIN_PASSWORD, USER_PASSWORD));
+ assert_cmd_err!(
+ CommandError::WrongPassword,
device.unlock_user_pin(USER_PASSWORD, USER_PASSWORD)
);
@@ -228,13 +237,11 @@ 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
- .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
@@ -244,57 +251,50 @@ 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
- .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]
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 +302,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 +327,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 +407,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);
@@ -441,8 +435,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,24 +448,23 @@ 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);
}
#[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);
@@ -479,18 +472,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/nitrokey/tests/lib.rs b/nitrokey/tests/lib.rs
index c92e224..697024d 100644
--- a/nitrokey/tests/lib.rs
+++ b/nitrokey/tests/lib.rs
@@ -1,6 +1,9 @@
+// Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org>
+// SPDX-License-Identifier: MIT
+
#[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);
diff --git a/nitrokey/tests/otp.rs b/nitrokey/tests/otp.rs
index 712f7a2..e424673 100644
--- a/nitrokey/tests/otp.rs
+++ b/nitrokey/tests/otp.rs
@@ -1,11 +1,14 @@
+// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org>
+// SPDX-License-Identifier: MIT
+
mod util;
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;
@@ -20,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)]
@@ -38,7 +41,7 @@ enum TotpTimestampSize {
fn make_admin_test_device<T>(device: T) -> Admin<T>
where
T: Device,
- (T, nitrokey::CommandError): Debug,
+ (T, nitrokey::Error): Debug,
{
device
.authenticate_admin(ADMIN_PASSWORD)
@@ -47,7 +50,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 +64,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,67 +90,64 @@ 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();
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]
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_lib_err!(LibraryError::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_lib_err!(
+ LibraryError::InvalidSlot,
admin.write_hotp_slot(slot_data, 0)
);
let slot_data = OtpSlotData::new(1, "test", "foobar", OtpMode::SixDigits);
- assert_eq!(
- Err(CommandError::InvalidHexString),
+ assert_lib_err!(
+ LibraryError::InvalidHexString,
admin.write_hotp_slot(slot_data, 0)
);
let code = admin.get_hotp_code(4);
- assert_eq!(CommandError::InvalidSlot, code.unwrap_err());
+ assert_lib_err!(LibraryError::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,25 +155,25 @@ 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) {
- for (i, &(base_time, code)) in TOTP_CODES.iter().enumerate() {
+ 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) {
continue;
}
- assert_eq!(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!((), device.set_time(time, true));
+ 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(", ")
);
}
}
@@ -186,7 +183,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 +201,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,13 +218,13 @@ 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();
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,68 +232,64 @@ 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();
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]
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());
+ assert_ok!("test-totp", result);
let result = device.get_totp_slot_name(16);
- assert_eq!(CommandError::InvalidSlot, result.unwrap_err());
+ assert_lib_err!(LibraryError::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_lib_err!(
+ LibraryError::InvalidSlot,
admin.write_totp_slot(slot_data, 0)
);
let slot_data = OtpSlotData::new(4, "test", "foobar", OtpMode::SixDigits);
- assert_eq!(
- Err(CommandError::InvalidHexString),
+ assert_lib_err!(
+ LibraryError::InvalidHexString,
admin.write_totp_slot(slot_data, 0)
);
let code = admin.get_totp_code(20);
- assert_eq!(CommandError::InvalidSlot, code.unwrap_err());
+ assert_lib_err!(LibraryError::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/nitrokey/tests/pws.rs b/nitrokey/tests/pws.rs
index fbcc0c1..df99e1c 100644
--- a/nitrokey/tests/pws.rs
+++ b/nitrokey/tests/pws.rs
@@ -1,18 +1,23 @@
+// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org>
+// SPDX-License-Identifier: MIT
+
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, LibraryError, 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<String, CommandError> {
+fn get_slot_name_direct(slot: u8) -> Result<String, Error> {
let ptr = unsafe { nitrokey_sys::NK_get_password_safe_slot_name(slot) };
if ptr.is_null() {
- return Err(CommandError::Undefined);
+ return Err(Error::UnexpectedError);
}
let s = unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() };
unsafe { free(ptr as *mut c_void) };
@@ -21,7 +26,7 @@ fn get_slot_name_direct(slot: u8) -> Result<String, CommandError> {
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(Error::from(other)),
}
}
false => Ok(s),
@@ -37,11 +42,15 @@ 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());
}
@@ -49,35 +58,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 +95,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_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_eq!(
- Err(CommandError::InvalidSlot),
+ assert_lib_err!(
+ LibraryError::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_lib_err!(LibraryError::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/nitrokey/tests/util/mod.rs b/nitrokey/tests/util/mod.rs
index cbf6b93..49ec13e 100644
--- a/nitrokey/tests/util/mod.rs
+++ b/nitrokey/tests/util/mod.rs
@@ -1,2 +1,86 @@
+// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org>
+// SPDX-License-Identifier: MIT
+
pub static ADMIN_PASSWORD: &str = "12345678";
pub static USER_PASSWORD: &str = "123456";
+
+#[macro_export]
+macro_rules! 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);
+ };
+}
+
+#[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) => {
+ assert_err!(::nitrokey::Error::LibraryError, $left, $right);
+ };
+}