mirror of
https://github.com/trussed-dev/usbd-ctaphid.git
synced 2026-06-20 04:16:20 -07:00
Add request interrupt mechanism
This commit is contained in:
committed by
sosthene-nitrokey
parent
2f658fbe84
commit
9dc54d6de8
+5
-1
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user