capsules_extra/usb/
ctap.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Client to Authenticator Protocol CTAPv2 over USB HID
6//!
7//! Based on the spec avaliable at: <https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html>
8
9use core::cmp;
10
11use super::descriptors;
12use super::descriptors::Buffer64;
13use super::descriptors::DescriptorType;
14use super::descriptors::EndpointAddress;
15use super::descriptors::EndpointDescriptor;
16use super::descriptors::HIDCountryCode;
17use super::descriptors::HIDDescriptor;
18use super::descriptors::HIDSubordinateDescriptor;
19use super::descriptors::InterfaceDescriptor;
20use super::descriptors::ReportDescriptor;
21use super::descriptors::TransferDirection;
22use super::usbc_client_ctrl::ClientCtrl;
23
24use kernel::hil;
25use kernel::hil::usb::TransferType;
26use kernel::utilities::cells::OptionalCell;
27use kernel::utilities::cells::TakeCell;
28use kernel::ErrorCode;
29
30/// Use 1 Interrupt transfer IN/OUT endpoint
31const ENDPOINT_NUM: usize = 1;
32
33const OUT_BUFFER: usize = 0;
34const IN_BUFFER: usize = 1;
35
36static LANGUAGES: &[u16; 1] = &[
37    0x0409, // English (United States)
38];
39/// Max packet size specified by spec
40pub const MAX_CTRL_PACKET_SIZE: u8 = 64;
41
42const N_ENDPOINTS: usize = 2;
43
44/// The HID report descriptor for CTAP
45/// This is a combination of:
46///     - the CTAP spec, example 8
47///     - USB HID spec examples
48/// Plus it matches: <https://chromium.googlesource.com/chromiumos/platform2/+/master/u2fd/u2fhid.cc>
49static REPORT_DESCRIPTOR: &[u8] = &[
50    0x06, 0xD0, 0xF1, // HID_UsagePage ( FIDO_USAGE_PAGE ),
51    0x09, 0x01, // HID_Usage ( FIDO_USAGE_CTAPHID ),
52    0xA1, 0x01, // HID_Collection ( HID_Application ),
53    0x09, 0x20, // HID_Usage ( FIDO_USAGE_DATA_IN ),
54    0x15, 0x00, // HID_LogicalMin ( 0 ),
55    0x26, 0xFF, 0x00, // HID_LogicalMaxS ( 0xff ),
56    0x75, 0x08, // HID_ReportSize ( 8 ),
57    0x95, 0x40, // HID_ReportCount ( HID_INPUT_REPORT_BYTES ),
58    0x81, 0x02, // HID_Input ( HID_Data | HID_Absolute | HID_Variable ),
59    0x09, 0x21, // HID_Usage ( FIDO_USAGE_DATA_OUT ),
60    0x15, 0x00, // HID_LogicalMin ( 0 ),
61    0x26, 0xFF, 0x00, // HID_LogicalMaxS ( 0xff ),
62    0x75, 0x08, // HID_ReportSize ( 8 ),
63    0x95, 0x40, // HID_ReportCount ( HID_OUTPUT_REPORT_BYTES ),
64    0x91, 0x02, // HID_Output ( HID_Data | HID_Absolute | HID_Variable ),
65    0xC0, // HID_EndCollection
66];
67
68static REPORT: ReportDescriptor<'static> = ReportDescriptor {
69    desc: REPORT_DESCRIPTOR,
70};
71
72static SUB_HID_DESCRIPTOR: &[HIDSubordinateDescriptor] = &[HIDSubordinateDescriptor {
73    typ: DescriptorType::Report,
74    len: REPORT_DESCRIPTOR.len() as u16,
75}];
76
77static HID_DESCRIPTOR: HIDDescriptor<'static> = HIDDescriptor {
78    hid_class: 0x0110,
79    country_code: HIDCountryCode::NotSupported,
80    sub_descriptors: SUB_HID_DESCRIPTOR,
81};
82
83/// Implementation of the CTAP HID (Human Interface Device)
84pub struct CtapHid<'a, U: 'a> {
85    /// Helper USB client library for handling many USB operations.
86    client_ctrl: ClientCtrl<'a, 'static, U>,
87
88    /// 64 byte buffers for each endpoint.
89    buffers: [Buffer64; N_ENDPOINTS],
90
91    client: OptionalCell<&'a dyn hil::usb_hid::Client<'a, [u8; 64]>>,
92
93    /// A buffer to hold the data we want to send
94    send_buffer: TakeCell<'static, [u8; 64]>,
95
96    /// A holder for the buffer to receive bytes into. We use this as a flag as
97    /// well, if we have a buffer then we are actively doing a receive.
98    recv_buffer: TakeCell<'static, [u8; 64]>,
99}
100
101impl<'a, U: hil::usb::UsbController<'a>> CtapHid<'a, U> {
102    pub fn new(
103        controller: &'a U,
104        vendor_id: u16,
105        product_id: u16,
106        strings: &'static [&'static str; 3],
107    ) -> Self {
108        let interfaces: &mut [InterfaceDescriptor] = &mut [InterfaceDescriptor {
109            interface_number: 0,
110            interface_class: 0x03,    // HID
111            interface_subclass: 0x00, // No subcall
112            interface_protocol: 0x00, // No protocol
113            ..InterfaceDescriptor::default()
114        }];
115
116        let endpoints: &[&[EndpointDescriptor]] = &[&[
117            EndpointDescriptor {
118                endpoint_address: EndpointAddress::new_const(
119                    ENDPOINT_NUM,
120                    TransferDirection::DeviceToHost,
121                ),
122                transfer_type: TransferType::Interrupt,
123                max_packet_size: 64,
124                interval: 5,
125            },
126            EndpointDescriptor {
127                endpoint_address: EndpointAddress::new_const(
128                    ENDPOINT_NUM,
129                    TransferDirection::HostToDevice,
130                ),
131                transfer_type: TransferType::Interrupt,
132                max_packet_size: 64,
133                interval: 5,
134            },
135        ]];
136
137        let (device_descriptor_buffer, other_descriptor_buffer) =
138            descriptors::create_descriptor_buffers(
139                descriptors::DeviceDescriptor {
140                    vendor_id,
141                    product_id,
142                    manufacturer_string: 1,
143                    product_string: 2,
144                    serial_number_string: 3,
145                    class: 0x03, // Class: HID
146                    max_packet_size_ep0: MAX_CTRL_PACKET_SIZE,
147                    ..descriptors::DeviceDescriptor::default()
148                },
149                descriptors::ConfigurationDescriptor::default(),
150                interfaces,
151                endpoints,
152                Some(&HID_DESCRIPTOR),
153                None,
154            );
155
156        CtapHid {
157            client_ctrl: ClientCtrl::new(
158                controller,
159                device_descriptor_buffer,
160                other_descriptor_buffer,
161                Some(&HID_DESCRIPTOR),
162                Some(&REPORT),
163                LANGUAGES,
164                strings,
165            ),
166            buffers: [Buffer64::default(), Buffer64::default()],
167            client: OptionalCell::empty(),
168            send_buffer: TakeCell::empty(),
169            recv_buffer: TakeCell::empty(),
170        }
171    }
172
173    #[inline]
174    fn controller(&self) -> &'a U {
175        self.client_ctrl.controller()
176    }
177
178    pub fn set_client(&'a self, client: &'a dyn hil::usb_hid::Client<'a, [u8; 64]>) {
179        self.client.set(client);
180    }
181}
182
183impl<'a, U: hil::usb::UsbController<'a>> hil::usb_hid::UsbHid<'a, [u8; 64]> for CtapHid<'a, U> {
184    fn send_buffer(
185        &'a self,
186        send: &'static mut [u8; 64],
187    ) -> Result<usize, (ErrorCode, &'static mut [u8; 64])> {
188        let len = send.len();
189
190        self.send_buffer.replace(send);
191        self.controller().endpoint_resume_in(ENDPOINT_NUM);
192
193        Ok(len)
194    }
195
196    fn send_cancel(&'a self) -> Result<&'static mut [u8; 64], ErrorCode> {
197        match self.send_buffer.take() {
198            Some(buf) => Ok(buf),
199            None => Err(ErrorCode::BUSY),
200        }
201    }
202
203    fn receive_buffer(
204        &'a self,
205        recv: &'static mut [u8; 64],
206    ) -> Result<(), (ErrorCode, &'static mut [u8; 64])> {
207        self.recv_buffer.replace(recv);
208        self.controller().endpoint_resume_out(ENDPOINT_NUM);
209        Ok(())
210    }
211
212    fn receive_cancel(&'a self) -> Result<&'static mut [u8; 64], ErrorCode> {
213        match self.recv_buffer.take() {
214            Some(buf) => Ok(buf),
215            None => Err(ErrorCode::BUSY),
216        }
217    }
218}
219
220impl<'a, U: hil::usb::UsbController<'a>> hil::usb::Client<'a> for CtapHid<'a, U> {
221    fn enable(&'a self) {
222        // Set up the default control endpoint
223        self.client_ctrl.enable();
224
225        // Setup buffers for IN and OUT data transfer.
226        self.controller()
227            .endpoint_set_out_buffer(ENDPOINT_NUM, &self.buffers[OUT_BUFFER].buf);
228        self.controller()
229            .endpoint_set_in_buffer(ENDPOINT_NUM, &self.buffers[IN_BUFFER].buf);
230        self.controller()
231            .endpoint_in_out_enable(TransferType::Interrupt, ENDPOINT_NUM);
232    }
233
234    fn attach(&'a self) {
235        self.client_ctrl.attach();
236    }
237
238    fn bus_reset(&'a self) {}
239
240    /// Handle a Control Setup transaction.
241    fn ctrl_setup(&'a self, endpoint: usize) -> hil::usb::CtrlSetupResult {
242        self.client_ctrl.ctrl_setup(endpoint)
243    }
244
245    /// Handle a Control In transaction
246    fn ctrl_in(&'a self, endpoint: usize) -> hil::usb::CtrlInResult {
247        self.client_ctrl.ctrl_in(endpoint)
248    }
249
250    /// Handle a Control Out transaction
251    fn ctrl_out(&'a self, endpoint: usize, packet_bytes: u32) -> hil::usb::CtrlOutResult {
252        self.client_ctrl.ctrl_out(endpoint, packet_bytes)
253    }
254
255    fn ctrl_status(&'a self, endpoint: usize) {
256        self.client_ctrl.ctrl_status(endpoint)
257    }
258
259    /// Handle the completion of a Control transfer
260    fn ctrl_status_complete(&'a self, endpoint: usize) {
261        if self.send_buffer.is_some() {
262            self.controller().endpoint_resume_in(ENDPOINT_NUM);
263        }
264
265        self.client_ctrl.ctrl_status_complete(endpoint)
266    }
267
268    /// Handle a Bulk/Interrupt IN transaction.
269    ///
270    /// This is called when we can send data to the host. It should get called
271    /// when we tell the controller we want to resume the IN endpoint (meaning
272    /// we know we have data to send) and afterwards until we return
273    /// `hil::usb::InResult::Delay` from this function. That means we can use
274    /// this as a callback to mean that the transmission finished by waiting
275    /// until this function is called when we don't have anything left to send.
276    fn packet_in(&'a self, transfer_type: TransferType, _endpoint: usize) -> hil::usb::InResult {
277        match transfer_type {
278            TransferType::Interrupt => {
279                self.send_buffer
280                    .take()
281                    .map_or(hil::usb::InResult::Delay, |buf| {
282                        // Get packet that we have shared with the underlying
283                        // USB stack to copy the tx into.
284                        let packet = &self.buffers[IN_BUFFER].buf;
285
286                        // Copy from the TX buffer to the outgoing USB packet.
287                        for i in 0..64 {
288                            packet[i].set(buf[i]);
289                        }
290
291                        // Put the TX buffer back so we can keep sending from it.
292                        self.send_buffer.replace(buf);
293
294                        // Return that we have data to send.
295                        hil::usb::InResult::Packet(64)
296                    })
297            }
298            TransferType::Bulk | TransferType::Control | TransferType::Isochronous => {
299                panic!("Transfer protocol not supported by CTAP v2");
300            }
301        }
302    }
303
304    /// Handle a Bulk/Interrupt OUT transaction
305    ///
306    /// This is data going from the host to the device (us)
307    fn packet_out(
308        &'a self,
309        transfer_type: TransferType,
310        endpoint: usize,
311        packet_bytes: u32,
312    ) -> hil::usb::OutResult {
313        match transfer_type {
314            TransferType::Interrupt => {
315                // If we have a receive buffer we can copy the incoming data in.
316                // If we do not have a buffer, then we apply back pressure by
317                // returning `hil::usb::OutResult::Delay` to the USB stack until
318                // we get a receive call.
319                self.recv_buffer
320                    .take()
321                    .map_or(hil::usb::OutResult::Delay, |buf| {
322                        // How many more bytes can we store in our RX buffer?
323                        let copy_length = cmp::min(packet_bytes as usize, buf.len());
324
325                        // Do the copy into the RX buffer.
326                        let packet = &self.buffers[OUT_BUFFER].buf;
327                        for i in 0..copy_length {
328                            buf[i] = packet[i].get();
329                        }
330
331                        // Notify the client
332                        self.client.map(move |client| {
333                            client.packet_received(Ok(()), buf, endpoint);
334                        });
335
336                        hil::usb::OutResult::Ok
337                    })
338            }
339            TransferType::Bulk | TransferType::Control | TransferType::Isochronous => {
340                panic!("Transfer protocol not supported by CTAP v2");
341            }
342        }
343    }
344
345    fn packet_transmitted(&'a self, endpoint: usize) {
346        self.send_buffer.take().map(|buf| {
347            self.client.map(move |client| {
348                client.packet_transmitted(Ok(()), buf, endpoint);
349            });
350        });
351    }
352}