Vendor HID for libtock-drivers (#452)

* multi HID interface for libtock-drivers

* proper u8 print
This commit is contained in:
kaczmarczyck
2022-04-05 05:52:33 +02:00
committed by GitHub
parent 742e5f149f
commit b33ffb7979
5 changed files with 86 additions and 135 deletions

View File

@@ -32,7 +32,7 @@ std = ["crypto/std", "lang_items/std", "persistent_store/std"]
verbose = ["debug_ctap", "libtock_drivers/verbose_usb"] verbose = ["debug_ctap", "libtock_drivers/verbose_usb"]
with_ctap1 = ["crypto/with_ctap1"] with_ctap1 = ["crypto/with_ctap1"]
with_nfc = ["libtock_drivers/with_nfc"] with_nfc = ["libtock_drivers/with_nfc"]
vendor_hid = [] vendor_hid = ["libtock_drivers/vendor_hid"]
fuzz = ["arbitrary", "std"] fuzz = ["arbitrary", "std"]
[dev-dependencies] [dev-dependencies]

27
src/env/tock/mod.rs vendored
View File

@@ -1,6 +1,6 @@
pub use self::storage::{TockStorage, TockUpgradeStorage}; pub use self::storage::{TockStorage, TockUpgradeStorage};
use crate::api::firmware_protection::FirmwareProtection; use crate::api::firmware_protection::FirmwareProtection;
use crate::ctap::hid::{ChannelID, CtapHid, CtapHidCommand, KeepaliveStatus, ProcessedPacket}; use crate::ctap::hid::{CtapHid, CtapHidCommand, KeepaliveStatus, ProcessedPacket};
use crate::ctap::status_code::Ctap2StatusCode; use crate::ctap::status_code::Ctap2StatusCode;
use crate::ctap::Channel; use crate::ctap::Channel;
use crate::env::{Env, UserPresence}; use crate::env::{Env, UserPresence};
@@ -56,11 +56,7 @@ pub fn take_storage() -> StorageResult<TockStorage> {
impl UserPresence for TockEnv { impl UserPresence for TockEnv {
fn check(&mut self, channel: Channel) -> Result<(), Ctap2StatusCode> { fn check(&mut self, channel: Channel) -> Result<(), Ctap2StatusCode> {
match channel { check_user_presence(self, channel)
Channel::MainHid(cid) => check_user_presence(self, cid),
#[cfg(feature = "vendor_hid")]
Channel::VendorHid(cid) => check_user_presence(self, cid),
}
} }
} }
@@ -113,12 +109,17 @@ impl Env for TockEnv {
// Returns whether the keepalive was sent, or false if cancelled. // Returns whether the keepalive was sent, or false if cancelled.
fn send_keepalive_up_needed( fn send_keepalive_up_needed(
env: &mut TockEnv, env: &mut TockEnv,
cid: ChannelID, channel: Channel,
timeout: Duration<isize>, timeout: Duration<isize>,
) -> Result<(), Ctap2StatusCode> { ) -> Result<(), Ctap2StatusCode> {
let (interface, cid) = match channel {
Channel::MainHid(cid) => (usb_ctap_hid::UsbInterface::MainHid, cid),
#[cfg(feature = "vendor_hid")]
Channel::VendorHid(cid) => (usb_ctap_hid::UsbInterface::VendorHid, cid),
};
let keepalive_msg = CtapHid::keepalive(cid, KeepaliveStatus::UpNeeded); let keepalive_msg = CtapHid::keepalive(cid, KeepaliveStatus::UpNeeded);
for mut pkt in keepalive_msg { for mut pkt in keepalive_msg {
let status = usb_ctap_hid::send_or_recv_with_timeout(&mut pkt, timeout); let status = usb_ctap_hid::send_or_recv_with_timeout(&mut pkt, timeout, interface);
match status { match status {
None => { None => {
debug_ctap!(env, "Sending a KEEPALIVE packet timed out"); debug_ctap!(env, "Sending a KEEPALIVE packet timed out");
@@ -128,10 +129,10 @@ fn send_keepalive_up_needed(
Some(usb_ctap_hid::SendOrRecvStatus::Sent) => { Some(usb_ctap_hid::SendOrRecvStatus::Sent) => {
debug_ctap!(env, "Sent KEEPALIVE packet"); debug_ctap!(env, "Sent KEEPALIVE packet");
} }
Some(usb_ctap_hid::SendOrRecvStatus::Received) => { Some(usb_ctap_hid::SendOrRecvStatus::Received(received_interface)) => {
// We only parse one packet, because we only care about CANCEL. // We only parse one packet, because we only care about CANCEL.
let (received_cid, processed_packet) = CtapHid::process_single_packet(&pkt); let (received_cid, processed_packet) = CtapHid::process_single_packet(&pkt);
if received_cid != &cid { if received_interface != interface || received_cid != &cid {
debug_ctap!( debug_ctap!(
env, env,
"Received a packet on channel ID {:?} while sending a KEEPALIVE packet", "Received a packet on channel ID {:?} while sending a KEEPALIVE packet",
@@ -218,13 +219,13 @@ pub fn switch_off_leds() {
const KEEPALIVE_DELAY_MS: isize = 100; const KEEPALIVE_DELAY_MS: isize = 100;
pub const KEEPALIVE_DELAY_TOCK: Duration<isize> = Duration::from_ms(KEEPALIVE_DELAY_MS); pub const KEEPALIVE_DELAY_TOCK: Duration<isize> = Duration::from_ms(KEEPALIVE_DELAY_MS);
fn check_user_presence(env: &mut TockEnv, cid: ChannelID) -> Result<(), Ctap2StatusCode> { fn check_user_presence(env: &mut TockEnv, channel: Channel) -> Result<(), Ctap2StatusCode> {
// The timeout is N times the keepalive delay. // The timeout is N times the keepalive delay.
const TIMEOUT_ITERATIONS: usize = const TIMEOUT_ITERATIONS: usize =
crate::ctap::TOUCH_TIMEOUT_MS as usize / KEEPALIVE_DELAY_MS as usize; crate::ctap::TOUCH_TIMEOUT_MS as usize / KEEPALIVE_DELAY_MS as usize;
// First, send a keep-alive packet to notify that the keep-alive status has changed. // First, send a keep-alive packet to notify that the keep-alive status has changed.
send_keepalive_up_needed(env, cid, KEEPALIVE_DELAY_TOCK)?; send_keepalive_up_needed(env, channel, KEEPALIVE_DELAY_TOCK)?;
// Listen to the button presses. // Listen to the button presses.
let button_touched = Cell::new(false); let button_touched = Cell::new(false);
@@ -274,7 +275,7 @@ fn check_user_presence(env: &mut TockEnv, cid: ChannelID) -> Result<(), Ctap2Sta
// so that LEDs blink with a consistent pattern. // so that LEDs blink with a consistent pattern.
if keepalive_expired.get() { if keepalive_expired.get() {
// Do not return immediately, because we must clean up still. // Do not return immediately, because we must clean up still.
keepalive_response = send_keepalive_up_needed(env, cid, KEEPALIVE_DELAY_TOCK); keepalive_response = send_keepalive_up_needed(env, channel, KEEPALIVE_DELAY_TOCK);
} }
if button_touched.get() || keepalive_response.is_err() { if button_touched.get() || keepalive_response.is_err() {

View File

@@ -89,15 +89,15 @@ fn main() {
} }
let mut pkt_request = [0; 64]; let mut pkt_request = [0; 64];
let has_packet = let usb_interface =
match usb_ctap_hid::recv_with_timeout(&mut pkt_request, KEEPALIVE_DELAY_TOCK) { match usb_ctap_hid::recv_with_timeout(&mut pkt_request, KEEPALIVE_DELAY_TOCK) {
Some(usb_ctap_hid::SendOrRecvStatus::Received) => { Some(usb_ctap_hid::SendOrRecvStatus::Received(interface)) => {
#[cfg(feature = "debug_ctap")] #[cfg(feature = "debug_ctap")]
print_packet_notice("Received packet", &clock); print_packet_notice("Received packet", &clock);
true Some(interface)
} }
Some(_) => panic!("Error receiving packet"), Some(_) => panic!("Error receiving packet"),
None => false, None => None,
}; };
let now = clock.try_now().unwrap(); let now = clock.try_now().unwrap();
@@ -120,11 +120,20 @@ fn main() {
// don't cause problems with timers. // don't cause problems with timers.
ctap.update_timeouts(now); ctap.update_timeouts(now);
if has_packet { if let Some(interface) = usb_interface {
let reply = ctap.process_hid_packet(&pkt_request, Transport::MainHid, now); let transport = match interface {
usb_ctap_hid::UsbInterface::MainHid => Transport::MainHid,
#[cfg(feature = "vendor_hid")]
usb_ctap_hid::UsbInterface::VendorHid => Transport::VendorHid,
};
let reply = ctap.process_hid_packet(&pkt_request, transport, now);
// This block handles sending packets. // This block handles sending packets.
for mut pkt_reply in reply { for mut pkt_reply in reply {
let status = usb_ctap_hid::send_or_recv_with_timeout(&mut pkt_reply, SEND_TIMEOUT); let status = usb_ctap_hid::send_or_recv_with_timeout(
&mut pkt_reply,
SEND_TIMEOUT,
interface,
);
match status { match status {
None => { None => {
#[cfg(feature = "debug_ctap")] #[cfg(feature = "debug_ctap")]
@@ -138,7 +147,7 @@ fn main() {
#[cfg(feature = "debug_ctap")] #[cfg(feature = "debug_ctap")]
print_packet_notice("Sent packet", &clock); print_packet_notice("Sent packet", &clock);
} }
Some(usb_ctap_hid::SendOrRecvStatus::Received) => { Some(usb_ctap_hid::SendOrRecvStatus::Received(_)) => {
#[cfg(feature = "debug_ctap")] #[cfg(feature = "debug_ctap")]
print_packet_notice("Received an UNEXPECTED packet", &clock); print_packet_notice("Received an UNEXPECTED packet", &clock);
// TODO: handle this unexpected packet. // TODO: handle this unexpected packet.

View File

@@ -13,5 +13,6 @@ libtock_core = { path = "../../third_party/libtock-rs/core" }
[features] [features]
debug_ctap = [] debug_ctap = []
vendor_hid = []
verbose_usb = ["debug_ctap"] verbose_usb = ["debug_ctap"]
with_nfc=[] with_nfc=[]

View File

@@ -1,4 +1,4 @@
// Copyright 2019 Google LLC // Copyright 2019-2022 Google LLC
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@@ -28,14 +28,14 @@ const DRIVER_NUMBER: usize = 0x20009;
mod command_nr { mod command_nr {
pub const CHECK: usize = 0; pub const CHECK: usize = 0;
pub const CONNECT: usize = 1; pub const CONNECT: usize = 1;
pub const TRANSMIT: usize = 2; pub const _TRANSMIT: usize = 2;
pub const RECEIVE: usize = 3; pub const RECEIVE: usize = 3;
pub const TRANSMIT_OR_RECEIVE: usize = 4; pub const TRANSMIT_OR_RECEIVE: usize = 4;
pub const CANCEL: usize = 5; pub const CANCEL: usize = 5;
} }
mod subscribe_nr { mod subscribe_nr {
pub const TRANSMIT: usize = 1; pub const _TRANSMIT: usize = 1;
pub const RECEIVE: usize = 2; pub const RECEIVE: usize = 2;
pub const TRANSMIT_OR_RECEIVE: usize = 3; pub const TRANSMIT_OR_RECEIVE: usize = 3;
pub mod callback_status { pub mod callback_status {
@@ -45,7 +45,7 @@ mod subscribe_nr {
} }
mod allow_nr { mod allow_nr {
pub const TRANSMIT: usize = 1; pub const _TRANSMIT: usize = 1;
pub const RECEIVE: usize = 2; pub const RECEIVE: usize = 2;
pub const TRANSMIT_OR_RECEIVE: usize = 3; pub const TRANSMIT_OR_RECEIVE: usize = 3;
} }
@@ -64,114 +64,23 @@ pub fn setup() -> bool {
true true
} }
#[allow(dead_code)] #[derive(Clone, Copy, PartialEq, Eq)]
pub fn recv(buf: &mut [u8; 64]) -> bool { pub enum UsbInterface {
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::RECEIVE, buf); MainHid = 0,
if result.is_err() { #[cfg(feature = "vendor_hid")]
return false; VendorHid = 1,
}
let done = Cell::new(false);
let mut alarm = || done.set(true);
let subscription = syscalls::subscribe::<callback::Identity0Consumer, _>(
DRIVER_NUMBER,
subscribe_nr::RECEIVE,
&mut alarm,
);
if subscription.is_err() {
return false;
}
let result_code = syscalls::command(DRIVER_NUMBER, command_nr::RECEIVE, 0, 0);
if result_code.is_err() {
return false;
}
util::yieldk_for(|| done.get());
true
}
#[allow(dead_code)]
pub fn send(buf: &mut [u8; 64]) -> bool {
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::TRANSMIT, buf);
if result.is_err() {
return false;
}
let done = Cell::new(false);
let mut alarm = || done.set(true);
let subscription = syscalls::subscribe::<callback::Identity0Consumer, _>(
DRIVER_NUMBER,
subscribe_nr::TRANSMIT,
&mut alarm,
);
if subscription.is_err() {
return false;
}
let result_code = syscalls::command(DRIVER_NUMBER, command_nr::TRANSMIT, 0, 0);
if result_code.is_err() {
return false;
}
util::yieldk_for(|| done.get());
true
} }
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
pub enum SendOrRecvStatus { pub enum SendOrRecvStatus {
Error, Error,
Sent, Sent,
Received, Received(UsbInterface),
} }
// Either sends or receive a packet. /// Waits to receive a packet.
// Because USB transactions are initiated by the host, we don't decide whether an IN transaction ///
// (send for us), an OUT transaction (receive for us), or no transaction at all will happen next. /// Returns None if the transaction timed out, else its status.
//
// - If an IN transaction happens first, the initial content of buf is sent to the host and the
// Sent status is returned.
// - If an OUT transaction happens first, the content of buf is replaced by the packet received
// from the host and Received status is returned. In that case, the original content of buf is not
// sent to the host, and it's up to the caller to retry sending or to handle the packet received
// from the host.
#[allow(dead_code)]
pub fn send_or_recv(buf: &mut [u8; 64]) -> SendOrRecvStatus {
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::TRANSMIT_OR_RECEIVE, buf);
if result.is_err() {
return SendOrRecvStatus::Error;
}
let status = Cell::new(None);
let mut alarm = |direction| {
status.set(Some(match direction {
subscribe_nr::callback_status::TRANSMITTED => SendOrRecvStatus::Sent,
subscribe_nr::callback_status::RECEIVED => SendOrRecvStatus::Received,
// Unknown direction sent by the kernel.
_ => SendOrRecvStatus::Error,
}));
};
let subscription = syscalls::subscribe::<callback::Identity1Consumer, _>(
DRIVER_NUMBER,
subscribe_nr::TRANSMIT_OR_RECEIVE,
&mut alarm,
);
if subscription.is_err() {
return SendOrRecvStatus::Error;
}
let result_code = syscalls::command(DRIVER_NUMBER, command_nr::TRANSMIT_OR_RECEIVE, 0, 0);
if result_code.is_err() {
return SendOrRecvStatus::Error;
}
util::yieldk_for(|| status.get().is_some());
status.get().unwrap()
}
// Same as recv, but with a timeout.
// If the timeout elapses, return None.
#[allow(clippy::let_and_return)] #[allow(clippy::let_and_return)]
pub fn recv_with_timeout( pub fn recv_with_timeout(
buf: &mut [u8; 64], buf: &mut [u8; 64],
@@ -188,19 +97,36 @@ pub fn recv_with_timeout(
let result = recv_with_timeout_detail(buf, timeout_delay); let result = recv_with_timeout_detail(buf, timeout_delay);
#[cfg(feature = "verbose_usb")] #[cfg(feature = "verbose_usb")]
if let Some(SendOrRecvStatus::Received) = result { if let Some(SendOrRecvStatus::Received(interface)) = result {
writeln!(Console::new(), "Received packet = {:02x?}", buf as &[u8]).unwrap(); writeln!(
Console::new(),
"Received packet = {:02x?} on interface {}",
buf as &[u8],
interface as u8,
)
.unwrap();
} }
result result
} }
// Same as send_or_recv, but with a timeout. /// Either sends or receives a packet within a given time.
// If the timeout elapses, return None. ///
/// Because USB transactions are initiated by the host, we don't decide whether an IN transaction
/// (send for us), an OUT transaction (receive for us), or no transaction at all will happen next.
///
/// - If an IN transaction happens first, the initial content of buf is sent to the host and the
/// Sent status is returned.
/// - If an OUT transaction happens first, the content of buf is replaced by the packet received
/// from the host and Received status is returned. In that case, the original content of buf is not
/// sent to the host, and it's up to the caller to retry sending or to handle the packet received
/// from the host.
/// If the timeout elapses, return None.
#[allow(clippy::let_and_return)] #[allow(clippy::let_and_return)]
pub fn send_or_recv_with_timeout( pub fn send_or_recv_with_timeout(
buf: &mut [u8; 64], buf: &mut [u8; 64],
timeout_delay: Duration<isize>, timeout_delay: Duration<isize>,
interface: UsbInterface,
) -> Option<SendOrRecvStatus> { ) -> Option<SendOrRecvStatus> {
#[cfg(feature = "verbose_usb")] #[cfg(feature = "verbose_usb")]
writeln!( writeln!(
@@ -211,11 +137,17 @@ pub fn send_or_recv_with_timeout(
) )
.unwrap(); .unwrap();
let result = send_or_recv_with_timeout_detail(buf, timeout_delay); let result = send_or_recv_with_timeout_detail(buf, timeout_delay, interface);
#[cfg(feature = "verbose_usb")] #[cfg(feature = "verbose_usb")]
if let Some(SendOrRecvStatus::Received) = result { if let Some(SendOrRecvStatus::Received(received_interface)) = result {
writeln!(Console::new(), "Received packet = {:02x?}", buf as &[u8]).unwrap(); writeln!(
Console::new(),
"Received packet = {:02x?} on interface {}",
buf as &[u8],
received_interface as u8,
)
.unwrap();
} }
result result
@@ -233,7 +165,10 @@ fn recv_with_timeout_detail(
let status = Cell::new(None); let status = Cell::new(None);
let mut alarm = |direction| { let mut alarm = |direction| {
status.set(Some(match direction { status.set(Some(match direction {
subscribe_nr::callback_status::RECEIVED => SendOrRecvStatus::Received, subscribe_nr::callback_status::RECEIVED => {
// TODO: set the correct interface
SendOrRecvStatus::Received(UsbInterface::MainHid)
}
// Unknown direction or "transmitted" sent by the kernel. // Unknown direction or "transmitted" sent by the kernel.
_ => SendOrRecvStatus::Error, _ => SendOrRecvStatus::Error,
})); }));
@@ -324,6 +259,8 @@ fn recv_with_timeout_detail(
fn send_or_recv_with_timeout_detail( fn send_or_recv_with_timeout_detail(
buf: &mut [u8; 64], buf: &mut [u8; 64],
timeout_delay: Duration<isize>, timeout_delay: Duration<isize>,
// TODO: To be used as part of the syscall.
_interface: UsbInterface,
) -> Option<SendOrRecvStatus> { ) -> Option<SendOrRecvStatus> {
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::TRANSMIT_OR_RECEIVE, buf); let result = syscalls::allow(DRIVER_NUMBER, allow_nr::TRANSMIT_OR_RECEIVE, buf);
if result.is_err() { if result.is_err() {
@@ -334,7 +271,10 @@ fn send_or_recv_with_timeout_detail(
let mut alarm = |direction| { let mut alarm = |direction| {
status.set(Some(match direction { status.set(Some(match direction {
subscribe_nr::callback_status::TRANSMITTED => SendOrRecvStatus::Sent, subscribe_nr::callback_status::TRANSMITTED => SendOrRecvStatus::Sent,
subscribe_nr::callback_status::RECEIVED => SendOrRecvStatus::Received, subscribe_nr::callback_status::RECEIVED => {
// TODO: set the correct interface
SendOrRecvStatus::Received(UsbInterface::MainHid)
}
// Unknown direction sent by the kernel. // Unknown direction sent by the kernel.
_ => SendOrRecvStatus::Error, _ => SendOrRecvStatus::Error,
})); }));