diff options
| -rw-r--r-- | CHANGELOG.md | 1 | ||||
| -rw-r--r-- | TODO.md | 1 | ||||
| -rw-r--r-- | src/lib.rs | 54 | ||||
| -rw-r--r-- | 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. @@ -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][]). @@ -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(); | 
