diff options
| author | Robin Krahl <robin.krahl@ireas.org> | 2019-02-18 21:36:31 +0000 | 
|---|---|---|
| committer | Robin Krahl <robin.krahl@ireas.org> | 2019-02-18 23:27:48 +0100 | 
| commit | 9df0f3bd565dfbf8c97d02969a17504c688bf381 (patch) | |
| tree | da3a25d4900fc82a407ac729f5642f1e65cd0fbc | |
| parent | 49703cd770fd0522beae2dc53d1f882c9143c64e (diff) | |
| download | ntw-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.rs | 148 | ||||
| -rw-r--r-- | src/device.rs | 49 | ||||
| -rw-r--r-- | src/main.rs | 1 | ||||
| -rw-r--r-- | src/util.rs | 29 | 
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])*  | 
