aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Krahl <robin.krahl@ireas.org>2019-02-18 21:36:31 +0000
committerRobin Krahl <robin.krahl@ireas.org>2019-02-18 23:27:48 +0100
commit9df0f3bd565dfbf8c97d02969a17504c688bf381 (patch)
treeda3a25d4900fc82a407ac729f5642f1e65cd0fbc
parent49703cd770fd0522beae2dc53d1f882c9143c64e (diff)
downloadntw-9df0f3bd565dfbf8c97d02969a17504c688bf381.tar.gz
ntw-9df0f3bd565dfbf8c97d02969a17504c688bf381.tar.bz2
Refactor command execution into commands module
This patch refactors the command execution. A command is represented by a struct implementing the Command trait. The enum_cmd macro is used to generate a mapping from the CommandId enum to a Command instance and to execute the command. The request and response data is manually converted from and to raw byte slices. As we do not have a standard library, we cannot create a Box<Command> from a CommandId. Instead, we directly delegate the execute method to the corresponding Command.
-rw-r--r--src/commands.rs148
-rw-r--r--src/device.rs49
-rw-r--r--src/main.rs1
-rw-r--r--src/util.rs29
4 files changed, 180 insertions, 47 deletions
diff --git a/src/commands.rs b/src/commands.rs
new file mode 100644
index 0000000..62e2983
--- /dev/null
+++ b/src/commands.rs
@@ -0,0 +1,148 @@
+// Copyright 2019 Robin Krahl <robin.krahl@ireas.org>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+use core::default::Default;
+use core::marker::Sized;
+
+use crate::device::CommandStatus;
+
+pub const COMMAND_LEN: usize = 53;
+
+enum_cmd! {
+ #[derive(Clone, Copy, Debug, PartialEq)]
+ pub enum CommandId {
+ GetStatus(GetStatusCommand) = 0,
+ ReadSlotName(ReadSlotNameCommand) = 2,
+ }
+}
+
+trait RequestData: Sized {
+ fn from_bytes(data: &[u8]) -> Option<Self>;
+}
+
+impl RequestData for () {
+ fn from_bytes(_data: &[u8]) -> Option<Self> {
+ Some(())
+ }
+}
+
+trait ResponseData {
+ fn write_bytes(&self, buf: &mut [u8]);
+}
+
+trait Command {
+ type Request: RequestData;
+ type Response: ResponseData;
+
+ fn execute(data: Self::Request) -> Result<Self::Response, CommandStatus>;
+
+ fn execute_raw(data: &[u8], buf: &mut [u8]) -> CommandStatus {
+ if let Some(request) = Self::Request::from_bytes(data) {
+ match Self::execute(request) {
+ Ok(response) => {
+ response.write_bytes(buf);
+ CommandStatus::Ok
+ }
+ Err(status) => status,
+ }
+ } else {
+ CommandStatus::NotSupported
+ }
+ }
+}
+
+#[derive(Debug, Default)]
+struct GetStatusResponse {
+ firmware_version_minor: u8,
+ firmware_version_major: u8,
+ card_serial: u32,
+ config_numlock: u8,
+ config_capslock: u8,
+ config_enable_user_password: u8,
+ config_delete_user_password: u8,
+ buf: [u8; 10],
+}
+
+impl ResponseData for GetStatusResponse {
+ fn write_bytes(&self, buf: &mut [u8]) {
+ let card_serial = self.card_serial.to_le_bytes();
+ let data = &[
+ self.firmware_version_minor,
+ self.firmware_version_major,
+ card_serial[0],
+ card_serial[1],
+ card_serial[2],
+ card_serial[3],
+ self.config_numlock,
+ self.config_capslock,
+ self.config_enable_user_password,
+ self.config_delete_user_password,
+ ];
+ assert!(buf.len() >= data.len());
+ buf[..data.len()].copy_from_slice(data);
+ }
+}
+
+#[derive(Debug, Default)]
+struct GetStatusCommand {}
+
+impl Command for GetStatusCommand {
+ type Request = ();
+ type Response = GetStatusResponse;
+
+ fn execute(_data: Self::Request) -> Result<Self::Response, CommandStatus> {
+ let mut response: Self::Response = Default::default();
+ response.firmware_version_minor = 1;
+ Ok(response)
+ }
+}
+
+#[derive(Debug, Default)]
+struct ReadSlotNameRequest {
+ internal_slot_number: u8,
+}
+
+impl RequestData for ReadSlotNameRequest {
+ fn from_bytes(data: &[u8]) -> Option<Self> {
+ if data.is_empty() {
+ None
+ } else {
+ Some(ReadSlotNameRequest {
+ internal_slot_number: data[0],
+ })
+ }
+ }
+}
+
+#[derive(Debug, Default)]
+struct ReadSlotNameResponse {
+ slot_name: [u8; 15],
+}
+
+impl ResponseData for ReadSlotNameResponse {
+ fn write_bytes(&self, buf: &mut [u8]) {
+ assert!(buf.len() >= self.slot_name.len());
+ buf[..self.slot_name.len()].copy_from_slice(&self.slot_name)
+ }
+}
+
+#[derive(Debug, Default)]
+struct ReadSlotNameCommand {}
+
+impl Command for ReadSlotNameCommand {
+ type Request = ReadSlotNameRequest;
+ type Response = ReadSlotNameResponse;
+
+ fn execute(data: Self::Request) -> Result<Self::Response, CommandStatus> {
+ if data.internal_slot_number != 0x20 {
+ Err(CommandStatus::SlotNotProgrammed)
+ } else {
+ let mut response: Self::Response = Default::default();
+ response.slot_name[0] = 0x74; // t
+ response.slot_name[1] = 0x65; // e
+ response.slot_name[2] = 0x73; // s
+ response.slot_name[3] = 0x74; // t
+ Ok(response)
+ }
+ }
+}
diff --git a/src/device.rs b/src/device.rs
index a6bf9bf..300ce56 100644
--- a/src/device.rs
+++ b/src/device.rs
@@ -4,13 +4,13 @@
use usb_device::bus::{UsbBus, UsbBusAllocator};
use usb_device::device::{UsbDevice, UsbDeviceBuilder, UsbVidPid};
+use crate::commands::{CommandId, COMMAND_LEN};
use crate::hid::{HidDevice, Protocol, ReportType, Subclass};
use crate::util::TryFrom;
const VID_CLAY_LOGIC: u16 = 0x20a0;
const PID_NITROKEY_PRO: u16 = 0x4108;
-const COMMAND_LEN: usize = 53;
const REPORT_LEN: usize = 64;
const REPORT_DESCRIPTOR: &[u8] = &[
0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x05, 0x07, 0x19, 0xE0, 0x29, 0xE7, 0x15, 0x00, 0x25, 0x01,
@@ -47,14 +47,6 @@ enum_u8! {
}
}
-enum_u8! {
- #[derive(Clone, Copy, Debug, PartialEq)]
- pub enum CommandId {
- GetStatus = 0,
- ReadSlotName = 2,
- }
-}
-
pub struct Nitrokey {
buf: [u8; REPORT_LEN],
}
@@ -65,43 +57,6 @@ impl Nitrokey {
buf: [0; REPORT_LEN],
}
}
-
- fn execute_command(&self, command_id: CommandId, data: &[u8], buf: &mut [u8]) -> CommandStatus {
- let data: &[u8] = match command_id {
- CommandId::GetStatus => {
- &[
- 1, // firmware_version_st.minor
- 0, // firmware_version_st.major
- 0, // card_serial[0]
- 0, // card_serial[1]
- 0, // card_serial[2]
- 0, // card_serial[3]
- 0, // numlock
- 0, // capslock
- 0, // enable_user_password
- 0, // delete_user_password
- ]
- }
- CommandId::ReadSlotName => {
- assert!(data.len() > 1);
- let slot_number = data[0];
- if slot_number != 0x20 {
- return CommandStatus::SlotNotProgrammed;
- }
-
- &[
- 0x74, // t
- 0x65, // e
- 0x73, // s
- 0x74, // t
- 0x00, // NULL
- ]
- }
- };
- assert!(buf.len() >= data.len());
- buf[..data.len()].copy_from_slice(data);
- CommandStatus::Ok
- }
}
impl HidDevice for Nitrokey {
@@ -131,7 +86,7 @@ impl HidDevice for Nitrokey {
let device_status = DeviceStatus::Ok;
let command_id = self.buf[0];
let command_status = if let Ok(command_id) = CommandId::try_from(command_id) {
- self.execute_command(command_id, &self.buf[1..60], &mut buf)
+ command_id.execute(&self.buf[1..60], &mut buf)
} else {
CommandStatus::UnknownCommand
};
diff --git a/src/main.rs b/src/main.rs
index e10f4d2..caf7fdf 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -9,6 +9,7 @@ extern crate panic_halt;
#[macro_use]
mod util;
+mod commands;
mod device;
mod hid;
diff --git a/src/util.rs b/src/util.rs
index 4fc4448..9325e9e 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -3,6 +3,35 @@
use core::marker::Sized;
+macro_rules! enum_cmd {
+ (
+ $(#[$outer:meta])*
+ pub enum $name:ident {
+ $($var:ident($cmd:ident) = $num:expr),+
+ $(,)*
+ }
+ ) => {
+ enum_u8! {
+ $(#[$outer])*
+ pub enum $name {
+ $(
+ $var = $num,
+ )*
+ }
+ }
+
+ impl $name {
+ pub fn execute(&self, data: &[u8], buf: &mut [u8]) -> CommandStatus {
+ match *self {
+ $(
+ $name::$var => $cmd::execute_raw(data, buf),
+ )*
+ }
+ }
+ }
+ };
+}
+
macro_rules! enum_u8 {
(
$(#[$outer:meta])*