diff options
Diffstat (limited to 'nitrocli/src/nitrokey.rs')
-rw-r--r-- | nitrocli/src/nitrokey.rs | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/nitrocli/src/nitrokey.rs b/nitrocli/src/nitrokey.rs index 763bc4c..0b055fe 100644 --- a/nitrocli/src/nitrokey.rs +++ b/nitrocli/src/nitrokey.rs @@ -17,8 +17,157 @@ // * along with this program. If not, see <http://www.gnu.org/licenses/>. * // ************************************************************************* +use crc32::crc; +use std::cmp; +use std::mem; + // The Nitrokey Storage vendor ID. pub const VID: u16 = 0x20A0; // The Nitrokey Storage product ID. pub const PID: u16 = 0x4109; + + +#[derive(Debug)] +#[derive(PartialEq)] +#[repr(u8)] +pub enum Command { + // The command to enable the encrypted volume. + EnableEncryptedVolume = 0x20, + // The command to disable the encrypted volume. + DisableEncryptedVolume = 0x21, +} + + +/// A report is the entity we send to the Nitrokey Storage HID. +/// +/// A report is always 64 bytes in size. The last four bytes comprise a +/// CRC of the actual payload. Note that when sending or receiving a +/// report it usually is preceded by a one byte report ID. This report +/// ID is zero here and not represented in the actual report object in +/// our design. +#[repr(packed)] +pub struct Report<Payload> + where Payload: AsRef<[u8]>, +{ + // The actual payload data. A report may encapsulate a command to send + // to the stick or a response to receive from it. + pub data: Payload, + pub crc: u32, +} + + +impl<P> AsRef<[u8]> for Report<P> + where P: AsRef<[u8]>, +{ + fn as_ref(&self) -> &[u8] { + unsafe { return mem::transmute::<&Report<P>, &[u8; 64]>(self) }; + } +} + + +impl<P> From<P> for Report<P> + where P: AsRef<[u8]>, +{ + fn from(payload: P) -> Report<P> { + let crc = crc(payload.as_ref()); + return Report { + data: payload, + crc: crc, + }; + } +} + + +#[allow(dead_code)] +#[repr(packed)] +pub struct EnableEncryptedVolumeCommand { + command: Command, + // The kind of password. Unconditionally 'P' because the User PIN is + // used to enable the encrypted volume. + kind: u8, + // The password has a maximum length of twenty characters. + password: [u8; 20], + padding: [u8; 38], +} + + +impl EnableEncryptedVolumeCommand { + pub fn new(password: &Vec<u8>) -> EnableEncryptedVolumeCommand { + let mut report = EnableEncryptedVolumeCommand { + command: Command::EnableEncryptedVolume, + kind: 'P' as u8, + password: [0; 20], + padding: [0; 38], + }; + + debug_assert!(password.len() <= report.password.len()); + + let len = cmp::min(report.password.len(), password.len()); + report.password[..len].copy_from_slice(&password[..len]); + return report; + } +} + +impl AsRef<[u8]> for EnableEncryptedVolumeCommand { + fn as_ref(&self) -> &[u8] { + unsafe { return mem::transmute::<&EnableEncryptedVolumeCommand, &[u8; 60]>(self) }; + } +} + + +#[allow(dead_code)] +#[repr(packed)] +pub struct DisableEncryptedVolumeCommand { + command: Command, + padding: [u8; 59], +} + +impl DisableEncryptedVolumeCommand { + pub fn new() -> DisableEncryptedVolumeCommand { + return DisableEncryptedVolumeCommand { + command: Command::DisableEncryptedVolume, + padding: [0; 59], + }; + } +} + +impl AsRef<[u8]> for DisableEncryptedVolumeCommand { + fn as_ref(&self) -> &[u8] { + unsafe { return mem::transmute::<&DisableEncryptedVolumeCommand, &[u8; 60]>(self) }; + } +} + + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn encrypted_volume_report() { + let password = "test42".to_string().into_bytes(); + let report = EnableEncryptedVolumeCommand::new(&password); + let expected = ['t' as u8, 'e' as u8, 's' as u8, 't' as u8, '4' as u8, '2' as u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8]; + assert_eq!(report.password, expected); + } + + #[test] + #[cfg(debug)] + #[should_panic(expected = "assertion failed")] + fn overly_long_password() { + let password = "012345678912345678901".to_string().into_bytes(); + EnableEncryptedVolumeCommand::new(&password); + } + + #[test] + fn report_crc() { + let password = "passphrase".to_string().into_bytes(); + let payload = EnableEncryptedVolumeCommand::new(&password); + let report = Report::from(payload); + + // The expected checksum was computed using the original + // functionality. + assert_eq!(report.crc, 0xeeb583c); + } +} |