From 7f3cc787d03423aa982ade0fa054bb79be18f6ae Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Mon, 18 Feb 2019 14:23:10 +0000 Subject: Implement Nitrokey command handling This patch changes the Get_Report handler in Nitrokey to extract the command data from the buffer sent with the Set_Report request, to execute a command based on this data and to write appropriate return data back to the device. --- src/device.rs | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/src/device.rs b/src/device.rs index f7c4fc7..3b6cf46 100644 --- a/src/device.rs +++ b/src/device.rs @@ -5,6 +5,7 @@ use usb_device::bus::{UsbBus, UsbBusAllocator}; use usb_device::device::{UsbDevice, UsbDeviceBuilder, UsbVidPid}; use crate::hid::{HidDevice, Protocol, ReportType, Subclass}; +use crate::util::TryFrom; const VID_CLAY_LOGIC: u16 = 0x20a0; const PID_NITROKEY_PRO: u16 = 0x4108; @@ -18,6 +19,40 @@ const REPORT_DESCRIPTOR: &[u8] = &[ 0x75, 0x08, 0x95, 0x40, 0xB1, 0x02, 0xC0, ]; +enum_u8! { + #[derive(Clone, Copy, Debug, PartialEq)] + pub enum DeviceStatus { + Ok = 0, + Busy = 1, + Error = 2, + ReceivedReport = 3, + } +} + +enum_u8! { + #[derive(Clone, Copy, Debug, PartialEq)] + pub enum CommandStatus { + Ok = 0, + WrongCrc = 1, + WrongSlot = 2, + SlotNotProgrammed = 3, + WrongPassword = 4, + NotAuthorized = 5, + TimestampWarning = 6, + NoNameError = 7, + NotSupported = 8, + UnknownCommand = 9, + AesDecryptionFailed = 10, + } +} + +enum_u8! { + #[derive(Clone, Copy, Debug, PartialEq)] + pub enum CommandId { + GetStatus = 0, + } +} + pub struct Nitrokey { buf: [u8; REPORT_LEN], } @@ -28,6 +63,15 @@ impl Nitrokey { buf: [0; REPORT_LEN], } } + + fn execute_command( + &self, + command_id: CommandId, + data: &[u8], + ) -> Result<[u8; 53], CommandStatus> { + let _ = (command_id, data); + Err(CommandStatus::UnknownCommand) + } } impl HidDevice for Nitrokey { @@ -44,8 +88,38 @@ impl HidDevice for Nitrokey { } fn get_report(&mut self, report_type: ReportType, report_id: u8) -> Result<&[u8], ()> { - let _ = (report_type, report_id); - Err(()) + if report_type != ReportType::Feature || report_id != 0 { + return Err(()); + } + + let device_status = DeviceStatus::Ok; + let command_id = self.buf[0]; + let result = if let Ok(command_id) = CommandId::try_from(command_id) { + self.execute_command(command_id, &self.buf[1..60]) + } else { + Err(CommandStatus::UnknownCommand) + }; + let (command_status, command_data) = result + .map(|d| (CommandStatus::Ok, d)) + .unwrap_or_else(|status| (status, [0; 53])); + + let pre_data: [u8; 7] = [ + device_status.into(), // device status + command_id, // command_id + self.buf[60], + self.buf[61], + self.buf[62], + self.buf[63], // last_command_crc + command_status.into(), // last_command_status + ]; + let post_data: [u8; 4] = [ + 0x00, 0x00, 0x00, 0x01, // crc -- libnitrokey accepts any non-zero value + ]; + + self.buf[0..7].copy_from_slice(&pre_data); + self.buf[7..7 + command_data.len()].copy_from_slice(&command_data); + self.buf[60..64].copy_from_slice(&post_data); + Ok(&self.buf) } fn set_report( -- cgit v1.2.1