diff --git a/Cargo.toml b/Cargo.toml index 49a6cfb..3a4bcb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,8 @@ heapless-bytes = "0.3" interchange = "0.3.0" serde = { version = "1.0", default-features = false } usb-device = "0.2.3" +ref-swap = "0.1.0" +trussed = "0.1.0" [features] @@ -32,4 +34,6 @@ log-warn = [] log-error = [] [patch.crates-io] -ctaphid-dispatch = { git = "https://github.com/trussed-dev/ctaphid-dispatch.git", rev = "d9eb980da163b613fdf759f6092b7c3bdcc0a22c"} +ref-swap = { git = "https://github.com/nitrokey/ref-swap.git", rev = "de0330e85b479074ae03bcc05888cfeff682f61e" } +ctaphid-dispatch = { git = "https://github.com/sosthene-nitrokey/ctaphid-dispatch.git", rev = "a5a3696d7cf0665414cf57cdea384dbc8a157f33" } +trussed = { git = "https://github.com/sosthene-nitrokey/trussed.git", rev = "6f095e14b27bd58ab8fa56cf6c616266d92fbfb4" } diff --git a/src/class.rs b/src/class.rs index 61439de..720a532 100644 --- a/src/class.rs +++ b/src/class.rs @@ -2,6 +2,8 @@ // use core::convert::TryFrom as _; use embedded_time::duration::Extensions; +use ref_swap::OptionRefSwap; +use trussed::interrupt::InterruptFlag; use crate::{ constants::{INTERRUPT_POLL_MILLISECONDS, PACKET_SIZE}, @@ -20,12 +22,12 @@ use usb_device::{ }; /// Packet-level implementation of the CTAPHID protocol. -pub struct CtapHid<'alloc, 'pipe, Bus: UsbBus> { +pub struct CtapHid<'alloc, 'pipe, 'interrupt, Bus: UsbBus> { interface: InterfaceNumber, - pipe: Pipe<'alloc, 'pipe, Bus>, + pipe: Pipe<'alloc, 'pipe, 'interrupt, Bus>, } -impl<'alloc, 'pipe, Bus> CtapHid<'alloc, 'pipe, Bus> +impl<'alloc, 'pipe, 'interrupt, Bus> CtapHid<'alloc, 'pipe, 'interrupt, Bus> where Bus: UsbBus, { @@ -53,6 +55,38 @@ where pipe, } } +} + +impl<'alloc, 'pipe, 'interrupt, Bus> CtapHid<'alloc, 'pipe, 'interrupt, Bus> +where + Bus: UsbBus, +{ + pub fn with_interrupt( + allocate: &'alloc UsbBusAllocator, + interchange: Requester<'pipe>, + interrupt: Option<&'interrupt OptionRefSwap<'interrupt, InterruptFlag>>, + initial_milliseconds: u32, + ) -> Self { + // 64 bytes, interrupt endpoint polled every 5 milliseconds + let read_endpoint: EndpointOut<'alloc, Bus> = + allocate.interrupt(PACKET_SIZE as u16, INTERRUPT_POLL_MILLISECONDS); + // 64 bytes, interrupt endpoint polled every 5 milliseconds + let write_endpoint: EndpointIn<'alloc, Bus> = + allocate.interrupt(PACKET_SIZE as u16, INTERRUPT_POLL_MILLISECONDS); + + let pipe = Pipe::with_interrupt( + read_endpoint, + write_endpoint, + interchange, + interrupt, + initial_milliseconds, + ); + + Self { + interface: allocate.interface(), + pipe, + } + } /// Set versions returned in CTAPHID_INIT pub fn set_version(&mut self, version: crate::Version) { @@ -78,7 +112,7 @@ where } // implement DerefMut instead - pub fn pipe(&mut self) -> &mut Pipe<'alloc, 'pipe, Bus> { + pub fn pipe(&mut self) -> &mut Pipe<'alloc, 'pipe, 'interrupt, Bus> { &mut self.pipe } @@ -184,7 +218,7 @@ pub enum ClassRequests { SetProtocol = 0xB, } -impl<'alloc, 'pipe, Bus> UsbClass for CtapHid<'alloc, 'pipe, Bus> +impl<'alloc, 'pipe, 'interrupt, Bus> UsbClass for CtapHid<'alloc, 'pipe, 'interrupt, Bus> where Bus: UsbBus, { diff --git a/src/pipe.rs b/src/pipe.rs index 08e772f..ab5fb3c 100644 --- a/src/pipe.rs +++ b/src/pipe.rs @@ -14,6 +14,7 @@ No state is maintained between transactions. use core::convert::TryFrom; use core::convert::TryInto; +use core::sync::atomic::Ordering; // pub type ContactInterchange = usbd_ccid::types::ApduInterchange; // pub type ContactlessInterchange = iso14443::types::ApduInterchange; @@ -21,7 +22,9 @@ use ctaphid_dispatch::command::Command; use ctaphid_dispatch::types::Requester; use ctap_types::Error as AuthenticatorError; +use trussed::interrupt::InterruptFlag; +use ref_swap::OptionRefSwap; // use serde::Serialize; use usb_device::{ bus::UsbBus, @@ -121,12 +124,13 @@ pub enum State { Sending((Response, MessageState)), } -pub struct Pipe<'alloc, 'pipe, Bus: UsbBus> { +pub struct Pipe<'alloc, 'pipe, 'interrupt, Bus: UsbBus> { read_endpoint: EndpointOut<'alloc, Bus>, write_endpoint: EndpointIn<'alloc, Bus>, state: State, interchange: Requester<'pipe>, + interrupt: Option<&'interrupt OptionRefSwap<'interrupt, InterruptFlag>>, // shared between requests and responses, due to size buffer: [u8; MESSAGE_SIZE], @@ -149,11 +153,7 @@ pub struct Pipe<'alloc, 'pipe, Bus: UsbBus> { pub(crate) version: crate::Version, } -impl<'alloc, 'pipe, Bus: UsbBus> Pipe<'alloc, 'pipe, Bus> { - // pub fn borrow_mut_authenticator(&mut self) -> &mut Authenticator { - // &mut self.authenticator - // } - +impl<'alloc, 'pipe, 'interrupt, Bus: UsbBus> Pipe<'alloc, 'pipe, 'interrupt, Bus> { pub(crate) fn new( read_endpoint: EndpointOut<'alloc, Bus>, write_endpoint: EndpointIn<'alloc, Bus>, @@ -167,6 +167,37 @@ impl<'alloc, 'pipe, Bus: UsbBus> Pipe<'alloc, 'pipe, Bus> { interchange, buffer: [0u8; MESSAGE_SIZE], last_channel: 0, + interrupt: None, + // Default to nothing implemented. + implements: 0x80, + last_milliseconds: initial_milliseconds, + started_processing: false, + needs_keepalive: false, + version: Default::default(), + } + } +} + +impl<'alloc, 'pipe, 'interrupt, Bus: UsbBus> Pipe<'alloc, 'pipe, 'interrupt, Bus> { + // pub fn borrow_mut_authenticator(&mut self) -> &mut Authenticator { + // &mut self.authenticator + // } + + pub(crate) fn with_interrupt( + read_endpoint: EndpointOut<'alloc, Bus>, + write_endpoint: EndpointIn<'alloc, Bus>, + interchange: Requester<'pipe>, + interrupt: Option<&'interrupt OptionRefSwap<'interrupt, InterruptFlag>>, + initial_milliseconds: u32, + ) -> Self { + Self { + read_endpoint, + write_endpoint, + state: State::Idle, + interchange, + buffer: [0u8; MESSAGE_SIZE], + last_channel: 0, + interrupt, // Default to nothing implemented. implements: 0x80, last_milliseconds: initial_milliseconds, @@ -202,12 +233,10 @@ impl<'alloc, 'pipe, Bus: UsbBus> Pipe<'alloc, 'pipe, Bus> { // Remove response if it's there if let Some(_response) = self.interchange.take_response() { } else { - // Cancel if there's a request or processing - match self.interchange.state() { - interchange::State::Requested | interchange::State::BuildingResponse => { - self.interchange.cancel().expect("canceled"); - } - _ => {} + info_now!("Interrupting request"); + if let Some(Some(i)) = self.interrupt.map(|i| i.load(Ordering::Relaxed)) { + info_now!("Loadede some interrupter"); + i.interrupt(); } } @@ -284,19 +313,24 @@ impl<'alloc, 'pipe, Bus: UsbBus> Pipe<'alloc, 'pipe, Bus> { State::WaitingOnAuthenticator(request) => request, State::Receiving((request, _message_state)) => request, _ => { - info!("Ignoring transaction as we're already transmitting."); + info_now!("Ignoring transaction as we're already transmitting."); return; } }; if packet[4] == 0x86 { - info!("Resyncing!"); + info_now!("Resyncing!"); self.cancel_ongoing_activity(); } else { if channel == request.channel { - info!("Expected seq"); - self.start_sending_error(request, AuthenticatorError::InvalidSeq); + if command == Command::Cancel { + info_now!("Cancelling"); + self.cancel_ongoing_activity(); + } else { + info_now!("Expected seq, {:?}", request.command); + self.start_sending_error(request, AuthenticatorError::InvalidSeq); + } } else { - info!("busy."); + info_now!("busy."); self.send_error_now(current_request, AuthenticatorError::ChannelBusy); } @@ -399,6 +433,7 @@ impl<'alloc, 'pipe, Bus: UsbBus> Pipe<'alloc, 'pipe, Bus> { } fn dispatch_request(&mut self, request: Request) { + info!("Got request: {:?}", request.command); match request.command { Command::Init => {} _ => { @@ -461,6 +496,11 @@ impl<'alloc, 'pipe, Bus: UsbBus> Pipe<'alloc, 'pipe, Bus> { self.start_sending(response); } + Command::Cancel => { + info!("CTAPHID_CANCEL"); + self.cancel_ongoing_activity(); + } + _ => { if request.command == Command::Cbor { self.needs_keepalive = true;