From 95679e379a02c6a41ff71e5c948564b2e8ea4536 Mon Sep 17 00:00:00 2001 From: Surya Midatala Date: Mon, 22 Jul 2024 15:03:39 +0000 Subject: [PATCH 1/4] WIP Add ctap hid api --- crates/board/src/usb.rs | 10 + crates/board/src/usb/ctap.rs | 85 +++++++ crates/prelude/Cargo.toml | 1 + crates/prelude/src/ctap.rs | 372 ++++++++++++++++++++++++++++ crates/prelude/src/lib.rs | 2 + crates/prelude/src/usb.rs | 3 + crates/prelude/src/usb/ctap.rs | 65 +++++ examples/rust/opensk/Cargo.lock | 297 +++++++++++++++++++++- third_party/SchemaStore/schemastore | 2 +- third_party/WebAssembly/spec | 2 +- 10 files changed, 836 insertions(+), 3 deletions(-) create mode 100644 crates/board/src/usb/ctap.rs create mode 100644 crates/prelude/src/ctap.rs create mode 100644 crates/prelude/src/usb/ctap.rs diff --git a/crates/board/src/usb.rs b/crates/board/src/usb.rs index 999ddb43..087abf31 100644 --- a/crates/board/src/usb.rs +++ b/crates/board/src/usb.rs @@ -17,12 +17,18 @@ #[cfg(feature = "api-usb-serial")] pub mod serial; +// TODO: Should this also be gated behind api-usb-serial feature or should it be +// a different one? +pub mod ctap; + /// USB event. #[derive(Debug, PartialEq, Eq)] pub enum Event { /// Serial event. #[cfg(feature = "api-usb-serial")] Serial(serial::Event), + + Ctap(ctap::Event), } impl From for crate::Event { @@ -36,8 +42,12 @@ pub trait Api: Send { /// USB serial interface. #[cfg(feature = "api-usb-serial")] type Serial: serial::Api; + + type Ctap: ctap::Api; } /// USB serial interface. #[cfg(feature = "api-usb-serial")] pub type Serial = as Api>::Serial; + +pub type Ctap = as Api>::Ctap; diff --git a/crates/board/src/usb/ctap.rs b/crates/board/src/usb/ctap.rs new file mode 100644 index 00000000..b7a79a52 --- /dev/null +++ b/crates/board/src/usb/ctap.rs @@ -0,0 +1,85 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! CTAP HID interface. + +use usb_device::bus::UsbBus; +use usbd_ctaphid::CtapHid; + +use super::serial::{self, HasSerial}; + +/// CTAP event. +#[derive(Debug, PartialEq, Eq)] +pub enum Event { + /// There might be data to read. + /// + /// This is only guaranteed to be triggered after a short read. + Read, + + /// It might be possible to write data. + /// + /// This is only guaranteed to be triggered after a short write. + Write, +} + +impl From for crate::Event { + fn from(event: Event) -> Self { + super::Event::Ctap(event).into() + } +} + +pub trait Api: Send { + /// Reads from the USB serial into a buffer. + /// + /// Returns the number of bytes read. It could be zero if there's nothing to read. + fn read(output: &mut [u8]) -> Result; + + /// Writes from a buffer to the USB serial. + /// + /// Returns the number of bytes written. It could be zero if the other side is not ready. + fn write(input: &[u8]) -> Result; +} + +pub trait HasCtapHid: Send { + type UsbBus: UsbBus; + + fn with_ctaphid(f: impl FnOnce(&mut CtapHid) -> R) -> R; +} + +/// Wrapper type for boards using the `usbd_ctaphid` crate. +pub struct WithCtapHid { + _never: !, + _has_serial: T, +} + +/// Helper struct for boards using the `usbd_ctaphid` crate. +pub struct CtapHid<'a, T: UsbBus> { + ctap_hid: usbd_ctaphid::CtapHid, + read_enabled: bool, + write_enabled: bool, +} + +impl Api for WithCtapHid { + fn read(output: &mut [u8]) -> Result { + match T::with_ctaphid(|ctap_hid| ctap_hid.pipe.read_and_handle_packet()) { + // todo!() + } + } + + fn write(input: &[u8]) -> Result { + match T::with_ctaphid(|ctap_hid| ctap_hid.check_for_app_response()) { + // todo!() + } + } +} diff --git a/crates/prelude/Cargo.toml b/crates/prelude/Cargo.toml index b8b8451d..9aef9bce 100644 --- a/crates/prelude/Cargo.toml +++ b/crates/prelude/Cargo.toml @@ -24,6 +24,7 @@ digest = { version = "0.10.7", default-features = false, features = ["mac"], opt rlsf = { version = "0.2.1", default-features = false, optional = true } sealed = { version = "0.5.0", default-features = false, optional = true } typenum = { version = "1.17.0", default-features = false, optional = true } +usbd-ctaphid = "0.1.0" wasefire-applet-api = { version = "0.6.1", path = "../api", features = ["wasm"] } wasefire-error = { version = "0.1.1", path = "../error" } wasefire-sync = { version = "0.1.1", path = "../sync" } diff --git a/crates/prelude/src/ctap.rs b/crates/prelude/src/ctap.rs new file mode 100644 index 00000000..fc0d7224 --- /dev/null +++ b/crates/prelude/src/ctap.rs @@ -0,0 +1,372 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Provides an API for CTAP HID interface. + +// use alloc::boxed::Box; +// use alloc::vec::Vec; +// use core::cell::Cell; +use core::fmt::Debug; + +use sealed::sealed; + +use crate::Error; +// use crate::scheduling; + +/// CtapHid events to be notified. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Event { + /// The CtapHid may be ready to read. + Read, + + /// The CtapHid may be ready to write. + Write, +} + +// TODO: What else do we need in the Ctap Api? + +/// Provides high-level CtapHid API from low-level API. +/// +/// This trait should only be implemented by the prelude and is thus sealed. Its purpose is to +/// provide a unique interface to the different CtapHids. +#[sealed(pub(crate))] +pub trait CtapHid { + /// Reads from the CtapHid into a buffer without blocking. + /// + /// Returns how many bytes were read (and thus written to the buffer). This function does not + /// block, so if there are no data available for read, zero is returned. + fn read(&self, buffer: &mut [u8]) -> Result; + + /// Writes from a buffer to the CtapHid. + /// + /// Returns how many bytes were written (and thus read from the buffer). This function does not + /// block, so if the CtapHid is not ready for write, zero is returned. + fn write(&self, buffer: &[u8]) -> Result; + + // /// Flushes the CtapHid (in case reads or writes are buffered). + // fn flush(&self) -> Result<(), Error>; + + // /// Registers a callback for an event. + // /// + // /// # Safety + // /// + // /// The function pointer and data must live until unregistered. The function must support + // /// concurrent calls. + // unsafe fn register( + // &self, event: Event, func: extern "C" fn(*const u8), data: *const u8, + // ) -> Result<(), Error>; + + // /// Unregisters the callback for an event. + // fn unregister(&self, event: Event) -> Result<(), Error>; +} + +/// Reads from the CtapHid into a buffer without blocking. +/// +/// Returns how many bytes were read (and thus written to the buffer). This function does not +/// block, so if there are no data available for read, zero is returned. +pub fn read(ctap_hid: &T, buffer: &mut [u8]) -> Result { + ctap_hid.read(buffer) +} + +// /// Synchronously reads at least one byte from a CtapHid into a buffer. +// /// +// /// This function will block if necessary. +// pub fn read_any(ctap_hid: &T, buffer: &mut [u8]) -> Result { +// let mut reader = Reader::new(ctap_hid, buffer); +// scheduling::wait_until(|| !reader.is_empty()); +// reader.result() +// } + +// /// Synchronously reads from a CtapHid into a buffer until it is filled. +// /// +// /// This function will block if necessary. +// pub fn read_all(ctap_hid: &T, buffer: &mut [u8]) -> Result<(), Error> { +// let mut reader = Reader::new(ctap_hid, buffer); +// scheduling::wait_until(|| reader.is_done()); +// reader.result()?; +// Ok(()) +// } + +// /// Synchronously reads exactly one byte. +// pub fn read_byte(ctap_hid: &T) -> Result { +// let mut byte = 0; +// read_any(ctap_hid, core::slice::from_mut(&mut byte))?; +// Ok(byte) +// } + +/// Writes from a buffer to the CtapHid. +/// +/// Returns how many bytes were written (and thus read from the buffer). This function does not +/// block, so if the CtapHid is not ready for write, zero is returned. +pub fn write(ctap_hid: &T, buffer: &[u8]) -> Result { + ctap_hid.write(buffer) +} + +// /// Writes at least one byte from a buffer to a CtapHid. +// /// +// /// This function will block if necessary. +// pub fn write_any(ctap_hid: &T, buffer: &[u8]) -> Result { +// let mut writer = Writer::new(ctap_hid, buffer); +// scheduling::wait_until(|| !writer.is_empty()); +// writer.result() +// } + +// /// Writes from a buffer to a CtapHid until everything has been written. +// /// +// /// This function will block if necessary. +// pub fn write_all(ctap_hid: &T, buffer: &[u8]) -> Result<(), Error> { +// let mut writer = Writer::new(ctap_hid, buffer); +// scheduling::wait_until(|| writer.is_done()); +// writer.result()?; +// Ok(()) +// } + +// /// Flushes the CtapHid (in case reads or writes are buffered). +// pub fn flush(ctap_hid: &T) -> Result<(), Error> { +// ctap_hid.flush() +// } + +// /// Asynchronously listens for event notifications. +// pub struct Listener<'a, T: CtapHid> { +// ctap_hid: &'a T, +// event: Event, +// notified: &'static Cell, +// } + +// impl<'a, T: CtapHid> Listener<'a, T> { +// /// Starts listening for the provided event until dropped. +// pub fn new(ctap_hid: &'a T, event: Event) -> Self { +// let notified = Box::leak(Box::new(Cell::new(true))); +// let func = Self::call; +// let data = notified as *mut _ as *const u8; +// unsafe { ctap_hid.register(event, func, data) }.unwrap(); +// Listener { ctap_hid, event, notified } +// } + +// /// Returns whether the event triggered since the last call. +// pub fn is_notified(&mut self) -> bool { +// self.notified.replace(false) +// } + +// extern "C" fn call(data: *const u8) { +// let notified = unsafe { &*(data as *const Cell) }; +// notified.set(true); +// } +// } + +// impl<'a, T: CtapHid> Drop for Listener<'a, T> { +// fn drop(&mut self) { +// self.ctap_hid.unregister(self.event).unwrap(); +// drop(unsafe { Box::from_raw(self.notified.as_ptr()) }); +// } +// } + +// /// Asynchronously reads delimited frames. +// /// +// /// If you want to read at most a given amount instead, use [`Reader`]. +// pub struct DelimitedReader<'a, T: CtapHid> { +// listener: Listener<'a, T>, +// buffer: Vec, +// frame: Option, // index of first delimiter in buffer, if any +// delimiter: u8, +// } + +// impl<'a, T: CtapHid> DelimitedReader<'a, T> { +// /// Starts reading delimited frames from a CtapHid. +// pub fn new(ctap_hid: &'a T, delimiter: u8) -> Self { +// let listener = Listener::new(ctap_hid, Event::Read); +// DelimitedReader { listener, buffer: Vec::new(), frame: None, delimiter } +// } + +// /// Returns the next delimited frame (including the delimiter), if any. +// /// +// /// This function should be called until it returns `None` before waiting for callback again. +// /// Otherwise, it may be possible that the platform doesn't notify for new data if the existing +// /// data has not been read. +// pub fn next_frame(&mut self) -> Option> { +// if self.frame.is_none() && self.listener.is_notified() { +// self.flush(); +// } +// self.frame.map(|len| { +// let mut frame = self.buffer.split_off(len + 1); +// core::mem::swap(&mut frame, &mut self.buffer); +// self.frame = self.buffer.iter().position(|&x| x == self.delimiter); +// frame +// }) +// } + +// /// Stops reading and returns the current buffer. +// /// +// /// The buffer may contain multiple delimited frames. +// pub fn stop(self) -> Vec { +// self.buffer +// } + +// fn flush(&mut self) { +// while self.read() {} +// } + +// fn read(&mut self) -> bool { +// let mut data = [0; 32]; +// let len = self.listener.ctap_hid.read(&mut data).unwrap(); +// let pos = self.buffer.len(); +// self.buffer.extend_from_slice(&data[.. len]); +// if self.frame.is_none() { +// for i in pos .. pos + len { +// if self.buffer[i] == self.delimiter { +// self.frame = Some(i); +// break; +// } +// } +// } +// len == data.len() +// } +// } + +// /// Asynchronously reads into the provided buffer. +// /// +// /// If instead you want to continuously read delimited frames, use [`DelimitedReader`]. +// #[must_use] +// pub struct Reader<'a, T: CtapHid>(Updater<'a, T>); + +// impl<'a, T: CtapHid> Reader<'a, T> { +// /// Asynchronously reads from a CtapHid into a buffer. +// pub fn new(ctap_hid: &'a T, buffer: &'a mut [u8]) -> Self { +// Reader(Updater::new(ctap_hid, Kind::Reader { buffer })) +// } + +// /// Returns whether anything has been read (or an error occurred). +// pub fn is_empty(&mut self) -> bool { +// self.0.is_empty() +// } + +// /// Returns whether everything has been read (or an error occurred). +// pub fn is_done(&mut self) -> bool { +// self.0.is_done() +// } + +// /// Returns how many bytes were read (or if an error occurred). +// pub fn result(self) -> Result { +// self.0.result() +// } +// } + +// /// Asynchronously writes from the provided buffer. +// #[must_use] +// pub struct Writer<'a, T: CtapHid>(Updater<'a, T>); + +// impl<'a, T: CtapHid> Writer<'a, T> { +// /// Asynchronously writes from a buffer to a CtapHid. +// pub fn new(ctap_hid: &'a T, buffer: &'a [u8]) -> Self { +// Writer(Updater::new(ctap_hid, Kind::Writer { buffer })) +// } + +// /// Returns whether anything has been written (or an error occurred). +// pub fn is_empty(&mut self) -> bool { +// self.0.is_empty() +// } + +// /// Returns whether everything has been written (or an error occurred). +// pub fn is_done(&mut self) -> bool { +// self.0.is_done() +// } + +// /// Returns how many bytes were written (or if an error occurred). +// pub fn result(self) -> Result { +// self.0.result() +// } +// } + +// struct Updater<'a, T: CtapHid> { +// // The listener is alive as long as not done (see `should_listen()`). +// listener: Option>, +// kind: Kind<'a>, +// result: Result, +// } + +// impl<'a, T: CtapHid> Updater<'a, T> { +// fn new(ctap_hid: &'a T, kind: Kind<'a>) -> Self { +// let event = kind.event(); +// let mut result = Updater { listener: None, kind, result: Ok(0) }; +// if result.should_listen() { +// result.listener = Some(Listener::new(ctap_hid, event)); +// } +// let _ = result.update(); +// result +// } + +// fn is_empty(&mut self) -> bool { +// matches!(self.update(), Ok(0)) +// } + +// fn is_done(&mut self) -> bool { +// let _ = self.update(); +// self.listener.is_none() +// } + +// fn result(mut self) -> Result { +// self.update() +// } + +// fn update(&mut self) -> Result { +// let listener = match &mut self.listener { +// Some(x) => x, +// None => return self.result, +// }; +// if !listener.is_notified() { +// return self.result; +// } +// let pos = self.result.as_mut().unwrap(); +// match self.kind.update(listener.ctap_hid, *pos) { +// Ok(len) => *pos += len, +// err => self.result = err, +// } +// if !self.should_listen() { +// self.listener = None; +// } +// self.result +// } + +// fn should_listen(&self) -> bool { +// matches!(self.result, Ok(len) if len < self.kind.len()) +// } +// } + +// enum Kind<'a> { +// Reader { buffer: &'a mut [u8] }, +// Writer { buffer: &'a [u8] }, +// } + +// impl<'a> Kind<'a> { +// fn event(&self) -> Event { +// match self { +// Kind::Reader { .. } => Event::Read, +// Kind::Writer { .. } => Event::Write, +// } +// } + +// fn len(&self) -> usize { +// match self { +// Kind::Reader { buffer } => buffer.len(), +// Kind::Writer { buffer } => buffer.len(), +// } +// } + +// fn update(&mut self, ctap_hid: &impl CtapHid, pos: usize) -> Result { +// match self { +// Kind::Reader { buffer } => ctap_hid.read(&mut buffer[pos ..]), +// Kind::Writer { buffer } => ctap_hid.write(&buffer[pos ..]), +// } +// } +// } diff --git a/crates/prelude/src/lib.rs b/crates/prelude/src/lib.rs index 1e95112d..571827c4 100644 --- a/crates/prelude/src/lib.rs +++ b/crates/prelude/src/lib.rs @@ -51,6 +51,8 @@ pub mod button; mod callback; #[cfg(feature = "internal-api-crypto")] pub mod crypto; +// TODO: Add feature +pub mod ctap; pub mod debug; #[cfg(feature = "api-gpio")] pub mod gpio; diff --git a/crates/prelude/src/usb.rs b/crates/prelude/src/usb.rs index 14fb0aba..d92e3605 100644 --- a/crates/prelude/src/usb.rs +++ b/crates/prelude/src/usb.rs @@ -19,3 +19,6 @@ #[cfg(feature = "api-usb-serial")] pub mod serial; + +// TODO: Add feature +pub mod ctap; diff --git a/crates/prelude/src/usb/ctap.rs b/crates/prelude/src/usb/ctap.rs new file mode 100644 index 00000000..bbe3ddd9 --- /dev/null +++ b/crates/prelude/src/usb/ctap.rs @@ -0,0 +1,65 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Provides API for CTAP HID. + +use sealed::sealed; +use wasefire_applet_api::usb::serial as api; + +// use crate::ctap::Event; +use crate::{convert, Error}; +// use crate::convert_unit; + +/// Implements the [`CtapHid`](crate::ctap::CtapHid) interface for USB transport. +pub struct UsbCtapHid; + +#[sealed] +impl crate::ctap::CtapHid for UsbCtapHid { + fn read(&self, buffer: &mut [u8]) -> Result { + let params = api::read::Params { ptr: buffer.as_mut_ptr(), len: buffer.len() }; + convert(unsafe { api::read(params) }) + } + + fn write(&self, buffer: &[u8]) -> Result { + let params = api::write::Params { ptr: buffer.as_ptr(), len: buffer.len() }; + convert(unsafe { api::write(params) }) + } + + // fn flush(&self) -> Result<(), Error> { + // convert_unit(unsafe { api::flush() }) + // } + + // unsafe fn register( + // &self, event: Event, func: extern "C" fn(*const u8), data: *const u8, + // ) -> Result<(), Error> { + // let params = api::register::Params { + // event: convert_event(event) as usize, + // handler_func: func, + // handler_data: data, + // }; + // convert_unit(unsafe { api::register(params) }) + // } + // #[doc = " Unregisters the callback for an event."] + // fn unregister(&self, event: Event) -> Result<(), Error> { + // let params = api::unregister::Params { event: convert_event(event) as usize }; + // convert_unit(unsafe { api::unregister(params) }) + // } +} + +// fn convert_event(event: Event) -> api::Event { +// match event { +// Event::Read => api::Event::Read, +// Event::Write => api::Event::Write, +// } +// } diff --git a/examples/rust/opensk/Cargo.lock b/examples/rust/opensk/Cargo.lock index 9d5bb941..1a9f67b5 100644 --- a/examples/rust/opensk/Cargo.lock +++ b/examples/rust/opensk/Cargo.lock @@ -17,12 +17,33 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + [[package]] name = "base16ct" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.4.1" @@ -70,6 +91,18 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "cbor-smol" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d516e3e353d5fc5ee156028f43224033430fd08ef05f8d5dba18a57a4ee5df49" +dependencies = [ + "delog", + "heapless", + "heapless-bytes", + "serde", +] + [[package]] name = "cc" version = "1.0.83" @@ -91,6 +124,17 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "cosey" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39323fe531b92e7acad90b8550b58cec63d29a6c5a56e02de4b25b6aeedbf82e" +dependencies = [ + "heapless-bytes", + "serde", + "serde_repr", +] + [[package]] name = "cpufeatures" version = "0.2.12" @@ -100,6 +144,12 @@ dependencies = [ "libc", ] +[[package]] +name = "critical-section" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" + [[package]] name = "crypto" version = "0.1.0" @@ -139,6 +189,37 @@ dependencies = [ "typenum", ] +[[package]] +name = "ctap-types" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4fa005ff525537460e1fd70a1ff4f417ae4a6ed14887f3e4720221e20f7562" +dependencies = [ + "bitflags 1.3.2", + "cbor-smol", + "cosey", + "delog", + "heapless", + "heapless-bytes", + "interchange", + "iso7816", + "serde", + "serde-indexed", + "serde_repr", +] + +[[package]] +name = "ctaphid-dispatch" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9e775f67c3a82a134a9e23e0771d3fb3808612ab03843cd31a9b0312004bdde" +dependencies = [ + "delog", + "heapless", + "heapless-bytes", + "interchange", +] + [[package]] name = "data-encoding" version = "2.6.0" @@ -165,6 +246,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "delog" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af2b93368262340c9d4441251b824500d1b641a50957ecf4219a2cc41b9eac8f" +dependencies = [ + "log", +] + [[package]] name = "der" version = "0.7.9" @@ -218,6 +308,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "embedded-time" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a4b4d10ac48d08bfe3db7688c402baadb244721f30a77ce360bd24c3dffe58" +dependencies = [ + "num", +] + [[package]] name = "ff" version = "0.13.0" @@ -276,6 +375,40 @@ dependencies = [ "subtle", ] +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "serde", + "spin 0.9.8", + "stable_deref_trait", +] + +[[package]] +name = "heapless-bytes" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7285eba272c6af3e9f15fb9e1c1b6e7d35aa70580ffe0d47af017e97dfb6f48b" +dependencies = [ + "heapless", + "serde", + "typenum", +] + [[package]] name = "heck" version = "0.4.1" @@ -306,6 +439,22 @@ dependencies = [ "digest", ] +[[package]] +name = "interchange" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "310d743c23f798f10d5ba2f77fdd3eff06aaf2d8f8b9d78beba7fb1167f4ccbf" + +[[package]] +name = "iso7816" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3af73ac9c821e7aea3280532118e15cdf9e7bb45c923cbf0e319ae25b27d20c" +dependencies = [ + "delog", + "heapless", +] + [[package]] name = "itoa" version = "0.4.8" @@ -327,6 +476,16 @@ version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.21" @@ -339,6 +498,68 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +[[package]] +name = "num" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f" +dependencies = [ + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "num_enum" version = "0.7.2" @@ -400,7 +621,7 @@ version = "0.10.62" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" dependencies = [ - "bitflags", + "bitflags 2.4.1", "cfg-if", "foreign-types", "libc", @@ -589,12 +810,27 @@ dependencies = [ "winapi", ] +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "ryu" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "sealed" version = "0.5.0" @@ -620,6 +856,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + [[package]] name = "serde" version = "1.0.197" @@ -629,6 +871,17 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-indexed" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fca2da10b1f1623f47130256065e05e94fd7a98dbd26a780a4c5de831b21e5c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "serde_derive" version = "1.0.197" @@ -651,6 +904,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "sha2" version = "0.10.8" @@ -688,9 +952,16 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" dependencies = [ + "lock_api", "portable-atomic", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "subtle" version = "2.5.0" @@ -737,6 +1008,29 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "usb-device" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f6cc3adc849b5292b4075fc0d5fdcf2f24866e88e336dd27a8943090a520508" + +[[package]] +name = "usbd-ctaphid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddb69f660f962236eb21565780c433412ad0ba35e5106f53191cdf443c1d03d2" +dependencies = [ + "ctap-types", + "ctaphid-dispatch", + "delog", + "embedded-time", + "heapless", + "heapless-bytes", + "interchange", + "serde", + "usb-device", +] + [[package]] name = "uuid" version = "0.8.2" @@ -772,6 +1066,7 @@ dependencies = [ "crypto-common", "sealed", "typenum", + "usbd-ctaphid", "wasefire-applet-api", "wasefire-error", "wasefire-sync", diff --git a/third_party/SchemaStore/schemastore b/third_party/SchemaStore/schemastore index ad5181c4..21a9cf41 160000 --- a/third_party/SchemaStore/schemastore +++ b/third_party/SchemaStore/schemastore @@ -1 +1 @@ -Subproject commit ad5181c491fabf8a86581d8793b9288dabc5a3b3 +Subproject commit 21a9cf416390ff212e56dff5da209c3102617532 diff --git a/third_party/WebAssembly/spec b/third_party/WebAssembly/spec index 1291af30..724b3e7f 160000 --- a/third_party/WebAssembly/spec +++ b/third_party/WebAssembly/spec @@ -1 +1 @@ -Subproject commit 1291af309889a5900cdad9c38526e457cb77eac3 +Subproject commit 724b3e7fc8338ef7b4acbbf5d834b4948f030bf5 From 57a4c47d73a9758e27e96cd6584b1bd0b8400b29 Mon Sep 17 00:00:00 2001 From: Surya Midatala Date: Sat, 27 Jul 2024 19:10:44 +0000 Subject: [PATCH 2/4] Address comments --- crates/board/src/usb.rs | 3 +- crates/board/src/usb/ctap.rs | 6 +- crates/prelude/Cargo.toml | 1 + crates/prelude/src/ctap.rs | 611 +++++++++++++++++------------------ crates/prelude/src/usb.rs | 2 +- 5 files changed, 310 insertions(+), 313 deletions(-) diff --git a/crates/board/src/usb.rs b/crates/board/src/usb.rs index 087abf31..7c1519c2 100644 --- a/crates/board/src/usb.rs +++ b/crates/board/src/usb.rs @@ -17,8 +17,7 @@ #[cfg(feature = "api-usb-serial")] pub mod serial; -// TODO: Should this also be gated behind api-usb-serial feature or should it be -// a different one? +#[cfg(feature = "api-usb-ctap")] pub mod ctap; /// USB event. diff --git a/crates/board/src/usb/ctap.rs b/crates/board/src/usb/ctap.rs index b7a79a52..00bca282 100644 --- a/crates/board/src/usb/ctap.rs +++ b/crates/board/src/usb/ctap.rs @@ -43,12 +43,12 @@ pub trait Api: Send { /// Reads from the USB serial into a buffer. /// /// Returns the number of bytes read. It could be zero if there's nothing to read. - fn read(output: &mut [u8]) -> Result; + fn read(output: &mut [u8; 64]) -> Result; /// Writes from a buffer to the USB serial. /// /// Returns the number of bytes written. It could be zero if the other side is not ready. - fn write(input: &[u8]) -> Result; + fn write(input: &[u8; 64]) -> Result; } pub trait HasCtapHid: Send { @@ -60,7 +60,7 @@ pub trait HasCtapHid: Send { /// Wrapper type for boards using the `usbd_ctaphid` crate. pub struct WithCtapHid { _never: !, - _has_serial: T, + _has_ctap: T, } /// Helper struct for boards using the `usbd_ctaphid` crate. diff --git a/crates/prelude/Cargo.toml b/crates/prelude/Cargo.toml index 9aef9bce..29cab407 100644 --- a/crates/prelude/Cargo.toml +++ b/crates/prelude/Cargo.toml @@ -61,6 +61,7 @@ api-store = ["internal-api-store", "wasefire-applet-api/api-store"] api-store-fragment = ["internal-api-store", "wasefire-applet-api/api-store-fragment"] api-timer = ["wasefire-applet-api/api-timer"] api-uart = ["internal-serial", "wasefire-applet-api/api-uart"] +api-usb-ctap = ["internal-api-usb", "internal-serial", "wasefire-applet-api/api-usb-ctap"] api-usb-serial = ["internal-api-usb", "internal-serial", "wasefire-applet-api/api-usb-serial"] # Enables all API features. full-api = [ diff --git a/crates/prelude/src/ctap.rs b/crates/prelude/src/ctap.rs index fc0d7224..70d33653 100644 --- a/crates/prelude/src/ctap.rs +++ b/crates/prelude/src/ctap.rs @@ -14,15 +14,14 @@ //! Provides an API for CTAP HID interface. -// use alloc::boxed::Box; -// use alloc::vec::Vec; -// use core::cell::Cell; +use alloc::boxed::Box; +use alloc::vec::Vec; +use core::cell::Cell; use core::fmt::Debug; use sealed::sealed; -use crate::Error; -// use crate::scheduling; +use crate::{scheduling, Error}; /// CtapHid events to be notified. #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -34,8 +33,6 @@ pub enum Event { Write, } -// TODO: What else do we need in the Ctap Api? - /// Provides high-level CtapHid API from low-level API. /// /// This trait should only be implemented by the prelude and is thus sealed. Its purpose is to @@ -46,29 +43,29 @@ pub trait CtapHid { /// /// Returns how many bytes were read (and thus written to the buffer). This function does not /// block, so if there are no data available for read, zero is returned. - fn read(&self, buffer: &mut [u8]) -> Result; + fn read(&self, buffer: &mut [u8; 64]) -> Result; /// Writes from a buffer to the CtapHid. /// /// Returns how many bytes were written (and thus read from the buffer). This function does not /// block, so if the CtapHid is not ready for write, zero is returned. - fn write(&self, buffer: &[u8]) -> Result; - - // /// Flushes the CtapHid (in case reads or writes are buffered). - // fn flush(&self) -> Result<(), Error>; - - // /// Registers a callback for an event. - // /// - // /// # Safety - // /// - // /// The function pointer and data must live until unregistered. The function must support - // /// concurrent calls. - // unsafe fn register( - // &self, event: Event, func: extern "C" fn(*const u8), data: *const u8, - // ) -> Result<(), Error>; - - // /// Unregisters the callback for an event. - // fn unregister(&self, event: Event) -> Result<(), Error>; + fn write(&self, buffer: &[u8; 64]) -> Result; + + /// Flushes the CtapHid (in case reads or writes are buffered). + fn flush(&self) -> Result<(), Error>; + + /// Registers a callback for an event. + /// + /// # Safety + /// + /// The function pointer and data must live until unregistered. The function must support + /// concurrent calls. + unsafe fn register( + &self, event: Event, func: extern "C" fn(*const u8), data: *const u8, + ) -> Result<(), Error>; + + /// Unregisters the callback for an event. + fn unregister(&self, event: Event) -> Result<(), Error>; } /// Reads from the CtapHid into a buffer without blocking. @@ -79,31 +76,31 @@ pub fn read(ctap_hid: &T, buffer: &mut [u8]) -> Result ctap_hid.read(buffer) } -// /// Synchronously reads at least one byte from a CtapHid into a buffer. -// /// -// /// This function will block if necessary. -// pub fn read_any(ctap_hid: &T, buffer: &mut [u8]) -> Result { -// let mut reader = Reader::new(ctap_hid, buffer); -// scheduling::wait_until(|| !reader.is_empty()); -// reader.result() -// } - -// /// Synchronously reads from a CtapHid into a buffer until it is filled. -// /// -// /// This function will block if necessary. -// pub fn read_all(ctap_hid: &T, buffer: &mut [u8]) -> Result<(), Error> { -// let mut reader = Reader::new(ctap_hid, buffer); -// scheduling::wait_until(|| reader.is_done()); -// reader.result()?; -// Ok(()) -// } - -// /// Synchronously reads exactly one byte. -// pub fn read_byte(ctap_hid: &T) -> Result { -// let mut byte = 0; -// read_any(ctap_hid, core::slice::from_mut(&mut byte))?; -// Ok(byte) -// } +/// Synchronously reads at least one byte from a CtapHid into a buffer. +/// +/// This function will block if necessary. +pub fn read_any(ctap_hid: &T, buffer: &mut [u8]) -> Result { + let mut reader = Reader::new(ctap_hid, buffer); + scheduling::wait_until(|| !reader.is_empty()); + reader.result() +} + +/// Synchronously reads from a CtapHid into a buffer until it is filled. +/// +/// This function will block if necessary. +pub fn read_all(ctap_hid: &T, buffer: &mut [u8]) -> Result<(), Error> { + let mut reader = Reader::new(ctap_hid, buffer); + scheduling::wait_until(|| reader.is_done()); + reader.result()?; + Ok(()) +} + +/// Synchronously reads exactly one byte. +pub fn read_byte(ctap_hid: &T) -> Result { + let mut byte = 0; + read_any(ctap_hid, core::slice::from_mut(&mut byte))?; + Ok(byte) +} /// Writes from a buffer to the CtapHid. /// @@ -113,260 +110,260 @@ pub fn write(ctap_hid: &T, buffer: &[u8]) -> Result { ctap_hid.write(buffer) } -// /// Writes at least one byte from a buffer to a CtapHid. -// /// -// /// This function will block if necessary. -// pub fn write_any(ctap_hid: &T, buffer: &[u8]) -> Result { -// let mut writer = Writer::new(ctap_hid, buffer); -// scheduling::wait_until(|| !writer.is_empty()); -// writer.result() -// } - -// /// Writes from a buffer to a CtapHid until everything has been written. -// /// -// /// This function will block if necessary. -// pub fn write_all(ctap_hid: &T, buffer: &[u8]) -> Result<(), Error> { -// let mut writer = Writer::new(ctap_hid, buffer); -// scheduling::wait_until(|| writer.is_done()); -// writer.result()?; -// Ok(()) -// } - -// /// Flushes the CtapHid (in case reads or writes are buffered). -// pub fn flush(ctap_hid: &T) -> Result<(), Error> { -// ctap_hid.flush() -// } - -// /// Asynchronously listens for event notifications. -// pub struct Listener<'a, T: CtapHid> { -// ctap_hid: &'a T, -// event: Event, -// notified: &'static Cell, -// } - -// impl<'a, T: CtapHid> Listener<'a, T> { -// /// Starts listening for the provided event until dropped. -// pub fn new(ctap_hid: &'a T, event: Event) -> Self { -// let notified = Box::leak(Box::new(Cell::new(true))); -// let func = Self::call; -// let data = notified as *mut _ as *const u8; -// unsafe { ctap_hid.register(event, func, data) }.unwrap(); -// Listener { ctap_hid, event, notified } -// } - -// /// Returns whether the event triggered since the last call. -// pub fn is_notified(&mut self) -> bool { -// self.notified.replace(false) -// } - -// extern "C" fn call(data: *const u8) { -// let notified = unsafe { &*(data as *const Cell) }; -// notified.set(true); -// } -// } - -// impl<'a, T: CtapHid> Drop for Listener<'a, T> { -// fn drop(&mut self) { -// self.ctap_hid.unregister(self.event).unwrap(); -// drop(unsafe { Box::from_raw(self.notified.as_ptr()) }); -// } -// } - -// /// Asynchronously reads delimited frames. -// /// -// /// If you want to read at most a given amount instead, use [`Reader`]. -// pub struct DelimitedReader<'a, T: CtapHid> { -// listener: Listener<'a, T>, -// buffer: Vec, -// frame: Option, // index of first delimiter in buffer, if any -// delimiter: u8, -// } - -// impl<'a, T: CtapHid> DelimitedReader<'a, T> { -// /// Starts reading delimited frames from a CtapHid. -// pub fn new(ctap_hid: &'a T, delimiter: u8) -> Self { -// let listener = Listener::new(ctap_hid, Event::Read); -// DelimitedReader { listener, buffer: Vec::new(), frame: None, delimiter } -// } - -// /// Returns the next delimited frame (including the delimiter), if any. -// /// -// /// This function should be called until it returns `None` before waiting for callback again. -// /// Otherwise, it may be possible that the platform doesn't notify for new data if the existing -// /// data has not been read. -// pub fn next_frame(&mut self) -> Option> { -// if self.frame.is_none() && self.listener.is_notified() { -// self.flush(); -// } -// self.frame.map(|len| { -// let mut frame = self.buffer.split_off(len + 1); -// core::mem::swap(&mut frame, &mut self.buffer); -// self.frame = self.buffer.iter().position(|&x| x == self.delimiter); -// frame -// }) -// } - -// /// Stops reading and returns the current buffer. -// /// -// /// The buffer may contain multiple delimited frames. -// pub fn stop(self) -> Vec { -// self.buffer -// } - -// fn flush(&mut self) { -// while self.read() {} -// } - -// fn read(&mut self) -> bool { -// let mut data = [0; 32]; -// let len = self.listener.ctap_hid.read(&mut data).unwrap(); -// let pos = self.buffer.len(); -// self.buffer.extend_from_slice(&data[.. len]); -// if self.frame.is_none() { -// for i in pos .. pos + len { -// if self.buffer[i] == self.delimiter { -// self.frame = Some(i); -// break; -// } -// } -// } -// len == data.len() -// } -// } - -// /// Asynchronously reads into the provided buffer. -// /// -// /// If instead you want to continuously read delimited frames, use [`DelimitedReader`]. -// #[must_use] -// pub struct Reader<'a, T: CtapHid>(Updater<'a, T>); - -// impl<'a, T: CtapHid> Reader<'a, T> { -// /// Asynchronously reads from a CtapHid into a buffer. -// pub fn new(ctap_hid: &'a T, buffer: &'a mut [u8]) -> Self { -// Reader(Updater::new(ctap_hid, Kind::Reader { buffer })) -// } - -// /// Returns whether anything has been read (or an error occurred). -// pub fn is_empty(&mut self) -> bool { -// self.0.is_empty() -// } - -// /// Returns whether everything has been read (or an error occurred). -// pub fn is_done(&mut self) -> bool { -// self.0.is_done() -// } - -// /// Returns how many bytes were read (or if an error occurred). -// pub fn result(self) -> Result { -// self.0.result() -// } -// } - -// /// Asynchronously writes from the provided buffer. -// #[must_use] -// pub struct Writer<'a, T: CtapHid>(Updater<'a, T>); - -// impl<'a, T: CtapHid> Writer<'a, T> { -// /// Asynchronously writes from a buffer to a CtapHid. -// pub fn new(ctap_hid: &'a T, buffer: &'a [u8]) -> Self { -// Writer(Updater::new(ctap_hid, Kind::Writer { buffer })) -// } - -// /// Returns whether anything has been written (or an error occurred). -// pub fn is_empty(&mut self) -> bool { -// self.0.is_empty() -// } - -// /// Returns whether everything has been written (or an error occurred). -// pub fn is_done(&mut self) -> bool { -// self.0.is_done() -// } - -// /// Returns how many bytes were written (or if an error occurred). -// pub fn result(self) -> Result { -// self.0.result() -// } -// } - -// struct Updater<'a, T: CtapHid> { -// // The listener is alive as long as not done (see `should_listen()`). -// listener: Option>, -// kind: Kind<'a>, -// result: Result, -// } - -// impl<'a, T: CtapHid> Updater<'a, T> { -// fn new(ctap_hid: &'a T, kind: Kind<'a>) -> Self { -// let event = kind.event(); -// let mut result = Updater { listener: None, kind, result: Ok(0) }; -// if result.should_listen() { -// result.listener = Some(Listener::new(ctap_hid, event)); -// } -// let _ = result.update(); -// result -// } - -// fn is_empty(&mut self) -> bool { -// matches!(self.update(), Ok(0)) -// } - -// fn is_done(&mut self) -> bool { -// let _ = self.update(); -// self.listener.is_none() -// } - -// fn result(mut self) -> Result { -// self.update() -// } - -// fn update(&mut self) -> Result { -// let listener = match &mut self.listener { -// Some(x) => x, -// None => return self.result, -// }; -// if !listener.is_notified() { -// return self.result; -// } -// let pos = self.result.as_mut().unwrap(); -// match self.kind.update(listener.ctap_hid, *pos) { -// Ok(len) => *pos += len, -// err => self.result = err, -// } -// if !self.should_listen() { -// self.listener = None; -// } -// self.result -// } - -// fn should_listen(&self) -> bool { -// matches!(self.result, Ok(len) if len < self.kind.len()) -// } -// } - -// enum Kind<'a> { -// Reader { buffer: &'a mut [u8] }, -// Writer { buffer: &'a [u8] }, -// } - -// impl<'a> Kind<'a> { -// fn event(&self) -> Event { -// match self { -// Kind::Reader { .. } => Event::Read, -// Kind::Writer { .. } => Event::Write, -// } -// } - -// fn len(&self) -> usize { -// match self { -// Kind::Reader { buffer } => buffer.len(), -// Kind::Writer { buffer } => buffer.len(), -// } -// } - -// fn update(&mut self, ctap_hid: &impl CtapHid, pos: usize) -> Result { -// match self { -// Kind::Reader { buffer } => ctap_hid.read(&mut buffer[pos ..]), -// Kind::Writer { buffer } => ctap_hid.write(&buffer[pos ..]), -// } -// } -// } +/// Writes at least one byte from a buffer to a CtapHid. +/// +/// This function will block if necessary. +pub fn write_any(ctap_hid: &T, buffer: &[u8]) -> Result { + let mut writer = Writer::new(ctap_hid, buffer); + scheduling::wait_until(|| !writer.is_empty()); + writer.result() +} + +/// Writes from a buffer to a CtapHid until everything has been written. +/// +/// This function will block if necessary. +pub fn write_all(ctap_hid: &T, buffer: &[u8]) -> Result<(), Error> { + let mut writer = Writer::new(ctap_hid, buffer); + scheduling::wait_until(|| writer.is_done()); + writer.result()?; + Ok(()) +} + +/// Flushes the CtapHid (in case reads or writes are buffered). +pub fn flush(ctap_hid: &T) -> Result<(), Error> { + ctap_hid.flush() +} + +/// Asynchronously listens for event notifications. +pub struct Listener<'a, T: CtapHid> { + ctap_hid: &'a T, + event: Event, + notified: &'static Cell, +} + +impl<'a, T: CtapHid> Listener<'a, T> { + /// Starts listening for the provided event until dropped. + pub fn new(ctap_hid: &'a T, event: Event) -> Self { + let notified = Box::leak(Box::new(Cell::new(true))); + let func = Self::call; + let data = notified as *mut _ as *const u8; + unsafe { ctap_hid.register(event, func, data) }.unwrap(); + Listener { ctap_hid, event, notified } + } + + /// Returns whether the event triggered since the last call. + pub fn is_notified(&mut self) -> bool { + self.notified.replace(false) + } + + extern "C" fn call(data: *const u8) { + let notified = unsafe { &*(data as *const Cell) }; + notified.set(true); + } +} + +impl<'a, T: CtapHid> Drop for Listener<'a, T> { + fn drop(&mut self) { + self.ctap_hid.unregister(self.event).unwrap(); + drop(unsafe { Box::from_raw(self.notified.as_ptr()) }); + } +} + +/// Asynchronously reads delimited frames. +/// +/// If you want to read at most a given amount instead, use [`Reader`]. +pub struct DelimitedReader<'a, T: CtapHid> { + listener: Listener<'a, T>, + buffer: Vec, + frame: Option, // index of first delimiter in buffer, if any + delimiter: u8, +} + +impl<'a, T: CtapHid> DelimitedReader<'a, T> { + /// Starts reading delimited frames from a CtapHid. + pub fn new(ctap_hid: &'a T, delimiter: u8) -> Self { + let listener = Listener::new(ctap_hid, Event::Read); + DelimitedReader { listener, buffer: Vec::new(), frame: None, delimiter } + } + + /// Returns the next delimited frame (including the delimiter), if any. + /// + /// This function should be called until it returns `None` before waiting for callback again. + /// Otherwise, it may be possible that the platform doesn't notify for new data if the existing + /// data has not been read. + pub fn next_frame(&mut self) -> Option> { + if self.frame.is_none() && self.listener.is_notified() { + self.flush(); + } + self.frame.map(|len| { + let mut frame = self.buffer.split_off(len + 1); + core::mem::swap(&mut frame, &mut self.buffer); + self.frame = self.buffer.iter().position(|&x| x == self.delimiter); + frame + }) + } + + /// Stops reading and returns the current buffer. + /// + /// The buffer may contain multiple delimited frames. + pub fn stop(self) -> Vec { + self.buffer + } + + fn flush(&mut self) { + while self.read() {} + } + + fn read(&mut self) -> bool { + let mut data = [0; 32]; + let len = self.listener.ctap_hid.read(&mut data).unwrap(); + let pos = self.buffer.len(); + self.buffer.extend_from_slice(&data[.. len]); + if self.frame.is_none() { + for i in pos .. pos + len { + if self.buffer[i] == self.delimiter { + self.frame = Some(i); + break; + } + } + } + len == data.len() + } +} + +/// Asynchronously reads into the provided buffer. +/// +/// If instead you want to continuously read delimited frames, use [`DelimitedReader`]. +#[must_use] +pub struct Reader<'a, T: CtapHid>(Updater<'a, T>); + +impl<'a, T: CtapHid> Reader<'a, T> { + /// Asynchronously reads from a CtapHid into a buffer. + pub fn new(ctap_hid: &'a T, buffer: &'a mut [u8]) -> Self { + Reader(Updater::new(ctap_hid, Kind::Reader { buffer })) + } + + /// Returns whether anything has been read (or an error occurred). + pub fn is_empty(&mut self) -> bool { + self.0.is_empty() + } + + /// Returns whether everything has been read (or an error occurred). + pub fn is_done(&mut self) -> bool { + self.0.is_done() + } + + /// Returns how many bytes were read (or if an error occurred). + pub fn result(self) -> Result { + self.0.result() + } +} + +/// Asynchronously writes from the provided buffer. +#[must_use] +pub struct Writer<'a, T: CtapHid>(Updater<'a, T>); + +impl<'a, T: CtapHid> Writer<'a, T> { + /// Asynchronously writes from a buffer to a CtapHid. + pub fn new(ctap_hid: &'a T, buffer: &'a [u8]) -> Self { + Writer(Updater::new(ctap_hid, Kind::Writer { buffer })) + } + + /// Returns whether anything has been written (or an error occurred). + pub fn is_empty(&mut self) -> bool { + self.0.is_empty() + } + + /// Returns whether everything has been written (or an error occurred). + pub fn is_done(&mut self) -> bool { + self.0.is_done() + } + + /// Returns how many bytes were written (or if an error occurred). + pub fn result(self) -> Result { + self.0.result() + } +} + +struct Updater<'a, T: CtapHid> { + // The listener is alive as long as not done (see `should_listen()`). + listener: Option>, + kind: Kind<'a>, + result: Result, +} + +impl<'a, T: CtapHid> Updater<'a, T> { + fn new(ctap_hid: &'a T, kind: Kind<'a>) -> Self { + let event = kind.event(); + let mut result = Updater { listener: None, kind, result: Ok(0) }; + if result.should_listen() { + result.listener = Some(Listener::new(ctap_hid, event)); + } + let _ = result.update(); + result + } + + fn is_empty(&mut self) -> bool { + matches!(self.update(), Ok(0)) + } + + fn is_done(&mut self) -> bool { + let _ = self.update(); + self.listener.is_none() + } + + fn result(mut self) -> Result { + self.update() + } + + fn update(&mut self) -> Result { + let listener = match &mut self.listener { + Some(x) => x, + None => return self.result, + }; + if !listener.is_notified() { + return self.result; + } + let pos = self.result.as_mut().unwrap(); + match self.kind.update(listener.ctap_hid, *pos) { + Ok(len) => *pos += len, + err => self.result = err, + } + if !self.should_listen() { + self.listener = None; + } + self.result + } + + fn should_listen(&self) -> bool { + matches!(self.result, Ok(len) if len < self.kind.len()) + } +} + +enum Kind<'a> { + Reader { buffer: &'a mut [u8] }, + Writer { buffer: &'a [u8] }, +} + +impl<'a> Kind<'a> { + fn event(&self) -> Event { + match self { + Kind::Reader { .. } => Event::Read, + Kind::Writer { .. } => Event::Write, + } + } + + fn len(&self) -> usize { + match self { + Kind::Reader { buffer } => buffer.len(), + Kind::Writer { buffer } => buffer.len(), + } + } + + fn update(&mut self, ctap_hid: &impl CtapHid, pos: usize) -> Result { + match self { + Kind::Reader { buffer } => ctap_hid.read(&mut buffer[pos ..]), + Kind::Writer { buffer } => ctap_hid.write(&buffer[pos ..]), + } + } +} diff --git a/crates/prelude/src/usb.rs b/crates/prelude/src/usb.rs index d92e3605..67c8727b 100644 --- a/crates/prelude/src/usb.rs +++ b/crates/prelude/src/usb.rs @@ -20,5 +20,5 @@ #[cfg(feature = "api-usb-serial")] pub mod serial; -// TODO: Add feature +#[cfg(feature = "api-usb-ctap")] pub mod ctap; From 78dc5094622580a77ff0d2938238ad345e49aa7e Mon Sep 17 00:00:00 2001 From: Surya Midatala Date: Fri, 9 Aug 2024 14:47:01 +0000 Subject: [PATCH 3/4] Use ctap-hid-fido2 crate instead --- crates/api-desc/Cargo.toml | 2 + crates/api-desc/src/usb.rs | 5 + crates/api-desc/src/usb/ctap.rs | 79 ++++ crates/api-macro/Cargo.toml | 1 + crates/api/Cargo.toml | 2 + crates/board/Cargo.lock | 583 +++++++++++++++++++++++++++++- crates/board/Cargo.toml | 3 + crates/board/src/usb.rs | 5 + crates/board/src/usb/ctap.rs | 75 +++- crates/prelude/Cargo.toml | 3 +- crates/prelude/src/ctap.rs | 369 ------------------- crates/prelude/src/lib.rs | 2 - crates/prelude/src/usb/ctap.rs | 369 +++++++++++++++++-- crates/runner-host/Cargo.toml | 1 + crates/runner-nordic/Cargo.toml | 1 + crates/scheduler/Cargo.toml | 8 + crates/scheduler/src/call/usb.rs | 4 + crates/scheduler/src/event/usb.rs | 9 + 18 files changed, 1100 insertions(+), 421 deletions(-) create mode 100644 crates/api-desc/src/usb/ctap.rs delete mode 100644 crates/prelude/src/ctap.rs diff --git a/crates/api-desc/Cargo.toml b/crates/api-desc/Cargo.toml index 8960b408..032ec648 100644 --- a/crates/api-desc/Cargo.toml +++ b/crates/api-desc/Cargo.toml @@ -38,6 +38,7 @@ api-store = ["internal-api-store"] api-store-fragment = ["internal-api-store"] api-timer = [] api-uart = [] +api-usb-ctap = ["internal-api-usb"] api-usb-serial = ["internal-api-usb"] # Enables all API features. full-api = [ @@ -59,6 +60,7 @@ full-api = [ "api-store-fragment", "api-timer", "api-uart", + "api-usb-ctap", "api-usb-serial", ] # Internal features. diff --git a/crates/api-desc/src/usb.rs b/crates/api-desc/src/usb.rs index 29ff5619..e6435190 100644 --- a/crates/api-desc/src/usb.rs +++ b/crates/api-desc/src/usb.rs @@ -17,6 +17,9 @@ use crate::*; #[cfg(feature = "api-usb-serial")] mod serial; +#[cfg(feature = "api-usb-ctap")] +mod ctap; + pub(crate) fn new() -> Item { let docs = docs! { /// USB operations. @@ -25,6 +28,8 @@ pub(crate) fn new() -> Item { let items = vec![ #[cfg(feature = "api-usb-serial")] serial::new(), + #[cfg(feature = "api-usb-ctap")] + ctap::new(), ]; Item::Mod(Mod { docs, name, items }) } diff --git a/crates/api-desc/src/usb/ctap.rs b/crates/api-desc/src/usb/ctap.rs new file mode 100644 index 00000000..609d93cb --- /dev/null +++ b/crates/api-desc/src/usb/ctap.rs @@ -0,0 +1,79 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::*; + +pub(crate) fn new() -> Item { + let docs = docs!(); + let name = "ctap".into(); + let items = vec![ + item! { + /// Reads from USB serial into a buffer. + /// + /// Returns the number of bytes read. This function does not block and may return zero. + fn read "usr" { + /// Address of the buffer. + ptr: *mut u8, + + /// Length of the buffer in bytes. + len: usize, + } -> usize + }, + item! { + /// Writes to USB serial from a buffer. + /// + /// Returns the number of bytes written. This function does not block and may return + /// zero. + fn write "usw" { + /// Address of the buffer. + ptr: *const u8, + + /// Length of the buffer in bytes. + len: usize, + } -> usize + }, + item! { + /// USB serial events. + enum Event { + /// Ready for read. + Read = 0, + /// Ready for write. + Write = 1, + } + }, + item! { + /// Registers a callback when USB serial is ready. + /// + /// It is possible that the callback is spuriously called. The callback is only + /// guaranteed to be called after the associated operation processed less bytes than the + /// buffer size. + fn register "use" { + event: usize, + handler_func: fn { data: *const void }, + handler_data: *const void, + } -> () + }, + item! { + /// Unregisters a callback. + fn unregister "usd" { + event: usize, + } -> () + }, + item! { + /// Flushs the USB serial. + fn flush "usf" {} -> () + }, + ]; + Item::Mod(Mod { docs, name, items }) +} diff --git a/crates/api-macro/Cargo.toml b/crates/api-macro/Cargo.toml index 9522c31a..d30d82d2 100644 --- a/crates/api-macro/Cargo.toml +++ b/crates/api-macro/Cargo.toml @@ -36,6 +36,7 @@ api-store = ["wasefire-applet-api-desc/api-store"] api-store-fragment = ["wasefire-applet-api-desc/api-store-fragment"] api-timer = ["wasefire-applet-api-desc/api-timer"] api-uart = ["wasefire-applet-api-desc/api-uart"] +api-usb-ctap = ["wasefire-applet-api-desc/api-usb-ctap"] api-usb-serial = ["wasefire-applet-api-desc/api-usb-serial"] [lints] diff --git a/crates/api/Cargo.toml b/crates/api/Cargo.toml index db848202..d2f0de94 100644 --- a/crates/api/Cargo.toml +++ b/crates/api/Cargo.toml @@ -45,6 +45,7 @@ api-store = ["wasefire-applet-api-macro/api-store"] api-store-fragment = ["wasefire-applet-api-macro/api-store-fragment"] api-timer = ["wasefire-applet-api-macro/api-timer"] api-uart = ["wasefire-applet-api-macro/api-uart"] +api-usb-ctap = ["wasefire-applet-api-macro/api-usb-ctap"] api-usb-serial = ["wasefire-applet-api-macro/api-usb-serial"] # Enables all API features (unstable for host). full-api = [ @@ -66,6 +67,7 @@ full-api = [ "api-store-fragment", "api-timer", "api-uart", + "api-usb-ctap", "api-usb-serial", ] # Implements the API with weak symbols to permit custom definitions (only diff --git a/crates/board/Cargo.lock b/crates/board/Cargo.lock index c2d69d75..77c8b272 100644 --- a/crates/board/Cargo.lock +++ b/crates/board/Cargo.lock @@ -37,12 +37,69 @@ dependencies = [ "subtle", ] +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "asn1-rs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + [[package]] name = "base16ct" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bitflags" version = "1.3.2" @@ -58,6 +115,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + [[package]] name = "bytemuck" version = "1.16.0" @@ -84,6 +150,21 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + +[[package]] +name = "cc" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "504bdec147f2cc13c8b57ed9401fd8a147cc66b67ad5cb241394244f2c947549" + [[package]] name = "ccm" version = "0.5.0" @@ -149,6 +230,29 @@ dependencies = [ "typenum", ] +[[package]] +name = "ctap-hid-fido2" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6473a333d82796d5b23529fde19386de33820ad19d368402f8e9de780e1723" +dependencies = [ + "aes", + "anyhow", + "base64", + "byteorder", + "cbc", + "hex", + "hidapi", + "num", + "pad", + "ring", + "serde", + "serde_cbor", + "strum", + "strum_macros", + "x509-parser", +] + [[package]] name = "ctr" version = "0.9.2" @@ -226,6 +330,29 @@ dependencies = [ "zeroize", ] +[[package]] +name = "der-parser" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + [[package]] name = "derivative" version = "2.2.0" @@ -249,6 +376,17 @@ dependencies = [ "subtle", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "ecdsa" version = "0.16.9" @@ -317,6 +455,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "ghash" version = "0.5.1" @@ -338,6 +487,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "half" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" + [[package]] name = "hash32" version = "0.3.1" @@ -363,6 +518,30 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hidapi" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "798154e4b6570af74899d71155fb0072d5b17e6aa12f39c8ef22c60fb8ec99e7" +dependencies = [ + "cc", + "libc", + "pkg-config", + "winapi", +] + [[package]] name = "hmac" version = "0.12.1" @@ -378,9 +557,22 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ + "block-padding", "generic-array", ] +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" version = "0.2.155" @@ -393,6 +585,18 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "nb" version = "0.1.3" @@ -408,6 +612,95 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "num_enum" version = "0.7.2" @@ -428,6 +721,15 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "oid-registry" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c958dd45046245b9c3c2547369bb634eb461670b2e7e0de552905801a648d1d" +dependencies = [ + "asn1-rs", +] + [[package]] name = "opaque-debug" version = "0.3.1" @@ -458,6 +760,21 @@ dependencies = [ "sha2", ] +[[package]] +name = "pad" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ad9b889f1b12e0b9ee24db044b5129150d5eada288edc800f789928dc8c0e3" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + [[package]] name = "polyval" version = "0.6.2" @@ -476,6 +793,12 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "primeorder" version = "0.13.6" @@ -543,13 +866,43 @@ dependencies = [ "subtle", ] +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + [[package]] name = "sealed" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4a8caec23b7800fb97971a1c6ae365b6239aaeddfb934d6265f8505e795699d" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "syn 2.0.66", @@ -568,6 +921,36 @@ dependencies = [ "zeroize", ] +[[package]] +name = "serde" +version = "1.0.205" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.205" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "sha2" version = "0.10.8" @@ -589,12 +972,37 @@ dependencies = [ "rand_core", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.66", +] + [[package]] name = "subtle" version = "2.5.0" @@ -623,6 +1031,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "thiserror" version = "1.0.61" @@ -643,6 +1062,37 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "typenum" version = "1.17.0" @@ -655,6 +1105,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-width" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" + [[package]] name = "universal-hash" version = "0.5.1" @@ -665,6 +1121,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "usb-device" version = "0.3.2" @@ -737,6 +1199,7 @@ dependencies = [ "bytemuck", "ccm", "crypto-common", + "ctap-hid-fido2", "defmt", "derivative", "digest", @@ -776,6 +1239,124 @@ dependencies = [ name = "wasefire-store" version = "0.2.4" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "x509-parser" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror", + "time", +] + [[package]] name = "zeroize" version = "1.7.0" diff --git a/crates/board/Cargo.toml b/crates/board/Cargo.toml index 956f2395..f726bb15 100644 --- a/crates/board/Cargo.toml +++ b/crates/board/Cargo.toml @@ -21,6 +21,7 @@ aes-gcm = { version = "0.10.3", default-features = false, optional = true } bytemuck = { version = "1.16.0", default-features = false, optional = true } ccm = { version = "0.5.0", default-features = false, optional = true } crypto-common = { version = "0.1.6", default-features = false, optional = true } +ctap-hid-fido2 = { version = "3.5.1", default-features = false, optional = true } defmt = { version = "0.3.8", default-features = false, optional = true } derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } digest = { version = "0.10.7", default-features = false, optional = true } @@ -70,6 +71,7 @@ api-rng = [] api-storage = ["dep:wasefire-store"] api-timer = [] api-uart = [] +api-usb-ctap = ["dep:ctap-hid-fido2", "dep:usb-device", "internal-api-usb"] api-usb-serial = ["dep:usb-device", "dep:usbd-serial", "internal-api-usb"] # Enables all API features (unstable). full-api = [ @@ -92,6 +94,7 @@ full-api = [ "api-storage", "api-timer", "api-uart", + "api-usb-ctap", "api-usb-serial", ] # Software crypto features. DO NOT enable those features if you also depend on diff --git a/crates/board/src/usb.rs b/crates/board/src/usb.rs index 7c1519c2..a832a3b8 100644 --- a/crates/board/src/usb.rs +++ b/crates/board/src/usb.rs @@ -27,6 +27,8 @@ pub enum Event { #[cfg(feature = "api-usb-serial")] Serial(serial::Event), + /// Ctap event. + #[cfg(feature = "api-usb-ctap")] Ctap(ctap::Event), } @@ -42,6 +44,8 @@ pub trait Api: Send { #[cfg(feature = "api-usb-serial")] type Serial: serial::Api; + /// CTAP serial interface. + #[cfg(feature = "api-usb-ctap")] type Ctap: ctap::Api; } @@ -49,4 +53,5 @@ pub trait Api: Send { #[cfg(feature = "api-usb-serial")] pub type Serial = as Api>::Serial; +#[cfg(feature = "api-usb-ctap")] pub type Ctap = as Api>::Ctap; diff --git a/crates/board/src/usb/ctap.rs b/crates/board/src/usb/ctap.rs index 00bca282..cbb5cd4e 100644 --- a/crates/board/src/usb/ctap.rs +++ b/crates/board/src/usb/ctap.rs @@ -14,8 +14,9 @@ //! CTAP HID interface. -use usb_device::bus::UsbBus; -use usbd_ctaphid::CtapHid; +use ctap_hid_fido2::{Cfg, FidoKeyHid, HidParam}; +use usb_device::device::{Config, UsbDevice}; +use usbip_device::UsbIpBus; use super::serial::{self, HasSerial}; @@ -49,37 +50,83 @@ pub trait Api: Send { /// /// Returns the number of bytes written. It could be zero if the other side is not ready. fn write(input: &[u8; 64]) -> Result; + + /// Enables a given event to be triggered. + fn enable(event: &Event) -> Result<(), Error>; + + /// Disables a given event from being triggered. + fn disable(event: &Event) -> Result<(), Error>; } +// Helper trait for boards using the `ctap_hid_fido2` crate. pub trait HasCtapHid: Send { - type UsbBus: UsbBus; + type UsbDevice: UsbDevice<'static, UsbIpBus>; - fn with_ctaphid(f: impl FnOnce(&mut CtapHid) -> R) -> R; + fn with_ctaphid(f: impl FnOnce(&mut CtapHid) -> R) -> R; } -/// Wrapper type for boards using the `usbd_ctaphid` crate. +/// Wrapper type for boards using the `ctap_hid_fido2` crate. pub struct WithCtapHid { _never: !, _has_ctap: T, } -/// Helper struct for boards using the `usbd_ctaphid` crate. -pub struct CtapHid<'a, T: UsbBus> { - ctap_hid: usbd_ctaphid::CtapHid, +/// Helper struct for boards using the `ctap_hid_fido2` crate. +pub struct CtapHid { + ctap_hid: ctap_hid_fido2::FidoKeyHid, read_enabled: bool, write_enabled: bool, } +impl CtapHid { + pub fn new(config: Config) -> Self { + params = HidParam::VidPid { vid: config.vendor_id, pid: config.product_id }; + ctap_hid = FidoKeyHid::new(params, &Cfg::init()); + Self { ctap_hid, read_enabled: false, write_enabled: false } + } + + pub fn set(&mut self, event: &Event, enabled: bool) { + match event { + Event::Read => self.read_enabled = enabled, + Event::Write => self.write_enabled = enabled, + } + } +} + impl Api for WithCtapHid { - fn read(output: &mut [u8]) -> Result { - match T::with_ctaphid(|ctap_hid| ctap_hid.pipe.read_and_handle_packet()) { - // todo!() + fn read(output: &mut [u8; 64]) -> Result { + match T::with_ctaphid(|ctap_hid| ctap_hid.read()) { + Ok(len) => { + log::trace!("{}{:?} = read({})", len, &output[.. len], output.len()); + Ok(len) + } + Err(e) => { + log::debug!("{} = read({})", log::Debug2Format(&e), output.len()); + Err(Error::world(0)) + } } } - fn write(input: &[u8]) -> Result { - match T::with_ctaphid(|ctap_hid| ctap_hid.check_for_app_response()) { - // todo!() + fn write(input: &[u8; 64]) -> Result { + match T::with_ctaphid(|ctap_hid| ctap_hid.write(input)) { + Ok(len) => { + log::trace!("{} = write({}{:?})", len, input.len(), input); + Ok(len) + } + Err(e) => { + log::debug!("{} = write({}{:?})", log::Debug2Format(&e), input.len(), input); + Err(Error::world(0)) + } } } + + fn enable(event: &Event) -> Result<(), Error> { + T::with_ctaphid(|ctap_hid| ctap_hid.set(event, true)); + Ok(()) + } + + fn disable(event: &Event) -> Result<(), Error> { + T::with_ctaphid(|ctap_hid| ctap_hid.set(event, false)); + Ok(()) + } } diff --git a/crates/prelude/Cargo.toml b/crates/prelude/Cargo.toml index 29cab407..891f5628 100644 --- a/crates/prelude/Cargo.toml +++ b/crates/prelude/Cargo.toml @@ -61,7 +61,7 @@ api-store = ["internal-api-store", "wasefire-applet-api/api-store"] api-store-fragment = ["internal-api-store", "wasefire-applet-api/api-store-fragment"] api-timer = ["wasefire-applet-api/api-timer"] api-uart = ["internal-serial", "wasefire-applet-api/api-uart"] -api-usb-ctap = ["internal-api-usb", "internal-serial", "wasefire-applet-api/api-usb-ctap"] +api-usb-ctap = ["internal-api-usb", "wasefire-applet-api/api-usb-ctap"] api-usb-serial = ["internal-api-usb", "internal-serial", "wasefire-applet-api/api-usb-serial"] # Enables all API features. full-api = [ @@ -83,6 +83,7 @@ full-api = [ "api-store-fragment", "api-timer", "api-uart", + "api-usb-ctap", "api-usb-serial", ] # Implements RustCrypto traits. diff --git a/crates/prelude/src/ctap.rs b/crates/prelude/src/ctap.rs deleted file mode 100644 index 70d33653..00000000 --- a/crates/prelude/src/ctap.rs +++ /dev/null @@ -1,369 +0,0 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Provides an API for CTAP HID interface. - -use alloc::boxed::Box; -use alloc::vec::Vec; -use core::cell::Cell; -use core::fmt::Debug; - -use sealed::sealed; - -use crate::{scheduling, Error}; - -/// CtapHid events to be notified. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum Event { - /// The CtapHid may be ready to read. - Read, - - /// The CtapHid may be ready to write. - Write, -} - -/// Provides high-level CtapHid API from low-level API. -/// -/// This trait should only be implemented by the prelude and is thus sealed. Its purpose is to -/// provide a unique interface to the different CtapHids. -#[sealed(pub(crate))] -pub trait CtapHid { - /// Reads from the CtapHid into a buffer without blocking. - /// - /// Returns how many bytes were read (and thus written to the buffer). This function does not - /// block, so if there are no data available for read, zero is returned. - fn read(&self, buffer: &mut [u8; 64]) -> Result; - - /// Writes from a buffer to the CtapHid. - /// - /// Returns how many bytes were written (and thus read from the buffer). This function does not - /// block, so if the CtapHid is not ready for write, zero is returned. - fn write(&self, buffer: &[u8; 64]) -> Result; - - /// Flushes the CtapHid (in case reads or writes are buffered). - fn flush(&self) -> Result<(), Error>; - - /// Registers a callback for an event. - /// - /// # Safety - /// - /// The function pointer and data must live until unregistered. The function must support - /// concurrent calls. - unsafe fn register( - &self, event: Event, func: extern "C" fn(*const u8), data: *const u8, - ) -> Result<(), Error>; - - /// Unregisters the callback for an event. - fn unregister(&self, event: Event) -> Result<(), Error>; -} - -/// Reads from the CtapHid into a buffer without blocking. -/// -/// Returns how many bytes were read (and thus written to the buffer). This function does not -/// block, so if there are no data available for read, zero is returned. -pub fn read(ctap_hid: &T, buffer: &mut [u8]) -> Result { - ctap_hid.read(buffer) -} - -/// Synchronously reads at least one byte from a CtapHid into a buffer. -/// -/// This function will block if necessary. -pub fn read_any(ctap_hid: &T, buffer: &mut [u8]) -> Result { - let mut reader = Reader::new(ctap_hid, buffer); - scheduling::wait_until(|| !reader.is_empty()); - reader.result() -} - -/// Synchronously reads from a CtapHid into a buffer until it is filled. -/// -/// This function will block if necessary. -pub fn read_all(ctap_hid: &T, buffer: &mut [u8]) -> Result<(), Error> { - let mut reader = Reader::new(ctap_hid, buffer); - scheduling::wait_until(|| reader.is_done()); - reader.result()?; - Ok(()) -} - -/// Synchronously reads exactly one byte. -pub fn read_byte(ctap_hid: &T) -> Result { - let mut byte = 0; - read_any(ctap_hid, core::slice::from_mut(&mut byte))?; - Ok(byte) -} - -/// Writes from a buffer to the CtapHid. -/// -/// Returns how many bytes were written (and thus read from the buffer). This function does not -/// block, so if the CtapHid is not ready for write, zero is returned. -pub fn write(ctap_hid: &T, buffer: &[u8]) -> Result { - ctap_hid.write(buffer) -} - -/// Writes at least one byte from a buffer to a CtapHid. -/// -/// This function will block if necessary. -pub fn write_any(ctap_hid: &T, buffer: &[u8]) -> Result { - let mut writer = Writer::new(ctap_hid, buffer); - scheduling::wait_until(|| !writer.is_empty()); - writer.result() -} - -/// Writes from a buffer to a CtapHid until everything has been written. -/// -/// This function will block if necessary. -pub fn write_all(ctap_hid: &T, buffer: &[u8]) -> Result<(), Error> { - let mut writer = Writer::new(ctap_hid, buffer); - scheduling::wait_until(|| writer.is_done()); - writer.result()?; - Ok(()) -} - -/// Flushes the CtapHid (in case reads or writes are buffered). -pub fn flush(ctap_hid: &T) -> Result<(), Error> { - ctap_hid.flush() -} - -/// Asynchronously listens for event notifications. -pub struct Listener<'a, T: CtapHid> { - ctap_hid: &'a T, - event: Event, - notified: &'static Cell, -} - -impl<'a, T: CtapHid> Listener<'a, T> { - /// Starts listening for the provided event until dropped. - pub fn new(ctap_hid: &'a T, event: Event) -> Self { - let notified = Box::leak(Box::new(Cell::new(true))); - let func = Self::call; - let data = notified as *mut _ as *const u8; - unsafe { ctap_hid.register(event, func, data) }.unwrap(); - Listener { ctap_hid, event, notified } - } - - /// Returns whether the event triggered since the last call. - pub fn is_notified(&mut self) -> bool { - self.notified.replace(false) - } - - extern "C" fn call(data: *const u8) { - let notified = unsafe { &*(data as *const Cell) }; - notified.set(true); - } -} - -impl<'a, T: CtapHid> Drop for Listener<'a, T> { - fn drop(&mut self) { - self.ctap_hid.unregister(self.event).unwrap(); - drop(unsafe { Box::from_raw(self.notified.as_ptr()) }); - } -} - -/// Asynchronously reads delimited frames. -/// -/// If you want to read at most a given amount instead, use [`Reader`]. -pub struct DelimitedReader<'a, T: CtapHid> { - listener: Listener<'a, T>, - buffer: Vec, - frame: Option, // index of first delimiter in buffer, if any - delimiter: u8, -} - -impl<'a, T: CtapHid> DelimitedReader<'a, T> { - /// Starts reading delimited frames from a CtapHid. - pub fn new(ctap_hid: &'a T, delimiter: u8) -> Self { - let listener = Listener::new(ctap_hid, Event::Read); - DelimitedReader { listener, buffer: Vec::new(), frame: None, delimiter } - } - - /// Returns the next delimited frame (including the delimiter), if any. - /// - /// This function should be called until it returns `None` before waiting for callback again. - /// Otherwise, it may be possible that the platform doesn't notify for new data if the existing - /// data has not been read. - pub fn next_frame(&mut self) -> Option> { - if self.frame.is_none() && self.listener.is_notified() { - self.flush(); - } - self.frame.map(|len| { - let mut frame = self.buffer.split_off(len + 1); - core::mem::swap(&mut frame, &mut self.buffer); - self.frame = self.buffer.iter().position(|&x| x == self.delimiter); - frame - }) - } - - /// Stops reading and returns the current buffer. - /// - /// The buffer may contain multiple delimited frames. - pub fn stop(self) -> Vec { - self.buffer - } - - fn flush(&mut self) { - while self.read() {} - } - - fn read(&mut self) -> bool { - let mut data = [0; 32]; - let len = self.listener.ctap_hid.read(&mut data).unwrap(); - let pos = self.buffer.len(); - self.buffer.extend_from_slice(&data[.. len]); - if self.frame.is_none() { - for i in pos .. pos + len { - if self.buffer[i] == self.delimiter { - self.frame = Some(i); - break; - } - } - } - len == data.len() - } -} - -/// Asynchronously reads into the provided buffer. -/// -/// If instead you want to continuously read delimited frames, use [`DelimitedReader`]. -#[must_use] -pub struct Reader<'a, T: CtapHid>(Updater<'a, T>); - -impl<'a, T: CtapHid> Reader<'a, T> { - /// Asynchronously reads from a CtapHid into a buffer. - pub fn new(ctap_hid: &'a T, buffer: &'a mut [u8]) -> Self { - Reader(Updater::new(ctap_hid, Kind::Reader { buffer })) - } - - /// Returns whether anything has been read (or an error occurred). - pub fn is_empty(&mut self) -> bool { - self.0.is_empty() - } - - /// Returns whether everything has been read (or an error occurred). - pub fn is_done(&mut self) -> bool { - self.0.is_done() - } - - /// Returns how many bytes were read (or if an error occurred). - pub fn result(self) -> Result { - self.0.result() - } -} - -/// Asynchronously writes from the provided buffer. -#[must_use] -pub struct Writer<'a, T: CtapHid>(Updater<'a, T>); - -impl<'a, T: CtapHid> Writer<'a, T> { - /// Asynchronously writes from a buffer to a CtapHid. - pub fn new(ctap_hid: &'a T, buffer: &'a [u8]) -> Self { - Writer(Updater::new(ctap_hid, Kind::Writer { buffer })) - } - - /// Returns whether anything has been written (or an error occurred). - pub fn is_empty(&mut self) -> bool { - self.0.is_empty() - } - - /// Returns whether everything has been written (or an error occurred). - pub fn is_done(&mut self) -> bool { - self.0.is_done() - } - - /// Returns how many bytes were written (or if an error occurred). - pub fn result(self) -> Result { - self.0.result() - } -} - -struct Updater<'a, T: CtapHid> { - // The listener is alive as long as not done (see `should_listen()`). - listener: Option>, - kind: Kind<'a>, - result: Result, -} - -impl<'a, T: CtapHid> Updater<'a, T> { - fn new(ctap_hid: &'a T, kind: Kind<'a>) -> Self { - let event = kind.event(); - let mut result = Updater { listener: None, kind, result: Ok(0) }; - if result.should_listen() { - result.listener = Some(Listener::new(ctap_hid, event)); - } - let _ = result.update(); - result - } - - fn is_empty(&mut self) -> bool { - matches!(self.update(), Ok(0)) - } - - fn is_done(&mut self) -> bool { - let _ = self.update(); - self.listener.is_none() - } - - fn result(mut self) -> Result { - self.update() - } - - fn update(&mut self) -> Result { - let listener = match &mut self.listener { - Some(x) => x, - None => return self.result, - }; - if !listener.is_notified() { - return self.result; - } - let pos = self.result.as_mut().unwrap(); - match self.kind.update(listener.ctap_hid, *pos) { - Ok(len) => *pos += len, - err => self.result = err, - } - if !self.should_listen() { - self.listener = None; - } - self.result - } - - fn should_listen(&self) -> bool { - matches!(self.result, Ok(len) if len < self.kind.len()) - } -} - -enum Kind<'a> { - Reader { buffer: &'a mut [u8] }, - Writer { buffer: &'a [u8] }, -} - -impl<'a> Kind<'a> { - fn event(&self) -> Event { - match self { - Kind::Reader { .. } => Event::Read, - Kind::Writer { .. } => Event::Write, - } - } - - fn len(&self) -> usize { - match self { - Kind::Reader { buffer } => buffer.len(), - Kind::Writer { buffer } => buffer.len(), - } - } - - fn update(&mut self, ctap_hid: &impl CtapHid, pos: usize) -> Result { - match self { - Kind::Reader { buffer } => ctap_hid.read(&mut buffer[pos ..]), - Kind::Writer { buffer } => ctap_hid.write(&buffer[pos ..]), - } - } -} diff --git a/crates/prelude/src/lib.rs b/crates/prelude/src/lib.rs index 571827c4..1e95112d 100644 --- a/crates/prelude/src/lib.rs +++ b/crates/prelude/src/lib.rs @@ -51,8 +51,6 @@ pub mod button; mod callback; #[cfg(feature = "internal-api-crypto")] pub mod crypto; -// TODO: Add feature -pub mod ctap; pub mod debug; #[cfg(feature = "api-gpio")] pub mod gpio; diff --git a/crates/prelude/src/usb/ctap.rs b/crates/prelude/src/usb/ctap.rs index bbe3ddd9..85caf823 100644 --- a/crates/prelude/src/usb/ctap.rs +++ b/crates/prelude/src/usb/ctap.rs @@ -14,52 +14,353 @@ //! Provides API for CTAP HID. -use sealed::sealed; +use alloc::boxed::Box; +use alloc::vec::Vec; +use core::cell::Cell; +use core::fmt::Debug; + use wasefire_applet_api::usb::serial as api; -// use crate::ctap::Event; -use crate::{convert, Error}; -// use crate::convert_unit; +use crate::{convert, convert_unit, scheduling, Error}; /// Implements the [`CtapHid`](crate::ctap::CtapHid) interface for USB transport. -pub struct UsbCtapHid; +pub struct CtapHid; -#[sealed] -impl crate::ctap::CtapHid for UsbCtapHid { - fn read(&self, buffer: &mut [u8]) -> Result { +impl CtapHid { + /// Reads from the CtapHid into a buffer without blocking. + /// + /// Returns how many bytes were read (and thus written to the buffer). This function does not + /// block, so if there are no data available for read, zero is returned. + pub fn read(&self, buffer: &mut [u8]) -> Result { let params = api::read::Params { ptr: buffer.as_mut_ptr(), len: buffer.len() }; convert(unsafe { api::read(params) }) } - fn write(&self, buffer: &[u8]) -> Result { + /// Synchronously reads at least one byte from a CtapHid into a buffer. + /// + /// This function will block if necessary. + pub fn read_any(&self, buffer: &mut [u8]) -> Result { + let mut reader = Reader::new(self, buffer); + scheduling::wait_until(|| !reader.is_empty()); + reader.result() + } + + /// Synchronously reads from a CtapHid into a buffer until it is filled. + /// + /// This function will block if necessary. + pub fn read_all(&self, buffer: &mut [u8]) -> Result<(), Error> { + let mut reader = Reader::new(self, buffer); + scheduling::wait_until(|| reader.is_done()); + reader.result()?; + Ok(()) + } + + /// Synchronously reads exactly one byte. + pub fn read_byte(&self) -> Result { + let mut byte = 0; + self.read_any(core::slice::from_mut(&mut byte))?; + Ok(byte) + } + + /// Writes from a buffer to the CtapHid. + /// + /// Returns how many bytes were written (and thus read from the buffer). This function does not + /// block, so if the CtapHid is not ready for write, zero is returned. + pub fn write(&self, buffer: &[u8]) -> Result { let params = api::write::Params { ptr: buffer.as_ptr(), len: buffer.len() }; convert(unsafe { api::write(params) }) } - // fn flush(&self) -> Result<(), Error> { - // convert_unit(unsafe { api::flush() }) - // } - - // unsafe fn register( - // &self, event: Event, func: extern "C" fn(*const u8), data: *const u8, - // ) -> Result<(), Error> { - // let params = api::register::Params { - // event: convert_event(event) as usize, - // handler_func: func, - // handler_data: data, - // }; - // convert_unit(unsafe { api::register(params) }) - // } - // #[doc = " Unregisters the callback for an event."] - // fn unregister(&self, event: Event) -> Result<(), Error> { - // let params = api::unregister::Params { event: convert_event(event) as usize }; - // convert_unit(unsafe { api::unregister(params) }) - // } + /// Writes at least one byte from a buffer to a CtapHid. + /// + /// This function will block if necessary. + pub fn write_any(&self, buffer: &[u8]) -> Result { + let mut writer = Writer::new(self, buffer); + scheduling::wait_until(|| !writer.is_empty()); + writer.result() + } + + /// Writes from a buffer to a CtapHid until everything has been written. + /// + /// This function will block if necessary. + pub fn write_all(&self, buffer: &[u8]) -> Result<(), Error> { + let mut writer = Writer::new(self, buffer); + scheduling::wait_until(|| writer.is_done()); + writer.result()?; + Ok(()) + } + + /// Registers a callback for an event. + /// + /// # Safety + /// + /// The function pointer and data must live until unregistered. The function must support + /// concurrent calls. + unsafe fn register( + &self, event: Event, func: extern "C" fn(*const u8), data: *const u8, + ) -> Result<(), Error> { + let params = api::register::Params { + event: convert_event(event) as usize, + handler_func: func, + handler_data: data, + }; + convert_unit(unsafe { api::register(params) }) + } + + /// Unregisters the callback for an event. + fn unregister(&self, event: Event) -> Result<(), Error> { + let params = api::unregister::Params { event: convert_event(event) as usize }; + convert_unit(unsafe { api::unregister(params) }) + } +} + +/// CtapHid events to be notified. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Event { + /// The CtapHid may be ready to read. + Read, + + /// The CtapHid may be ready to write. + Write, +} + +fn convert_event(event: Event) -> api::Event { + match event { + Event::Read => api::Event::Read, + Event::Write => api::Event::Write, + } +} + +/// Asynchronously listens for event notifications. +pub struct Listener<'a> { + ctap_hid: &'a CtapHid, + event: Event, + notified: &'static Cell, +} + +impl<'a> Listener<'a> { + /// Starts listening for the provided event until dropped. + pub fn new(ctap_hid: &'a CtapHid, event: Event) -> Self { + let notified = Box::leak(Box::new(Cell::new(true))); + let func = Self::call; + let data = notified as *mut _ as *const u8; + unsafe { ctap_hid.register(event, func, data) }.unwrap(); + Listener { ctap_hid, event, notified } + } + + /// Returns whether the event triggered since the last call. + pub fn is_notified(&mut self) -> bool { + self.notified.replace(false) + } + + extern "C" fn call(data: *const u8) { + let notified = unsafe { &*(data as *const Cell) }; + notified.set(true); + } +} + +impl<'a> Drop for Listener<'a> { + fn drop(&mut self) { + self.ctap_hid.unregister(self.event).unwrap(); + drop(unsafe { Box::from_raw(self.notified.as_ptr()) }); + } +} + +/// Asynchronously reads delimited frames. +/// +/// If you want to read at most a given amount instead, use [`Reader`]. +pub struct DelimitedReader<'a> { + listener: Listener<'a>, + buffer: Vec, + frame: Option, // index of first delimiter in buffer, if any + delimiter: u8, +} + +impl<'a> DelimitedReader<'a> { + /// Starts reading delimited frames from a CtapHid. + pub fn new(ctap_hid: &'a CtapHid, delimiter: u8) -> Self { + let listener = Listener::new(ctap_hid, Event::Read); + DelimitedReader { listener, buffer: Vec::new(), frame: None, delimiter } + } + + /// Returns the next delimited frame (including the delimiter), if any. + /// + /// This function should be called until it returns `None` before waiting for callback again. + /// Otherwise, it may be possible that the platform doesn't notify for new data if the existing + /// data has not been read. + pub fn next_frame(&mut self) -> Option> { + if self.frame.is_none() && self.listener.is_notified() { + self.flush(); + } + self.frame.map(|len| { + let mut frame = self.buffer.split_off(len + 1); + core::mem::swap(&mut frame, &mut self.buffer); + self.frame = self.buffer.iter().position(|&x| x == self.delimiter); + frame + }) + } + + /// Stops reading and returns the current buffer. + /// + /// The buffer may contain multiple delimited frames. + pub fn stop(self) -> Vec { + self.buffer + } + + fn flush(&mut self) { + while self.read() {} + } + + fn read(&mut self) -> bool { + let mut data = [0; 32]; + let len = self.listener.ctap_hid.read(&mut data).unwrap(); + let pos = self.buffer.len(); + self.buffer.extend_from_slice(&data[.. len]); + if self.frame.is_none() { + for i in pos .. pos + len { + if self.buffer[i] == self.delimiter { + self.frame = Some(i); + break; + } + } + } + len == data.len() + } +} + +/// Asynchronously reads into the provided buffer. +/// +/// If instead you want to continuously read delimited frames, use [`DelimitedReader`]. +#[must_use] +pub struct Reader<'a>(Updater<'a>); + +impl<'a> Reader<'a> { + /// Asynchronously reads from a CtapHid into a buffer. + pub fn new(ctap_hid: &'a CtapHid, buffer: &'a mut [u8]) -> Self { + Reader(Updater::new(ctap_hid, Kind::Reader { buffer })) + } + + /// Returns whether anything has been read (or an error occurred). + pub fn is_empty(&mut self) -> bool { + self.0.is_empty() + } + + /// Returns whether everything has been read (or an error occurred). + pub fn is_done(&mut self) -> bool { + self.0.is_done() + } + + /// Returns how many bytes were read (or if an error occurred). + pub fn result(self) -> Result { + self.0.result() + } +} + +/// Asynchronously writes from the provided buffer. +#[must_use] +pub struct Writer<'a>(Updater<'a>); + +impl<'a> Writer<'a> { + /// Asynchronously writes from a buffer to a CtapHid. + pub fn new(ctap_hid: &'a CtapHid, buffer: &'a [u8]) -> Self { + Writer(Updater::new(ctap_hid, Kind::Writer { buffer })) + } + + /// Returns whether anything has been written (or an error occurred). + pub fn is_empty(&mut self) -> bool { + self.0.is_empty() + } + + /// Returns whether everything has been written (or an error occurred). + pub fn is_done(&mut self) -> bool { + self.0.is_done() + } + + /// Returns how many bytes were written (or if an error occurred). + pub fn result(self) -> Result { + self.0.result() + } +} + +struct Updater<'a> { + // The listener is alive as long as not done (see `should_listen()`). + listener: Option>, + kind: Kind<'a>, + result: Result, +} + +impl<'a> Updater<'a> { + fn new(ctap_hid: &'a CtapHid, kind: Kind<'a>) -> Self { + let event = kind.event(); + let mut result = Updater { listener: None, kind, result: Ok(0) }; + if result.should_listen() { + result.listener = Some(Listener::new(ctap_hid, event)); + } + let _ = result.update(); + result + } + + fn is_empty(&mut self) -> bool { + matches!(self.update(), Ok(0)) + } + + fn is_done(&mut self) -> bool { + let _ = self.update(); + self.listener.is_none() + } + + fn result(mut self) -> Result { + self.update() + } + + fn update(&mut self) -> Result { + let listener = match &mut self.listener { + Some(x) => x, + None => return self.result, + }; + if !listener.is_notified() { + return self.result; + } + let pos = self.result.as_mut().unwrap(); + match self.kind.update(listener.ctap_hid, *pos) { + Ok(len) => *pos += len, + err => self.result = err, + } + if !self.should_listen() { + self.listener = None; + } + self.result + } + + fn should_listen(&self) -> bool { + matches!(self.result, Ok(len) if len < self.kind.len()) + } } -// fn convert_event(event: Event) -> api::Event { -// match event { -// Event::Read => api::Event::Read, -// Event::Write => api::Event::Write, -// } -// } +enum Kind<'a> { + Reader { buffer: &'a mut [u8] }, + Writer { buffer: &'a [u8] }, +} + +impl<'a> Kind<'a> { + fn event(&self) -> Event { + match self { + Kind::Reader { .. } => Event::Read, + Kind::Writer { .. } => Event::Write, + } + } + + fn len(&self) -> usize { + match self { + Kind::Reader { buffer } => buffer.len(), + Kind::Writer { buffer } => buffer.len(), + } + } + + fn update(&mut self, ctap_hid: &CtapHid, pos: usize) -> Result { + match self { + Kind::Reader { buffer } => ctap_hid.read(&mut buffer[pos ..]), + Kind::Writer { buffer } => ctap_hid.write(&buffer[pos ..]), + } + } +} diff --git a/crates/runner-host/Cargo.toml b/crates/runner-host/Cargo.toml index e656d4a5..ff702801 100644 --- a/crates/runner-host/Cargo.toml +++ b/crates/runner-host/Cargo.toml @@ -53,6 +53,7 @@ usb = [ "dep:usbip-device", "dep:wasefire-protocol-usb", "wasefire-scheduler/board-api-platform-protocol", + "wasefire-scheduler/board-api-usb-ctap", "wasefire-scheduler/board-api-usb-serial", ] web = ["dep:web-server"] diff --git a/crates/runner-nordic/Cargo.toml b/crates/runner-nordic/Cargo.toml index 21562338..15a208e1 100644 --- a/crates/runner-nordic/Cargo.toml +++ b/crates/runner-nordic/Cargo.toml @@ -57,6 +57,7 @@ features = [ "board-api-storage", "board-api-timer", "board-api-uart", + "board-api-usb-ctap", "board-api-usb-serial", ] diff --git a/crates/scheduler/Cargo.toml b/crates/scheduler/Cargo.toml index f567cc2f..ad2f3bf0 100644 --- a/crates/scheduler/Cargo.toml +++ b/crates/scheduler/Cargo.toml @@ -84,6 +84,7 @@ applet-api-store = ["internal-applet-api-store", "wasefire-applet-api/api-store" applet-api-store-fragment = ["internal-applet-api-store", "wasefire-applet-api/api-store-fragment"] applet-api-timer = ["wasefire-applet-api/api-timer"] applet-api-uart = ["wasefire-applet-api/api-uart"] +applet-api-usb-ctap = ["internal-applet-api-usb", "wasefire-applet-api/api-usb-ctap"] applet-api-usb-serial = ["internal-applet-api-usb", "wasefire-applet-api/api-usb-serial"] # Board API features. Enabling a board API feature automatically enables all # applet API features that would be implemented. @@ -153,6 +154,11 @@ board-api-storage = [ ] board-api-timer = ["applet-api-timer", "wasefire-board-api/api-timer"] board-api-uart = ["applet-api-uart", "wasefire-board-api/api-uart"] +board-api-usb-ctap = [ + "applet-api-usb-ctap", + "internal-board-api-usb", + "wasefire-board-api/api-usb-ctap", +] board-api-usb-serial = [ "applet-api-usb-serial", "internal-board-api-usb", @@ -180,6 +186,7 @@ full-applet-api = [ "applet-api-store-fragment", "applet-api-timer", "applet-api-uart", + "applet-api-usb-ctap", "applet-api-usb-serial", ] # Enables all board API features (unstable). @@ -203,6 +210,7 @@ full-board-api = [ "board-api-storage", "board-api-timer", "board-api-uart", + "board-api-usb-ctap", "board-api-usb-serial", ] # Software crypto features. Enabling a software crypto feature automatically diff --git a/crates/scheduler/src/call/usb.rs b/crates/scheduler/src/call/usb.rs index e5686bce..53086e70 100644 --- a/crates/scheduler/src/call/usb.rs +++ b/crates/scheduler/src/call/usb.rs @@ -17,6 +17,8 @@ use wasefire_board_api::Api as Board; use crate::DispatchSchedulerCall; +#[cfg(feature = "applet-api-usb-ctap")] +mod ctap; #[cfg(feature = "applet-api-usb-serial")] mod serial; @@ -24,5 +26,7 @@ pub fn process(call: Api>) { match call { #[cfg(feature = "applet-api-usb-serial")] Api::Serial(call) => serial::process(call), + #[cfg(feature = "applet-api-usb-ctap")] + Api::Ctap(call) => ctap::process(call), } } diff --git a/crates/scheduler/src/event/usb.rs b/crates/scheduler/src/event/usb.rs index 9a940c02..af7ba2e7 100644 --- a/crates/scheduler/src/event/usb.rs +++ b/crates/scheduler/src/event/usb.rs @@ -18,10 +18,15 @@ use wasefire_board_api::Api as Board; #[cfg(feature = "board-api-usb-serial")] pub mod serial; +#[cfg(feature = "board-api-usb-ctap")] +pub mod ctap; + #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] pub enum Key { #[cfg(feature = "board-api-usb-serial")] Serial(serial::Key), + #[cfg(feature = "board-api-usb-ctap")] + Ctap(ctap::Key), } impl From for crate::event::Key { @@ -35,6 +40,8 @@ impl<'a> From<&'a Event> for Key { match event { #[cfg(feature = "board-api-usb-serial")] Event::Serial(event) => Key::Serial(event.into()), + #[cfg(feature = "board-api-usb-ctap")] + Event::Ctap(event) => Key::Ctap(event.into()), } } } @@ -43,5 +50,7 @@ pub fn process(event: Event) { match event { #[cfg(feature = "board-api-usb-serial")] Event::Serial(_) => serial::process(), + #[cfg(feature = "board-api-usb-ctap")] + Event::Ctap(_) => ctap::process(), } } From 4b5965d80756fc755fb6cfb1b6320a0edb1b056d Mon Sep 17 00:00:00 2001 From: Surya Midatala Date: Fri, 9 Aug 2024 15:08:59 +0000 Subject: [PATCH 4/4] Fix CI --- crates/api-desc/src/usb.rs | 5 - crates/prelude/Cargo.toml | 1 - crates/prelude/src/usb/ctap.rs | 2 +- crates/runner-host/Cargo.toml | 1 - crates/runner-nordic/Cargo.toml | 1 - crates/scheduler/Cargo.toml | 8 - crates/scheduler/src/call/usb.rs | 4 - examples/rust/opensk/Cargo.lock | 297 +------------------------------ 8 files changed, 2 insertions(+), 317 deletions(-) diff --git a/crates/api-desc/src/usb.rs b/crates/api-desc/src/usb.rs index e6435190..29ff5619 100644 --- a/crates/api-desc/src/usb.rs +++ b/crates/api-desc/src/usb.rs @@ -17,9 +17,6 @@ use crate::*; #[cfg(feature = "api-usb-serial")] mod serial; -#[cfg(feature = "api-usb-ctap")] -mod ctap; - pub(crate) fn new() -> Item { let docs = docs! { /// USB operations. @@ -28,8 +25,6 @@ pub(crate) fn new() -> Item { let items = vec![ #[cfg(feature = "api-usb-serial")] serial::new(), - #[cfg(feature = "api-usb-ctap")] - ctap::new(), ]; Item::Mod(Mod { docs, name, items }) } diff --git a/crates/prelude/Cargo.toml b/crates/prelude/Cargo.toml index 891f5628..aced93b7 100644 --- a/crates/prelude/Cargo.toml +++ b/crates/prelude/Cargo.toml @@ -24,7 +24,6 @@ digest = { version = "0.10.7", default-features = false, features = ["mac"], opt rlsf = { version = "0.2.1", default-features = false, optional = true } sealed = { version = "0.5.0", default-features = false, optional = true } typenum = { version = "1.17.0", default-features = false, optional = true } -usbd-ctaphid = "0.1.0" wasefire-applet-api = { version = "0.6.1", path = "../api", features = ["wasm"] } wasefire-error = { version = "0.1.1", path = "../error" } wasefire-sync = { version = "0.1.1", path = "../sync" } diff --git a/crates/prelude/src/usb/ctap.rs b/crates/prelude/src/usb/ctap.rs index 85caf823..d6637df7 100644 --- a/crates/prelude/src/usb/ctap.rs +++ b/crates/prelude/src/usb/ctap.rs @@ -23,7 +23,7 @@ use wasefire_applet_api::usb::serial as api; use crate::{convert, convert_unit, scheduling, Error}; -/// Implements the [`CtapHid`](crate::ctap::CtapHid) interface for USB transport. +/// Implements a CTAP HID interface for USB transport. pub struct CtapHid; impl CtapHid { diff --git a/crates/runner-host/Cargo.toml b/crates/runner-host/Cargo.toml index ff702801..e656d4a5 100644 --- a/crates/runner-host/Cargo.toml +++ b/crates/runner-host/Cargo.toml @@ -53,7 +53,6 @@ usb = [ "dep:usbip-device", "dep:wasefire-protocol-usb", "wasefire-scheduler/board-api-platform-protocol", - "wasefire-scheduler/board-api-usb-ctap", "wasefire-scheduler/board-api-usb-serial", ] web = ["dep:web-server"] diff --git a/crates/runner-nordic/Cargo.toml b/crates/runner-nordic/Cargo.toml index 15a208e1..21562338 100644 --- a/crates/runner-nordic/Cargo.toml +++ b/crates/runner-nordic/Cargo.toml @@ -57,7 +57,6 @@ features = [ "board-api-storage", "board-api-timer", "board-api-uart", - "board-api-usb-ctap", "board-api-usb-serial", ] diff --git a/crates/scheduler/Cargo.toml b/crates/scheduler/Cargo.toml index ad2f3bf0..f567cc2f 100644 --- a/crates/scheduler/Cargo.toml +++ b/crates/scheduler/Cargo.toml @@ -84,7 +84,6 @@ applet-api-store = ["internal-applet-api-store", "wasefire-applet-api/api-store" applet-api-store-fragment = ["internal-applet-api-store", "wasefire-applet-api/api-store-fragment"] applet-api-timer = ["wasefire-applet-api/api-timer"] applet-api-uart = ["wasefire-applet-api/api-uart"] -applet-api-usb-ctap = ["internal-applet-api-usb", "wasefire-applet-api/api-usb-ctap"] applet-api-usb-serial = ["internal-applet-api-usb", "wasefire-applet-api/api-usb-serial"] # Board API features. Enabling a board API feature automatically enables all # applet API features that would be implemented. @@ -154,11 +153,6 @@ board-api-storage = [ ] board-api-timer = ["applet-api-timer", "wasefire-board-api/api-timer"] board-api-uart = ["applet-api-uart", "wasefire-board-api/api-uart"] -board-api-usb-ctap = [ - "applet-api-usb-ctap", - "internal-board-api-usb", - "wasefire-board-api/api-usb-ctap", -] board-api-usb-serial = [ "applet-api-usb-serial", "internal-board-api-usb", @@ -186,7 +180,6 @@ full-applet-api = [ "applet-api-store-fragment", "applet-api-timer", "applet-api-uart", - "applet-api-usb-ctap", "applet-api-usb-serial", ] # Enables all board API features (unstable). @@ -210,7 +203,6 @@ full-board-api = [ "board-api-storage", "board-api-timer", "board-api-uart", - "board-api-usb-ctap", "board-api-usb-serial", ] # Software crypto features. Enabling a software crypto feature automatically diff --git a/crates/scheduler/src/call/usb.rs b/crates/scheduler/src/call/usb.rs index 53086e70..617992c4 100644 --- a/crates/scheduler/src/call/usb.rs +++ b/crates/scheduler/src/call/usb.rs @@ -19,14 +19,10 @@ use crate::DispatchSchedulerCall; #[cfg(feature = "applet-api-usb-ctap")] mod ctap; -#[cfg(feature = "applet-api-usb-serial")] -mod serial; pub fn process(call: Api>) { match call { #[cfg(feature = "applet-api-usb-serial")] Api::Serial(call) => serial::process(call), - #[cfg(feature = "applet-api-usb-ctap")] - Api::Ctap(call) => ctap::process(call), } } diff --git a/examples/rust/opensk/Cargo.lock b/examples/rust/opensk/Cargo.lock index 1a9f67b5..9d5bb941 100644 --- a/examples/rust/opensk/Cargo.lock +++ b/examples/rust/opensk/Cargo.lock @@ -17,33 +17,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" -[[package]] -name = "atomic-polyfill" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" -dependencies = [ - "critical-section", -] - -[[package]] -name = "autocfg" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" - [[package]] name = "base16ct" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.4.1" @@ -91,18 +70,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" -[[package]] -name = "cbor-smol" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d516e3e353d5fc5ee156028f43224033430fd08ef05f8d5dba18a57a4ee5df49" -dependencies = [ - "delog", - "heapless", - "heapless-bytes", - "serde", -] - [[package]] name = "cc" version = "1.0.83" @@ -124,17 +91,6 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "cosey" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39323fe531b92e7acad90b8550b58cec63d29a6c5a56e02de4b25b6aeedbf82e" -dependencies = [ - "heapless-bytes", - "serde", - "serde_repr", -] - [[package]] name = "cpufeatures" version = "0.2.12" @@ -144,12 +100,6 @@ dependencies = [ "libc", ] -[[package]] -name = "critical-section" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" - [[package]] name = "crypto" version = "0.1.0" @@ -189,37 +139,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "ctap-types" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e4fa005ff525537460e1fd70a1ff4f417ae4a6ed14887f3e4720221e20f7562" -dependencies = [ - "bitflags 1.3.2", - "cbor-smol", - "cosey", - "delog", - "heapless", - "heapless-bytes", - "interchange", - "iso7816", - "serde", - "serde-indexed", - "serde_repr", -] - -[[package]] -name = "ctaphid-dispatch" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9e775f67c3a82a134a9e23e0771d3fb3808612ab03843cd31a9b0312004bdde" -dependencies = [ - "delog", - "heapless", - "heapless-bytes", - "interchange", -] - [[package]] name = "data-encoding" version = "2.6.0" @@ -246,15 +165,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "delog" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af2b93368262340c9d4441251b824500d1b641a50957ecf4219a2cc41b9eac8f" -dependencies = [ - "log", -] - [[package]] name = "der" version = "0.7.9" @@ -308,15 +218,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "embedded-time" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a4b4d10ac48d08bfe3db7688c402baadb244721f30a77ce360bd24c3dffe58" -dependencies = [ - "num", -] - [[package]] name = "ff" version = "0.13.0" @@ -375,40 +276,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "hash32" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" -dependencies = [ - "byteorder", -] - -[[package]] -name = "heapless" -version = "0.7.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" -dependencies = [ - "atomic-polyfill", - "hash32", - "rustc_version", - "serde", - "spin 0.9.8", - "stable_deref_trait", -] - -[[package]] -name = "heapless-bytes" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7285eba272c6af3e9f15fb9e1c1b6e7d35aa70580ffe0d47af017e97dfb6f48b" -dependencies = [ - "heapless", - "serde", - "typenum", -] - [[package]] name = "heck" version = "0.4.1" @@ -439,22 +306,6 @@ dependencies = [ "digest", ] -[[package]] -name = "interchange" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310d743c23f798f10d5ba2f77fdd3eff06aaf2d8f8b9d78beba7fb1167f4ccbf" - -[[package]] -name = "iso7816" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3af73ac9c821e7aea3280532118e15cdf9e7bb45c923cbf0e319ae25b27d20c" -dependencies = [ - "delog", - "heapless", -] - [[package]] name = "itoa" version = "0.4.8" @@ -476,16 +327,6 @@ version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.21" @@ -498,68 +339,6 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" -[[package]] -name = "num" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f" -dependencies = [ - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-complex" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - [[package]] name = "num_enum" version = "0.7.2" @@ -621,7 +400,7 @@ version = "0.10.62" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" dependencies = [ - "bitflags 2.4.1", + "bitflags", "cfg-if", "foreign-types", "libc", @@ -810,27 +589,12 @@ dependencies = [ "winapi", ] -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - [[package]] name = "ryu" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "sealed" version = "0.5.0" @@ -856,12 +620,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "semver" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" - [[package]] name = "serde" version = "1.0.197" @@ -871,17 +629,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-indexed" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fca2da10b1f1623f47130256065e05e94fd7a98dbd26a780a4c5de831b21e5c2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - [[package]] name = "serde_derive" version = "1.0.197" @@ -904,17 +651,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_repr" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - [[package]] name = "sha2" version = "0.10.8" @@ -952,16 +688,9 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" dependencies = [ - "lock_api", "portable-atomic", ] -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - [[package]] name = "subtle" version = "2.5.0" @@ -1008,29 +737,6 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" -[[package]] -name = "usb-device" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f6cc3adc849b5292b4075fc0d5fdcf2f24866e88e336dd27a8943090a520508" - -[[package]] -name = "usbd-ctaphid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddb69f660f962236eb21565780c433412ad0ba35e5106f53191cdf443c1d03d2" -dependencies = [ - "ctap-types", - "ctaphid-dispatch", - "delog", - "embedded-time", - "heapless", - "heapless-bytes", - "interchange", - "serde", - "usb-device", -] - [[package]] name = "uuid" version = "0.8.2" @@ -1066,7 +772,6 @@ dependencies = [ "crypto-common", "sealed", "typenum", - "usbd-ctaphid", "wasefire-applet-api", "wasefire-error", "wasefire-sync",