aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--TODO.md1
-rw-r--r--src/lib.rs54
-rw-r--r--tests/device.rs47
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<S: Into<Vec<u8>>>(&mut self, path: S) -> Result<DeviceWrapper<'_>, 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<sync::MutexGuard<'static, Manager>, 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<sync::MutexGuard<'static, Manager>, 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);
- },
+ }
}
}
}
@@ -105,6 +105,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);
assert_empty_serial_number();