aboutsummaryrefslogtreecommitdiff
path: root/src/device
diff options
context:
space:
mode:
Diffstat (limited to 'src/device')
-rw-r--r--src/device/mod.rs69
1 files changed, 49 insertions, 20 deletions
diff --git a/src/device/mod.rs b/src/device/mod.rs
index 16d6b11..0234bf0 100644
--- a/src/device/mod.rs
+++ b/src/device/mod.rs
@@ -5,7 +5,6 @@ mod pro;
mod storage;
mod wrapper;
-use std::cmp;
use std::convert::{TryFrom, TryInto};
use std::ffi;
use std::fmt;
@@ -120,21 +119,38 @@ impl fmt::Display for DeviceInfo {
/// Parses a serial number returned by hidapi and transforms it to the Nitrokey format.
///
-/// If the serial number is all zero, this function returns `None`. Otherwise, all leading zeros
-/// (except the last eight characters) are stripped and `Some(_)` is returned. If the serial
-/// number has less than eight characters, leading zeros are added.
+/// If the serial number is all zero, this function returns `None`. Otherwise, it uses the last
+/// eight characters. If these are all zero, the first eight characters are used instead. This
+/// function also makes sure that the returned string is lowercase, consistent with libnitrokey’s
+/// hex string formatting.
+///
+/// The reason for this behavior is that the Nitrokey Storage does not report its serial number at
+/// all (all zero value), while the Nitrokey Pro with firmware 0.9 or later writes its serial
+/// number to the last eight characters. Nitrokey Pro devices with firmware 0.8 or earlier wrote
+/// their serial number to the first eight characters.
fn get_hidapi_serial_number(serial_number: &str) -> Option<String> {
- let mut iter = serial_number.char_indices().skip_while(|(_, c)| *c == '0');
- let first_non_null = iter.next();
+ let len = serial_number.len();
+ if len < 8 {
+ // The serial number in the USB descriptor has 12 bytes, we need at least four of them
+ return None;
+ }
+
+ let iter = serial_number.char_indices().rev();
+ let first_non_null = iter.skip_while(|(_, c)| *c == '0').next();
if let Some((i, _)) = first_non_null {
- // keep at least the last 8 characters
- let len = serial_number.len();
- let cut = cmp::min(len.saturating_sub(8), i);
- let (_, suffix) = serial_number.split_at(cut);
- // if necessary, add leading zeros to reach 8 characters
- let fill = 8usize.saturating_sub(len);
- Some("0".repeat(fill) + suffix)
+ if len - i < 8 {
+ // The last eight characters contain at least one non-zero character --> use them
+ let mut serial_number = serial_number.split_at(len - 8).1.to_string();
+ serial_number.make_ascii_lowercase();
+ Some(serial_number)
+ } else {
+ // The last eight characters are all zero --> use the first eight
+ let mut serial_number = serial_number.split_at(8).0.to_string();
+ serial_number.make_ascii_lowercase();
+ Some(serial_number)
+ }
} else {
+ // The serial number is all zero
None
}
}
@@ -615,10 +631,7 @@ mod tests {
fn hidapi_serial_number() {
assert_eq!(None, get_hidapi_serial_number(""));
assert_eq!(None, get_hidapi_serial_number("00000000000000000"));
- assert_eq!(
- Some("00001234".to_string()),
- get_hidapi_serial_number("1234")
- );
+ assert_eq!(None, get_hidapi_serial_number("1234"));
assert_eq!(
Some("00001234".to_string()),
get_hidapi_serial_number("00001234")
@@ -628,12 +641,28 @@ mod tests {
get_hidapi_serial_number("000000001234")
);
assert_eq!(
- Some("100000001234".to_string()),
+ Some("00001234".to_string()),
get_hidapi_serial_number("100000001234")
);
assert_eq!(
- Some("10000001234".to_string()),
- get_hidapi_serial_number("010000001234")
+ Some("12340000".to_string()),
+ get_hidapi_serial_number("123400000000")
+ );
+ assert_eq!(
+ Some("00005678".to_string()),
+ get_hidapi_serial_number("000000000000000000005678")
+ );
+ assert_eq!(
+ Some("00001234".to_string()),
+ get_hidapi_serial_number("000012340000000000000000")
+ );
+ assert_eq!(
+ Some("0000ffff".to_string()),
+ get_hidapi_serial_number("00000000000000000000FFFF")
+ );
+ assert_eq!(
+ Some("0000ffff".to_string()),
+ get_hidapi_serial_number("00000000000000000000ffff")
);
}
}