usbd_human_interface_device/device/
fido.rs

1//! HID FIDO Universal 2nd Factor (U2F)
2use crate::usb_class::prelude::*;
3use fugit::ExtU32;
4use usb_device::bus::UsbBus;
5use usb_device::class_prelude::UsbBusAllocator;
6
7/// Raw FIDO report descriptor.
8/// 
9/// See the [FIDO U2F HID Protocol Specification](https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-hid-protocol-v1.2-ps-20170411.html)
10/// for protocol detail
11#[rustfmt::skip]
12pub const FIDO_REPORT_DESCRIPTOR: &[u8] = &[
13    0x06, 0xD0, 0xF1, // Usage Page (FIDO),
14    0x09, 0x01, // Usage (U2F Authenticator Device)
15    0xA1, 0x01, // Collection (Application),
16    0x09, 0x20, //   Usage (Data In),
17    0x15, 0x00, //       Logical Minimum(0),
18    0x26, 0xFF, 0x00, // Logical Max (0x00FF),
19    0x75, 0x08, //       Report size (8)
20    0x95, 0x40, //       Report count (64)
21    0x81, 0x02, //       Input (Data | Variable | Absolute)
22    0x09, 0x21, //   Usage (Data Out),
23    0x15, 0x00, //       Logical Minimum(0),
24    0x26, 0xFF, 0x00, // Logical Max (0x00FF),
25    0x75, 0x08, //       Report size (8)
26    0x95, 0x40, //       Report count (64)
27    0x91, 0x02, //       Output (Data | Variable | Absolute)
28    0xC0,       // End Collection
29];
30
31#[cfg_attr(feature = "defmt", derive(defmt::Format))]
32#[derive(Clone, Copy, Debug, Eq, PartialEq)]
33#[repr(C, align(8))]
34pub struct RawFidoReport {
35    pub packet: [u8; 64],
36}
37impl Default for RawFidoReport {
38    fn default() -> Self {
39        Self { packet: [0u8; 64] }
40    }
41}
42
43pub struct RawFido<'a, B: UsbBus> {
44    interface: Interface<'a, B, InBytes64, OutBytes64, ReportSingle>,
45}
46
47impl<B: UsbBus> RawFido<'_, B> {
48    pub fn write_report(&mut self, report: &RawFidoReport) -> Result<(), UsbHidError> {
49        self.interface
50            .write_report(&report.packet)
51            .map(|_| ())
52            .map_err(UsbHidError::from)
53    }
54    pub fn read_report(&mut self) -> usb_device::Result<RawFidoReport> {
55        let mut report = RawFidoReport::default();
56        match self.interface.read_report(&mut report.packet) {
57            Err(e) => Err(e),
58            Ok(_) => Ok(report),
59        }
60    }
61}
62
63impl<'a, B: UsbBus> DeviceClass<'a> for RawFido<'a, B> {
64    type I = Interface<'a, B, InBytes64, OutBytes64, ReportSingle>;
65
66    fn interface(&mut self) -> &mut Self::I {
67        &mut self.interface
68    }
69
70    fn reset(&mut self) {}
71
72    fn tick(&mut self) -> Result<(), UsbHidError> {
73        Ok(())
74    }
75}
76
77pub struct RawFidoConfig<'a> {
78    interface: InterfaceConfig<'a, InBytes64, OutBytes64, ReportSingle>,
79}
80
81impl Default for RawFidoConfig<'_> {
82    fn default() -> Self {
83        Self::new(
84            unwrap!(
85                unwrap!(unwrap!(InterfaceBuilder::new(FIDO_REPORT_DESCRIPTOR))
86                    .description("U2F Token")
87                    .in_endpoint(5.millis()))
88                .with_out_endpoint(5.millis())
89            )
90            .build(),
91        )
92    }
93}
94
95impl<'a> RawFidoConfig<'a> {
96    #[must_use]
97    pub fn new(interface: InterfaceConfig<'a, InBytes64, OutBytes64, ReportSingle>) -> Self {
98        Self { interface }
99    }
100}
101
102impl<'a, B: UsbBus + 'a> UsbAllocatable<'a, B> for RawFidoConfig<'a> {
103    type Allocated = RawFido<'a, B>;
104
105    fn allocate(self, usb_alloc: &'a UsbBusAllocator<B>) -> Self::Allocated {
106        Self::Allocated {
107            interface: Interface::new(usb_alloc, self.interface),
108        }
109    }
110}