From 97c772724dd1fe395e7154e0d71c3b2408436082 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 29 Dec 2019 13:15:04 +0100 Subject: Add the connect_path function to the Manager struct This patch adds the connect_path function to the Manager struct that uses NK_connect_with_path to connect to a Nitrokey device at a given USB path. --- CHANGELOG.md | 1 + TODO.md | 1 - src/lib.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- tests/device.rs | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 98 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15c76b3..abdcab7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ SPDX-License-Identifier: CC0-1.0 - Add the `Error::UnsupportedDeviceError` variant. - Add the `DeviceInfo` struct. - Add the `list_devices` function. + - Add the `connect_path` function to the `Manager` struct. # v0.4.0 (2020-01-02) - Remove the `test-pro` and `test-storage` features. diff --git a/TODO.md b/TODO.md index e3708f9..eba14be 100644 --- a/TODO.md +++ b/TODO.md @@ -8,7 +8,6 @@ SPDX-License-Identifier: CC0-1.0 - `NK_get_SD_usage_data` - `NK_get_progress_bar_value` - `NK_get_status` -- waiting for [libnitrokey issue 166][] - - `NK_connect_with_path` - Clear passwords from memory. - Lock password safe in `PasswordSafe::drop()` (see [nitrokey-storage-firmware issue 65][]). diff --git a/src/lib.rs b/src/lib.rs index 5600d24..7e8b3ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,6 +16,10 @@ //! [`connect_model`][], [`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 +//! `Manager` struct. +//! //! You can call [`authenticate_user`][] or [`authenticate_admin`][] to get an authenticated device //! that can perform operations that require authentication. You can use [`device`][] to go back //! to the unauthenticated device. @@ -86,8 +90,10 @@ //! [`take`]: fn.take.html //! [`connect`]: struct.Manager.html#method.connect //! [`connect_model`]: struct.Manager.html#method.connect_model +//! [`connect_path`]: struct.Manager.html#method.connect_path //! [`connect_pro`]: struct.Manager.html#method.connect_pro //! [`connect_storage`]: struct.Manager.html#method.connect_storage +//! [`list_devices`]: fn.list_devices.html //! [`manager`]: trait.Device.html#method.manager //! [`device`]: struct.User.html#method.device //! [`get_hotp_code`]: trait.GenerateOtp.html#method.get_hotp_code @@ -128,7 +134,7 @@ pub use crate::otp::{ConfigureOtp, GenerateOtp, OtpMode, OtpSlotData}; pub use crate::pws::{GetPasswordSafe, PasswordSafe, SLOT_COUNT}; pub use crate::util::LogLevel; -use crate::util::get_last_result; +use crate::util::{get_cstring, get_last_result}; /// The default admin PIN for all Nitrokey devices. pub const DEFAULT_ADMIN_PIN: &str = "12345678"; @@ -296,6 +302,49 @@ impl Manager { } } + /// Connects to a Nitrokey device at the given USB path. + /// + /// To get a list of all connected Nitrokey devices, use the [`list_devices`][] function. The + /// [`DeviceInfo`][] structs returned by that function contain the USB path in the `path` + /// field. + /// + /// # Errors + /// + /// - [`InvalidString`][] if the USB path contains a null byte + /// - [`NotConnected`][] if no Nitrokey device can be found at the given USB path + /// - [`UnsupportedModelError`][] if the model of the Nitrokey device at the given USB path is + /// not supported by this crate + /// + /// # Example + /// + /// ``` + /// use nitrokey::DeviceWrapper; + /// + /// fn use_device(device: DeviceWrapper) {} + /// + /// let mut manager = nitrokey::take()?; + /// let devices = nitrokey::list_devices()?; + /// for device in devices { + /// let device = manager.connect_path(device.path)?; + /// use_device(device); + /// } + /// # Ok::<(), nitrokey::Error>(()) + /// ``` + /// + /// [`list_devices`]: fn.list_devices.html + /// [`DeviceInfo`]: struct.DeviceInfo.html + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString + /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected + /// [`UnsupportedModelError`]: enum.Error.html#variant.UnsupportedModelError + pub fn connect_path>>(&mut self, path: S) -> Result, Error> { + let path = get_cstring(path)?; + if unsafe { nitrokey_sys::NK_connect_with_path(path.as_ptr()) } == 1 { + device::get_connected_device(self) + } else { + Err(CommunicationError::NotConnected.into()) + } + } + /// Connects to a Nitrokey Pro. /// /// # Errors @@ -424,7 +473,7 @@ pub fn force_take() -> Result, Error> { /// /// This functions returns a vector with [`DeviceInfo`][] structs that contain information about /// all connected Nitrokey devices. It will even list unsupported models, although you cannot -/// connect to them. +/// connect to them. To connect to a supported model, call the [`connect_path`][] function. /// /// # Errors /// @@ -455,6 +504,7 @@ pub fn force_take() -> Result, Error> { /// # Ok::<(), nitrokey::Error>(()) /// ``` /// +/// [`connect_path`]: struct.Manager.html#fn.connect_path /// [`DeviceInfo`]: struct.DeviceInfo.html /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected /// [`Utf8Error`]: enum.Error.html#variant.Utf8Error diff --git a/tests/device.rs b/tests/device.rs index 5e60002..509763b 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -49,10 +49,10 @@ fn list_devices(_device: DeviceWrapper) { let serial_number = device.serial_number.unwrap(); assert!(!serial_number.is_empty()); assert_valid_serial_number(&serial_number); - }, + } nitrokey::Model::Storage => { assert_eq!(None, device.serial_number); - }, + } } } } @@ -104,6 +104,49 @@ fn assert_empty_serial_number() { } } +#[test_device] +fn connect_path_no_device() { + let mut manager = unwrap_ok!(nitrokey::take()); + + assert_cmu_err!(CommunicationError::NotConnected, manager.connect_path("")); + assert_cmu_err!( + CommunicationError::NotConnected, + manager.connect_path("foobar") + ); + // TODO: add realistic path +} + +#[test_device] +fn connect_path(device: DeviceWrapper) { + let manager = device.into_manager(); + + assert_cmu_err!(CommunicationError::NotConnected, manager.connect_path("")); + assert_cmu_err!( + CommunicationError::NotConnected, + manager.connect_path("foobar") + ); + // TODO: add realistic path + + let devices = unwrap_ok!(nitrokey::list_devices()); + assert!(!devices.is_empty()); + for device in devices { + let connected_device = unwrap_ok!(manager.connect_path(device.path)); + assert_eq!(device.model, Some(connected_device.get_model())); + match device.model.unwrap() { + nitrokey::Model::Pro => { + assert!(device.serial_number.is_some()); + assert_ok!( + device.serial_number.unwrap(), + connected_device.get_serial_number() + ); + } + nitrokey::Model::Storage => { + assert_eq!(None, device.serial_number); + } + } + } +} + #[test_device] fn disconnect(device: DeviceWrapper) { drop(device); -- cgit v1.2.3