aboutsummaryrefslogtreecommitdiff
path: root/nitrocli/src/nitrokey.rs
diff options
context:
space:
mode:
Diffstat (limited to 'nitrocli/src/nitrokey.rs')
-rw-r--r--nitrocli/src/nitrokey.rs149
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);
+ }
+}