Add request interrupt mechanism

This commit is contained in:
Sosthène Guédon
2023-06-20 17:37:08 +02:00
committed by sosthene-nitrokey
parent 2f658fbe84
commit 9dc54d6de8
3 changed files with 101 additions and 23 deletions
+5 -1
View File
@@ -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" }
+39 -5
View File
@@ -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<Bus>,
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<Target = Pipe> 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<Bus> for CtapHid<'alloc, 'pipe, Bus>
impl<'alloc, 'pipe, 'interrupt, Bus> UsbClass<Bus> for CtapHid<'alloc, 'pipe, 'interrupt, Bus>
where
Bus: UsbBus,
{
+57 -17
View File
@@ -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;