M CHANGELOG.md => CHANGELOG.md +15 -0
@@ 10,6 10,21 @@ SPDX-License-Identifier: CC0-1.0
- Bump the MSRV to 1.40.0.
- Rename the `numlock`, `capslock`, `scrollock` fields of the `Config` struct
to `num_lock`, `caps_lock`, `scroll_lock`.
+- Update the `nitrokey-sys` dependency to v3.6.0.
+ - Use `NK_device_serial_number_as_u32` instead of `NK_device_serial_number`
+ in `Device::get_serial_number`.
+ - Use `NK_free_password_safe_slot_status` to free the pointer returned by
+ `NK_get_password_safe_slot_status` in `PasswordSafe::get_slot_status`.
+ - Use the `NK_config` struct for configuration handling.
+ - Use the derived `Default` implementation for the structs defined by
+ `nitrokey-sys`.
+- Add `get_struct` utility function for querying structs with libnitrokey
+ functions that use an output parameter.
+- Support the Librem Key model.
+ - Add the `Librem` variant to the `Model` and `DeviceWrapper` enums.
+ - Add the `Librem` struct.
+ - Change the `Display` implementation for the `Model` enum to print the
+ complete model name, i. e. Nitrokey Pro, Nitrokey Storage or Librem Key.
# v0.7.1 (2020-08-30)
- Remove the custom `std::error::Error::source` implementation for
M Cargo.toml => Cargo.toml +1 -1
@@ 19,7 19,7 @@ exclude = [".builds/*"]
[dependencies]
lazy_static = "1.2"
libc = "0.2"
-nitrokey-sys = "3.5"
+nitrokey-sys = "3.6"
rand_core = {version = "0.5.1", features = ["getrandom"] }
[dev-dependencies]
M README.md => README.md +8 -6
@@ 46,12 46,11 @@ supported by `nitrokey-rs`:
- `NK_is_AES_supported`. This function is no longer needed for Nitrokey
devices with a recent firmware version.
- `NK_send_startup`. Currently, this function is redundant to `NK_get_time`.
-- `NK_set_unencrypted_volume_rorw_pin_type_user`,
- `NK_set_unencrypted_read_only`, `NK_set_unencrypted_read_write`. These
- functions are only relevant for older firmware versions (pre-v0.51). As the
- Nitrokey Storage firmware can be updated easily, we do not support these
- outdated versions.
-- `NK_totp_get_time`, `NK_status`. These functions are deprecated.
+- `NK_set_unencrypted_volume_rorw_pin_type_user`: This function is only
+ relevant for older firmware versions (pre-v0.51). As the Nitrokey Storage
+ firmware can be updated easily, we do not support these outdated versions.
+- `NK_totp_get_time`, `NK_status`, `NK_set_unencrypted_read_only`,
+ `NK_set_unencrypted_read_write`. These functions are deprecated.
- `NK_read_HOTP_slot`. This function is only available for HOTP slots, not for
TOTP. We will support it once both types are supported by `libnitrokey`.
- All `*_as_string` functions that return string representations of data
@@ 77,6 76,9 @@ The test suite contains some test that take very long to execute, for example
filling the SD card of a Nitrokey Storage with random data. These tests are
ignored per default. Use `cargo test -- --ignored` to execute the tests.
+Currently, there are no tests specifically for the Librem Key as we first have
+to release a new `nitrokey-rs` version that `nitrokey-test` can use.
+
## Acknowledgments
Thanks to Nitrokey UG for providing two Nitrokey devices to support the
M src/auth.rs => src/auth.rs +24 -12
@@ 1,15 1,15 @@
// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org>
// SPDX-License-Identifier: MIT
-use std::convert::TryFrom as _;
+use std::convert::TryInto as _;
use std::ffi::CString;
use std::marker;
use std::ops;
use std::os::raw::c_char;
use std::os::raw::c_int;
-use crate::config::{Config, RawConfig};
-use crate::device::{Device, DeviceWrapper, Pro, Storage};
+use crate::config::Config;
+use crate::device::{Device, DeviceWrapper, Librem, 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};
@@ 304,16 304,8 @@ impl<'a, T: Device<'a>> Admin<'a, T> {
///
/// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot
pub fn write_config(&mut self, config: Config) -> Result<(), Error> {
- let raw_config = RawConfig::try_from(config)?;
get_command_result(unsafe {
- nitrokey_sys::NK_write_config(
- raw_config.num_lock,
- raw_config.caps_lock,
- raw_config.scroll_lock,
- raw_config.user_password,
- false,
- self.temp_password.as_ptr(),
- )
+ nitrokey_sys::NK_write_config_struct(config.try_into()?, self.temp_password.as_ptr())
})
}
}
@@ 379,6 371,9 @@ impl<'a, T: Device<'a>> AuthenticatedDevice<T> for Admin<'a, T> {
impl<'a> Authenticate<'a> for DeviceWrapper<'a> {
fn authenticate_user(self, password: &str) -> Result<User<'a, Self>, (Self, Error)> {
match self {
+ DeviceWrapper::Librem(librem) => {
+ authenticate_user_wrapper(librem, DeviceWrapper::Librem, password)
+ }
DeviceWrapper::Storage(storage) => {
authenticate_user_wrapper(storage, DeviceWrapper::Storage, password)
}
@@ 388,6 383,9 @@ impl<'a> Authenticate<'a> for DeviceWrapper<'a> {
fn authenticate_admin(self, password: &str) -> Result<Admin<'a, Self>, (Self, Error)> {
match self {
+ DeviceWrapper::Librem(librem) => {
+ authenticate_admin_wrapper(librem, DeviceWrapper::Librem, password)
+ }
DeviceWrapper::Storage(storage) => {
authenticate_admin_wrapper(storage, DeviceWrapper::Storage, password)
}
@@ 398,6 396,20 @@ impl<'a> Authenticate<'a> for DeviceWrapper<'a> {
}
}
+impl<'a> Authenticate<'a> for Librem<'a> {
+ fn authenticate_user(self, password: &str) -> Result<User<'a, 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<'a, Self>, (Self, Error)> {
+ authenticate(self, password, |password_ptr, temp_password_ptr| unsafe {
+ nitrokey_sys::NK_first_authenticate(password_ptr, temp_password_ptr)
+ })
+ }
+}
+
impl<'a> Authenticate<'a> for Pro<'a> {
fn authenticate_user(self, password: &str) -> Result<User<'a, Self>, (Self, Error)> {
authenticate(self, password, |password_ptr, temp_password_ptr| unsafe {
M src/config.rs => src/config.rs +33 -31
@@ 25,14 25,6 @@ pub struct Config {
pub user_password: bool,
}
-#[derive(Debug)]
-pub struct RawConfig {
- pub num_lock: u8,
- pub caps_lock: u8,
- pub scroll_lock: u8,
- pub user_password: bool,
-}
-
fn config_otp_slot_to_option(value: u8) -> Option<u8> {
if value < 3 {
Some(value)
@@ 68,39 60,49 @@ impl Config {
user_password,
}
}
+
+ fn from_raw(numlock: u8, capslock: u8, scrollock: u8, user_password: bool) -> Config {
+ Config {
+ num_lock: config_otp_slot_to_option(numlock),
+ caps_lock: config_otp_slot_to_option(capslock),
+ scroll_lock: config_otp_slot_to_option(scrollock),
+ user_password,
+ }
+ }
}
-impl convert::TryFrom<Config> for RawConfig {
+impl convert::TryFrom<Config> for nitrokey_sys::NK_config {
type Error = Error;
- fn try_from(config: Config) -> Result<RawConfig, Error> {
- Ok(RawConfig {
- num_lock: option_to_config_otp_slot(config.num_lock)?,
- caps_lock: option_to_config_otp_slot(config.caps_lock)?,
- scroll_lock: option_to_config_otp_slot(config.scroll_lock)?,
- user_password: config.user_password,
+ fn try_from(config: Config) -> Result<nitrokey_sys::NK_config, Error> {
+ Ok(nitrokey_sys::NK_config {
+ numlock: option_to_config_otp_slot(config.num_lock)?,
+ capslock: option_to_config_otp_slot(config.caps_lock)?,
+ scrolllock: option_to_config_otp_slot(config.scroll_lock)?,
+ enable_user_password: config.user_password,
+ disable_user_password: false,
})
}
}
-impl From<&nitrokey_sys::NK_status> for RawConfig {
- fn from(status: &nitrokey_sys::NK_status) -> Self {
- Self {
- num_lock: status.config_numlock,
- caps_lock: status.config_capslock,
- scroll_lock: status.config_scrolllock,
- user_password: status.otp_user_password,
- }
+impl From<nitrokey_sys::NK_config> for Config {
+ fn from(config: nitrokey_sys::NK_config) -> Config {
+ Config::from_raw(
+ config.numlock,
+ config.capslock,
+ config.scrolllock,
+ config.enable_user_password,
+ )
}
}
-impl Into<Config> for RawConfig {
- fn into(self) -> Config {
- Config {
- num_lock: config_otp_slot_to_option(self.num_lock),
- caps_lock: config_otp_slot_to_option(self.caps_lock),
- scroll_lock: config_otp_slot_to_option(self.scroll_lock),
- user_password: self.user_password,
- }
+impl From<&nitrokey_sys::NK_status> for Config {
+ fn from(status: &nitrokey_sys::NK_status) -> Config {
+ Config::from_raw(
+ status.config_numlock,
+ status.config_capslock,
+ status.config_scrolllock,
+ status.otp_user_password,
+ )
}
}
A src/device/librem.rs => src/device/librem.rs +83 -0
@@ 0,0 1,83 @@
+// Copyright (C) 2018-2020 Robin Krahl <robin.krahl@ireas.org>
+// SPDX-License-Identifier: MIT
+
+use crate::device::{Device, Model, Status};
+use crate::error::Error;
+use crate::otp::GenerateOtp;
+use crate::util::get_struct;
+
+/// A Librem Key device without user or admin authentication.
+///
+/// Use the [`connect`][] method to obtain an instance wrapper or the [`connect_librem`] method to
+/// directly obtain an instance. If you want to execute a command that requires user or admin
+/// authentication, use [`authenticate_admin`][] or [`authenticate_user`][].
+///
+/// # Examples
+///
+/// Authentication with error handling:
+///
+/// ```no_run
+/// use nitrokey::{Authenticate, User, Librem};
+/// # use nitrokey::Error;
+///
+/// fn perform_user_task<'a>(device: &User<'a, Librem<'a>>) {}
+/// fn perform_other_task(device: &Librem) {}
+///
+/// # fn try_main() -> Result<(), Error> {
+/// let mut manager = nitrokey::take()?;
+/// let device = manager.connect_librem()?;
+/// let device = match device.authenticate_user("123456") {
+/// Ok(user) => {
+/// perform_user_task(&user);
+/// user.device()
+/// },
+/// Err((device, err)) => {
+/// eprintln!("Could not authenticate as user: {}", err);
+/// device
+/// },
+/// };
+/// perform_other_task(&device);
+/// # Ok(())
+/// # }
+/// ```
+///
+/// [`authenticate_admin`]: trait.Authenticate.html#method.authenticate_admin
+/// [`authenticate_user`]: trait.Authenticate.html#method.authenticate_user
+/// [`connect`]: struct.Manager.html#method.connect
+/// [`connect_librem`]: struct.Manager.html#method.connect_librem
+#[derive(Debug)]
+pub struct Librem<'a> {
+ manager: Option<&'a mut crate::Manager>,
+}
+
+impl<'a> Librem<'a> {
+ pub(crate) fn new(manager: &'a mut crate::Manager) -> Librem<'a> {
+ Librem {
+ manager: Some(manager),
+ }
+ }
+}
+
+impl<'a> Drop for Librem<'a> {
+ fn drop(&mut self) {
+ unsafe {
+ nitrokey_sys::NK_logout();
+ }
+ }
+}
+
+impl<'a> Device<'a> for Librem<'a> {
+ fn into_manager(mut self) -> &'a mut crate::Manager {
+ self.manager.take().unwrap()
+ }
+
+ fn get_model(&self) -> Model {
+ Model::Librem
+ }
+
+ fn get_status(&self) -> Result<Status, Error> {
+ get_struct(|out| unsafe { nitrokey_sys::NK_get_status(out) })
+ }
+}
+
+impl<'a> GenerateOtp for Librem<'a> {}
M src/device/mod.rs => src/device/mod.rs +18 -21
@@ 1,6 1,7 @@
// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org>
// SPDX-License-Identifier: MIT
+mod librem;
mod pro;
mod storage;
mod wrapper;
@@ 11,14 12,15 @@ use std::fmt;
use std::str;
use crate::auth::Authenticate;
-use crate::config::{Config, RawConfig};
+use crate::config::Config;
use crate::error::{CommunicationError, Error, LibraryError};
use crate::otp::GenerateOtp;
use crate::pws::GetPasswordSafe;
use crate::util::{
- get_command_result, get_cstring, owned_str_from_ptr, result_or_error, run_with_string,
+ get_command_result, get_cstring, get_struct, owned_str_from_ptr, result_or_error,
};
+pub use librem::Librem;
pub use pro::Pro;
pub use storage::{
OperationStatus, SdCardData, Storage, StorageProductionInfo, StorageStatus, VolumeMode,
@@ 30,6 32,8 @@ pub use wrapper::DeviceWrapper;
#[derive(Clone, Copy, Debug, PartialEq)]
#[non_exhaustive]
pub enum Model {
+ /// The Librem Key.
+ Librem,
/// The Nitrokey Storage.
Storage,
/// The Nitrokey Pro.
@@ 39,8 43,9 @@ pub enum Model {
impl fmt::Display for Model {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match *self {
- Model::Pro => "Pro",
- Model::Storage => "Storage",
+ Model::Librem => "Librem Key",
+ Model::Pro => "Nitrokey Pro",
+ Model::Storage => "Nitrokey Storage",
})
}
}
@@ 48,6 53,7 @@ impl fmt::Display for Model {
impl From<Model> for nitrokey_sys::NK_device_model {
fn from(model: Model) -> Self {
match model {
+ Model::Librem => nitrokey_sys::NK_device_model_NK_LIBREM,
Model::Storage => nitrokey_sys::NK_device_model_NK_STORAGE,
Model::Pro => nitrokey_sys::NK_device_model_NK_PRO,
}
@@ 62,6 68,7 @@ impl TryFrom<nitrokey_sys::NK_device_model> for Model {
nitrokey_sys::NK_device_model_NK_DISCONNECTED => {
Err(CommunicationError::NotConnected.into())
}
+ nitrokey_sys::NK_device_model_NK_LIBREM => Ok(Model::Librem),
nitrokey_sys::NK_device_model_NK_PRO => Ok(Model::Pro),
nitrokey_sys::NK_device_model_NK_STORAGE => Ok(Model::Storage),
_ => Err(Error::UnsupportedModelError),
@@ 195,8 202,8 @@ impl TryFrom<&nitrokey_sys::NK_device_info> for DeviceInfo {
impl fmt::Display for DeviceInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.model {
- Some(model) => write!(f, "Nitrokey {}", model)?,
- None => write!(f, "Unsupported Nitrokey model")?,
+ Some(model) => f.write_str(&model.to_string())?,
+ None => f.write_str("Unsupported Nitrokey model")?,
}
write!(f, " at {} with ", self.path)?;
match self.serial_number {
@@ 274,7 281,7 @@ impl From<nitrokey_sys::NK_status> for Status {
minor: status.firmware_version_minor,
},
serial_number: SerialNumber::new(status.serial_number_smart_card),
- config: RawConfig::from(&status).into(),
+ config: Config::from(&status),
}
}
}
@@ 375,9 382,8 @@ pub trait Device<'a>: Authenticate<'a> + GetPasswordSafe<'a> + GenerateOtp + fmt
/// # }
/// ```
fn get_serial_number(&self) -> Result<SerialNumber, Error> {
- run_with_string(unsafe { nitrokey_sys::NK_device_serial_number() }, |s| {
- s.parse()
- })
+ let serial_number = unsafe { nitrokey_sys::NK_device_serial_number_as_u32() };
+ result_or_error(SerialNumber::new(serial_number))
}
/// Returns the number of remaining authentication attempts for the user. The total number of
@@ 472,17 478,7 @@ pub trait Device<'a>: Authenticate<'a> + GetPasswordSafe<'a> + GenerateOtp + fmt
/// # }
/// ```
fn get_config(&self) -> Result<Config, Error> {
- let mut raw_status = nitrokey_sys::NK_status {
- firmware_version_major: 0,
- firmware_version_minor: 0,
- serial_number_smart_card: 0,
- config_numlock: 0,
- config_capslock: 0,
- config_scrolllock: 0,
- otp_user_password: false,
- };
- get_command_result(unsafe { nitrokey_sys::NK_get_status(&mut raw_status) })?;
- Ok(RawConfig::from(&raw_status).into())
+ get_struct(|out| unsafe { nitrokey_sys::NK_read_config_struct(out) })
}
/// Changes the administrator PIN.
@@ 699,6 695,7 @@ pub(crate) fn create_device_wrapper(
model: Model,
) -> DeviceWrapper<'_> {
match model {
+ Model::Librem => Librem::new(manager).into(),
Model::Pro => Pro::new(manager).into(),
Model::Storage => Storage::new(manager).into(),
}
M src/device/pro.rs => src/device/pro.rs +2 -12
@@ 4,7 4,7 @@
use crate::device::{Device, Model, Status};
use crate::error::Error;
use crate::otp::GenerateOtp;
-use crate::util::get_command_result;
+use crate::util::get_struct;
/// A Nitrokey Pro device without user or admin authentication.
///
@@ 76,17 76,7 @@ impl<'a> Device<'a> for Pro<'a> {
}
fn get_status(&self) -> Result<Status, Error> {
- let mut raw_status = nitrokey_sys::NK_status {
- firmware_version_major: 0,
- firmware_version_minor: 0,
- serial_number_smart_card: 0,
- config_numlock: 0,
- config_capslock: 0,
- config_scrolllock: 0,
- otp_user_password: false,
- };
- get_command_result(unsafe { nitrokey_sys::NK_get_status(&mut raw_status) })?;
- Ok(raw_status.into())
+ get_struct(|out| unsafe { nitrokey_sys::NK_get_status(out) })
}
}
M src/device/storage.rs => src/device/storage.rs +5 -53
@@ 8,7 8,7 @@ use std::ops;
use crate::device::{Device, FirmwareVersion, Model, SerialNumber, Status};
use crate::error::{CommandError, Error};
use crate::otp::GenerateOtp;
-use crate::util::{get_command_result, get_cstring, get_last_error};
+use crate::util::{get_command_result, get_cstring, get_last_error, get_struct};
/// A Nitrokey Storage device without user or admin authentication.
///
@@ 549,26 549,7 @@ impl<'a> Storage<'a> {
/// # }
/// ```
pub fn get_storage_status(&self) -> Result<StorageStatus, Error> {
- let mut raw_status = nitrokey_sys::NK_storage_status {
- unencrypted_volume_read_only: false,
- unencrypted_volume_active: false,
- encrypted_volume_read_only: false,
- encrypted_volume_active: false,
- hidden_volume_read_only: false,
- hidden_volume_active: false,
- firmware_version_major: 0,
- firmware_version_minor: 0,
- firmware_locked: false,
- serial_number_sd_card: 0,
- serial_number_smart_card: 0,
- user_retry_count: 0,
- admin_retry_count: 0,
- new_sd_card_found: false,
- filled_with_random: false,
- stick_initialized: false,
- };
- let raw_result = unsafe { nitrokey_sys::NK_get_status_storage(&mut raw_status) };
- get_command_result(raw_result).map(|_| StorageStatus::from(raw_status))
+ get_struct(|out| unsafe { nitrokey_sys::NK_get_status_storage(out) })
}
/// Returns the production information for the connected storage device.
@@ 594,23 575,7 @@ impl<'a> Storage<'a> {
/// # }
/// ```
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,
- SD_Card_Size_u8: 0,
- CPU_CardID_u32: 0,
- SmartCardID_u32: 0,
- SD_CardID_u32: 0,
- SC_UserPwRetryCount: 0,
- SC_AdminPwRetryCount: 0,
- SD_Card_ManufacturingYear_u8: 0,
- SD_Card_ManufacturingMonth_u8: 0,
- SD_Card_OEM_u16: 0,
- SD_WriteSpeed_u16: 0,
- SD_Card_Manufacturer_u8: 0,
- };
- let raw_result = unsafe { nitrokey_sys::NK_get_storage_production_info(&mut raw_data) };
- get_command_result(raw_result).map(|_| StorageProductionInfo::from(raw_data))
+ get_struct(|out| unsafe { nitrokey_sys::NK_get_storage_production_info(out) })
}
/// Clears the warning for a new SD card.
@@ 666,10 631,7 @@ impl<'a> Storage<'a> {
/// # Ok::<(), nitrokey::Error>(())
/// ```
pub fn get_sd_card_usage(&self) -> Result<ops::Range<u8>, Error> {
- let mut usage_data = nitrokey_sys::NK_SD_usage_data {
- write_level_min: 0,
- write_level_max: 0,
- };
+ let mut usage_data = nitrokey_sys::NK_SD_usage_data::default();
let result = unsafe { nitrokey_sys::NK_get_SD_usage_data(&mut usage_data) };
match get_command_result(result) {
Ok(_) => {
@@ 811,17 773,7 @@ impl<'a> Device<'a> for Storage<'a> {
// [0] https://github.com/Nitrokey/nitrokey-storage-firmware/issues/96
// [1] https://github.com/Nitrokey/libnitrokey/issues/166
- let mut raw_status = nitrokey_sys::NK_status {
- firmware_version_major: 0,
- firmware_version_minor: 0,
- serial_number_smart_card: 0,
- config_numlock: 0,
- config_capslock: 0,
- config_scrolllock: 0,
- otp_user_password: false,
- };
- get_command_result(unsafe { nitrokey_sys::NK_get_status(&mut raw_status) })?;
- let mut status = Status::from(raw_status);
+ let mut status: Status = get_struct(|out| unsafe { nitrokey_sys::NK_get_status(out) })?;
let storage_status = self.get_storage_status()?;
status.firmware_version = storage_status.firmware_version;
M src/device/wrapper.rs => src/device/wrapper.rs +14 -1
@@ 1,7 1,7 @@
// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org>
// SPDX-License-Identifier: MIT
-use crate::device::{Device, Model, Pro, Status, Storage};
+use crate::device::{Device, Librem, Model, Pro, Status, Storage};
use crate::error::Error;
use crate::otp::GenerateOtp;
@@ 66,6 66,8 @@ use crate::otp::GenerateOtp;
#[derive(Debug)]
#[non_exhaustive]
pub enum DeviceWrapper<'a> {
+ /// A Librem Key device.
+ Librem(Librem<'a>),
/// A Nitrokey Storage device.
Storage(Storage<'a>),
/// A Nitrokey Pro device.
@@ 75,6 77,7 @@ pub enum DeviceWrapper<'a> {
impl<'a> DeviceWrapper<'a> {
fn device(&self) -> &dyn Device<'a> {
match *self {
+ DeviceWrapper::Librem(ref librem) => librem,
DeviceWrapper::Storage(ref storage) => storage,
DeviceWrapper::Pro(ref pro) => pro,
}
@@ 82,12 85,19 @@ impl<'a> DeviceWrapper<'a> {
fn device_mut(&mut self) -> &mut dyn Device<'a> {
match *self {
+ DeviceWrapper::Librem(ref mut librem) => librem,
DeviceWrapper::Storage(ref mut storage) => storage,
DeviceWrapper::Pro(ref mut pro) => pro,
}
}
}
+impl<'a> From<Librem<'a>> for DeviceWrapper<'a> {
+ fn from(device: Librem<'a>) -> Self {
+ DeviceWrapper::Librem(device)
+ }
+}
+
impl<'a> From<Pro<'a>> for DeviceWrapper<'a> {
fn from(device: Pro<'a>) -> Self {
DeviceWrapper::Pro(device)
@@ 121,6 131,7 @@ impl<'a> GenerateOtp for DeviceWrapper<'a> {
impl<'a> Device<'a> for DeviceWrapper<'a> {
fn into_manager(self) -> &'a mut crate::Manager {
match self {
+ DeviceWrapper::Librem(dev) => dev.into_manager(),
DeviceWrapper::Pro(dev) => dev.into_manager(),
DeviceWrapper::Storage(dev) => dev.into_manager(),
}
@@ 128,6 139,7 @@ impl<'a> Device<'a> for DeviceWrapper<'a> {
fn get_model(&self) -> Model {
match *self {
+ DeviceWrapper::Librem(_) => Model::Librem,
DeviceWrapper::Pro(_) => Model::Pro,
DeviceWrapper::Storage(_) => Model::Storage,
}
@@ 135,6 147,7 @@ impl<'a> Device<'a> for DeviceWrapper<'a> {
fn get_status(&self) -> Result<Status, Error> {
match self {
+ DeviceWrapper::Librem(dev) => dev.get_status(),
DeviceWrapper::Pro(dev) => dev.get_status(),
DeviceWrapper::Storage(dev) => dev.get_status(),
}
M src/lib.rs => src/lib.rs +39 -8
@@ 13,8 13,8 @@
//! an reference to the [`Manager`][] singleton that keeps track of the connections. Then use the
//! [`connect`][] method to connect to any Nitrokey device. The method will return a
//! [`DeviceWrapper`][] that abstracts over the supported Nitrokey devices. You can also use
-//! [`connect_model`][], [`connect_pro`][] or [`connect_storage`][] to connect to a specific
-//! device.
+//! [`connect_model`][], [`connect_librem`][], [`connect_pro`][] or [`connect_storage`][] to
+//! connect to a specific device.
//!
//! To get a list of all connected Nitrokey devices, use the [`list_devices`][] function. You can
//! then connect to one of the connected devices using the [`connect_path`][] function of the
@@ 98,6 98,7 @@
//! [`authenticate_user`]: trait.Authenticate.html#method.authenticate_user
//! [`take`]: fn.take.html
//! [`connect`]: struct.Manager.html#method.connect
+//! [`connect_librem`]: struct.Manager.html#method.connect_librem
//! [`connect_model`]: struct.Manager.html#method.connect_model
//! [`connect_path`]: struct.Manager.html#method.connect_path
//! [`connect_pro`]: struct.Manager.html#method.connect_pro
@@ 136,8 137,9 @@ use std::sync;
pub use crate::auth::{Admin, Authenticate, User};
pub use crate::config::Config;
pub use crate::device::{
- Device, DeviceInfo, DeviceWrapper, FirmwareVersion, Model, OperationStatus, Pro, SdCardData,
- SerialNumber, Status, Storage, StorageProductionInfo, StorageStatus, VolumeMode, VolumeStatus,
+ Device, DeviceInfo, DeviceWrapper, FirmwareVersion, Librem, Model, OperationStatus, Pro,
+ SdCardData, SerialNumber, Status, Storage, StorageProductionInfo, StorageStatus, VolumeMode,
+ VolumeStatus,
};
pub use crate::error::{CommandError, CommunicationError, Error, LibraryError};
pub use crate::otp::{ConfigureOtp, GenerateOtp, OtpMode, OtpSlotData};
@@ 192,8 194,8 @@ impl fmt::Display for Version {
/// time.
///
/// To obtain a reference to an instance of this manager, use the [`take`][] function. Use one of
-/// the connect methods – [`connect`][], [`connect_model`][], [`connect_pro`][] or
-/// [`connect_storage`][] – to retrieve a [`Device`][] instance.
+/// the connect methods – [`connect`][], [`connect_model`][], [`connect_librem`][],
+/// [`connect_pro`][] or [`connect_storage`][] – to retrieve a [`Device`][] instance.
///
/// # Examples
///
@@ 229,6 231,7 @@ impl fmt::Display for Version {
/// ```
///
/// [`connect`]: #method.connect
+/// [`connect_librem`]: #method.connect_librem
/// [`connect_model`]: #method.connect_model
/// [`connect_pro`]: #method.connect_pro
/// [`connect_storage`]: #method.connect_storage
@@ 249,8 252,7 @@ impl Manager {
/// Connects to a Nitrokey device.
///
- /// This method can be used to connect to any connected device, both a Nitrokey Pro and a
- /// Nitrokey Storage.
+ /// This method can be used to connect to any connected device.
///
/// # Errors
///
@@ 355,6 357,35 @@ impl Manager {
}
}
+ /// Connects to a Librem Key.
+ ///
+ /// # Errors
+ ///
+ /// - [`NotConnected`][] if no Nitrokey device of the given model is connected
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use nitrokey::Librem;
+ ///
+ /// fn use_librem(device: Librem) {}
+ ///
+ /// match nitrokey::take()?.connect_librem() {
+ /// Ok(device) => use_librem(device),
+ /// Err(err) => println!("Could not connect to the Librem Key: {}", err),
+ /// }
+ /// # Ok::<(), nitrokey::Error>(())
+ /// ```
+ ///
+ /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected
+ pub fn connect_librem(&mut self) -> Result<Librem<'_>, Error> {
+ if device::connect_enum(device::Model::Librem) {
+ Ok(device::Librem::new(self))
+ } else {
+ Err(CommunicationError::NotConnected.into())
+ }
+ }
+
/// Connects to a Nitrokey Pro.
///
/// # Errors
M src/pws.rs => src/pws.rs +8 -2
@@ 1,7 1,7 @@
// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org>
// SPDX-License-Identifier: MIT
-use crate::device::{Device, DeviceWrapper, Pro, Storage};
+use crate::device::{Device, DeviceWrapper, Librem, Pro, Storage};
use crate::error::{CommandError, Error};
use crate::util::{get_command_result, get_cstring, get_last_error, result_from_string};
@@ 173,7 173,7 @@ impl<'a, 'b> PasswordSafe<'a, 'b> {
result[i as usize] = status_array[i as usize] == 1;
}
unsafe {
- libc::free(status_ptr as *mut libc::c_void);
+ nitrokey_sys::NK_free_password_safe_slot_status(status_ptr);
}
Ok(result)
}
@@ 369,6 369,12 @@ impl<'a, 'b> Drop for PasswordSafe<'a, 'b> {
}
}
+impl<'a> GetPasswordSafe<'a> for Librem<'a> {
+ fn get_password_safe(&mut self, user_pin: &str) -> Result<PasswordSafe<'_, 'a>, Error> {
+ get_password_safe(self, user_pin)
+ }
+}
+
impl<'a> GetPasswordSafe<'a> for Pro<'a> {
fn get_password_safe(&mut self, user_pin: &str) -> Result<PasswordSafe<'_, 'a>, Error> {
get_password_safe(self, user_pin)
M src/util.rs => src/util.rs +11 -0
@@ 68,6 68,17 @@ pub fn result_or_error<T>(value: T) -> Result<T, Error> {
get_last_result().and(Ok(value))
}
+pub fn get_struct<R, T, F>(f: F) -> Result<R, Error>
+where
+ R: From<T>,
+ T: Default,
+ F: Fn(&mut T) -> c_int,
+{
+ let mut out = T::default();
+ get_command_result(f(&mut out))?;
+ Ok(out.into())
+}
+
pub fn get_command_result(value: c_int) -> Result<(), Error> {
if value == 0 {
Ok(())