diff --git a/src/ctap/hid/mod.rs b/src/ctap/hid/mod.rs index bf6f799..d262ca7 100644 --- a/src/ctap/hid/mod.rs +++ b/src/ctap/hid/mod.rs @@ -26,10 +26,6 @@ use crate::env::Env; use alloc::vec; use alloc::vec::Vec; use arrayref::{array_ref, array_refs}; -#[cfg(feature = "debug_ctap")] -use core::fmt::Write; -#[cfg(feature = "debug_ctap")] -use libtock_drivers::console::Console; use libtock_drivers::timer::{ClockValue, Duration, Timestamp}; // CTAP specification (version 20190130) section 8.1 @@ -159,14 +155,12 @@ impl CtapHid { .parse_packet(packet, Timestamp::::from_clock_value(clock_value)) { Ok(Some(message)) => { - #[cfg(feature = "debug_ctap")] - writeln!(&mut Console::new(), "Received message: {:02x?}", message).unwrap(); + debug_ctap!(env, "Received message: {:02x?}", message); let cid = message.cid; if !self.has_valid_channel(&message) { - #[cfg(feature = "debug_ctap")] - writeln!(&mut Console::new(), "Invalid channel: {:02x?}", cid).unwrap(); - return CtapHid::error_message(cid, CtapHid::ERR_INVALID_CHANNEL); + debug_ctap!(env, "Invalid channel: {:02x?}", cid); + return CtapHid::error_message(env, cid, CtapHid::ERR_INVALID_CHANNEL); } // If another command arrives, stop winking to prevent accidential button touches. self.wink_permission = TimedPermission::waiting(); @@ -176,7 +170,7 @@ impl CtapHid { CtapHid::COMMAND_MSG => { // If we don't have CTAP1 backward compatibilty, this command is invalid. #[cfg(not(feature = "with_ctap1"))] - return CtapHid::error_message(cid, CtapHid::ERR_INVALID_CMD); + return CtapHid::error_message(env, cid, CtapHid::ERR_INVALID_CMD); #[cfg(feature = "with_ctap1")] match ctap1::Ctap1Command::process_command( @@ -185,9 +179,9 @@ impl CtapHid { ctap_state, clock_value, ) { - Ok(payload) => CtapHid::ctap1_success_message(cid, &payload), + Ok(payload) => CtapHid::ctap1_success_message(env, cid, &payload), Err(ctap1_status_code) => { - CtapHid::ctap1_error_message(cid, ctap1_status_code) + CtapHid::ctap1_error_message(env, cid, ctap1_status_code) } } } @@ -199,11 +193,14 @@ impl CtapHid { // TODO: Send keep-alive packets in the meantime. let response = ctap_state.process_command(env, &message.payload, cid, clock_value); - if let Some(iterator) = CtapHid::split_message(Message { - cid, - cmd: CtapHid::COMMAND_CBOR, - payload: response, - }) { + if let Some(iterator) = CtapHid::split_message( + env, + Message { + cid, + cmd: CtapHid::COMMAND_CBOR, + payload: response, + }, + ) { iterator } else { // Handle the case of a payload > 7609 bytes. @@ -213,20 +210,23 @@ impl CtapHid { // // The error payload that we send instead is 1 <= 7609 bytes, so it is // safe to unwrap() the result. - CtapHid::split_message(Message { - cid, - cmd: CtapHid::COMMAND_CBOR, - payload: vec![ - Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR as u8, - ], - }) + CtapHid::split_message( + env, + Message { + cid, + cmd: CtapHid::COMMAND_CBOR, + payload: vec![ + Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR as u8, + ], + }, + ) .unwrap() } } // CTAP specification (version 20190130) section 8.1.9.1.3 CtapHid::COMMAND_INIT => { if message.payload.len() != 8 { - return CtapHid::error_message(cid, CtapHid::ERR_INVALID_LEN); + return CtapHid::error_message(env, cid, CtapHid::ERR_INVALID_LEN); } let new_cid = if cid == CtapHid::CHANNEL_BROADCAST { @@ -248,11 +248,14 @@ impl CtapHid { payload[16] = CtapHid::CAPABILITIES; // This unwrap is safe because the payload length is 17 <= 7609 bytes. - CtapHid::split_message(Message { - cid, - cmd: CtapHid::COMMAND_INIT, - payload, - }) + CtapHid::split_message( + env, + Message { + cid, + cmd: CtapHid::COMMAND_INIT, + payload, + }, + ) .unwrap() } // CTAP specification (version 20190130) section 8.1.9.1.4 @@ -260,7 +263,7 @@ impl CtapHid { // Pong the same message. // This unwrap is safe because if we could parse the incoming message, it's // payload length must be <= 7609 bytes. - CtapHid::split_message(message).unwrap() + CtapHid::split_message(env, message).unwrap() } // CTAP specification (version 20190130) section 8.1.9.1.5 CtapHid::COMMAND_CANCEL => { @@ -272,22 +275,25 @@ impl CtapHid { // CTAP specification (version 20190130) section 8.1.9.2.1 CtapHid::COMMAND_WINK => { if !message.payload.is_empty() { - return CtapHid::error_message(cid, CtapHid::ERR_INVALID_LEN); + return CtapHid::error_message(env, cid, CtapHid::ERR_INVALID_LEN); } self.wink_permission = TimedPermission::granted(clock_value, CtapHid::WINK_TIMEOUT_DURATION); - CtapHid::split_message(Message { - cid, - cmd: CtapHid::COMMAND_WINK, - payload: vec![], - }) + CtapHid::split_message( + env, + Message { + cid, + cmd: CtapHid::COMMAND_WINK, + payload: vec![], + }, + ) .unwrap() } // CTAP specification (version 20190130) section 8.1.9.2.2 // TODO: implement LOCK _ => { // Unknown or unsupported command. - CtapHid::error_message(cid, CtapHid::ERR_INVALID_CMD) + CtapHid::error_message(env, cid, CtapHid::ERR_INVALID_CMD) } } } @@ -299,18 +305,18 @@ impl CtapHid { if !self.is_allocated_channel(cid) && error != receive::Error::UnexpectedContinuation { - CtapHid::error_message(cid, CtapHid::ERR_INVALID_CHANNEL) + CtapHid::error_message(env, cid, CtapHid::ERR_INVALID_CHANNEL) } else { match error { receive::Error::UnexpectedChannel => { - CtapHid::error_message(cid, CtapHid::ERR_CHANNEL_BUSY) + CtapHid::error_message(env, cid, CtapHid::ERR_CHANNEL_BUSY) } receive::Error::UnexpectedInit => { // TODO: Should we send another error code in this case? // Technically, we were expecting a sequence number and got another // byte, although the command/seqnum bit has higher-level semantics // than sequence numbers. - CtapHid::error_message(cid, CtapHid::ERR_INVALID_SEQ) + CtapHid::error_message(env, cid, CtapHid::ERR_INVALID_SEQ) } receive::Error::UnexpectedContinuation => { // CTAP specification (version 20190130) section 8.1.5.4 @@ -318,13 +324,13 @@ impl CtapHid { HidPacketIterator::none() } receive::Error::UnexpectedSeq => { - CtapHid::error_message(cid, CtapHid::ERR_INVALID_SEQ) + CtapHid::error_message(env, cid, CtapHid::ERR_INVALID_SEQ) } receive::Error::UnexpectedLen => { - CtapHid::error_message(cid, CtapHid::ERR_INVALID_LEN) + CtapHid::error_message(env, cid, CtapHid::ERR_INVALID_LEN) } receive::Error::Timeout => { - CtapHid::error_message(cid, CtapHid::ERR_MSG_TIMEOUT) + CtapHid::error_message(env, cid, CtapHid::ERR_MSG_TIMEOUT) } } } @@ -345,13 +351,16 @@ impl CtapHid { cid != CtapHid::CHANNEL_RESERVED && u32::from_be_bytes(cid) as usize <= self.allocated_cids } - fn error_message(cid: ChannelID, error_code: u8) -> HidPacketIterator { + fn error_message(env: &mut E, cid: ChannelID, error_code: u8) -> HidPacketIterator { // This unwrap is safe because the payload length is 1 <= 7609 bytes. - CtapHid::split_message(Message { - cid, - cmd: CtapHid::COMMAND_ERROR, - payload: vec![error_code], - }) + CtapHid::split_message( + env, + Message { + cid, + cmd: CtapHid::COMMAND_ERROR, + payload: vec![error_code], + }, + ) .unwrap() } @@ -379,23 +388,29 @@ impl CtapHid { } } - fn split_message(message: Message) -> Option { - #[cfg(feature = "debug_ctap")] - writeln!(&mut Console::new(), "Sending message: {:02x?}", message).unwrap(); + fn split_message(env: &mut E, message: Message) -> Option { + debug_ctap!(env, "Sending message: {:02x?}", message); HidPacketIterator::new(message) } - pub fn keepalive(cid: ChannelID, status: KeepaliveStatus) -> HidPacketIterator { + pub fn keepalive( + env: &mut E, + cid: ChannelID, + status: KeepaliveStatus, + ) -> HidPacketIterator { let status_code = match status { KeepaliveStatus::Processing => 1, KeepaliveStatus::UpNeeded => 2, }; // This unwrap is safe because the payload length is 1 <= 7609 bytes. - CtapHid::split_message(Message { - cid, - cmd: CtapHid::COMMAND_KEEPALIVE, - payload: vec![status_code], - }) + CtapHid::split_message( + env, + Message { + cid, + cmd: CtapHid::COMMAND_KEEPALIVE, + payload: vec![status_code], + }, + ) .unwrap() } @@ -404,30 +419,41 @@ impl CtapHid { } #[cfg(feature = "with_ctap1")] - fn ctap1_error_message( + fn ctap1_error_message( + env: &mut E, cid: ChannelID, error_code: ctap1::Ctap1StatusCode, ) -> HidPacketIterator { // This unwrap is safe because the payload length is 2 <= 7609 bytes let code: u16 = error_code.into(); - CtapHid::split_message(Message { - cid, - cmd: CtapHid::COMMAND_MSG, - payload: code.to_be_bytes().to_vec(), - }) + CtapHid::split_message( + env, + Message { + cid, + cmd: CtapHid::COMMAND_MSG, + payload: code.to_be_bytes().to_vec(), + }, + ) .unwrap() } #[cfg(feature = "with_ctap1")] - fn ctap1_success_message(cid: ChannelID, payload: &[u8]) -> HidPacketIterator { + fn ctap1_success_message( + env: &mut E, + cid: ChannelID, + payload: &[u8], + ) -> HidPacketIterator { let mut response = payload.to_vec(); let code: u16 = ctap1::Ctap1StatusCode::SW_SUCCESS.into(); response.extend_from_slice(&code.to_be_bytes()); - CtapHid::split_message(Message { - cid, - cmd: CtapHid::COMMAND_MSG, - payload: response, - }) + CtapHid::split_message( + env, + Message { + cid, + cmd: CtapHid::COMMAND_MSG, + payload: response, + }, + ) .unwrap() } } diff --git a/src/ctap/mod.rs b/src/ctap/mod.rs index cac6cea..04dafe4 100644 --- a/src/ctap/mod.rs +++ b/src/ctap/mod.rs @@ -74,15 +74,11 @@ use alloc::vec::Vec; use arrayref::array_ref; use byteorder::{BigEndian, ByteOrder}; use core::convert::TryFrom; -#[cfg(feature = "debug_ctap")] -use core::fmt::Write; use crypto::ecdsa; use crypto::hmac::{hmac_256, verify_hmac_256}; use crypto::rng256::Rng256; use crypto::sha256::Sha256; use crypto::Hash256; -#[cfg(feature = "debug_ctap")] -use libtock_drivers::console::Console; use libtock_drivers::timer::{ClockValue, Duration}; use sk_cbor as cbor; use sk_cbor::cbor_map_options; @@ -461,8 +457,7 @@ impl CtapState { now: ClockValue, ) -> Vec { let cmd = Command::deserialize(command_cbor); - #[cfg(feature = "debug_ctap")] - writeln!(&mut Console::new(), "Received command: {:#?}", cmd).unwrap(); + debug_ctap!(env, "Received command: {:#?}", cmd); match cmd { Ok(command) => { // Correct behavior between CTAP1 and CTAP2 isn't defined yet. Just a guess. @@ -536,8 +531,7 @@ impl CtapState { } Command::AuthenticatorVendorUpgradeInfo => self.process_vendor_upgrade_info(), }; - #[cfg(feature = "debug_ctap")] - writeln!(&mut Console::new(), "Sending response: {:#?}", response).unwrap(); + debug_ctap!(env, "Sending response: {:#?}", response); match response { Ok(response_data) => { let mut response_vec = vec![0x00]; diff --git a/src/env/mod.rs b/src/env/mod.rs index e04d1d8..505af02 100644 --- a/src/env/mod.rs +++ b/src/env/mod.rs @@ -13,7 +13,7 @@ pub trait UserPresence { /// Blocks for user presence. /// /// Returns an error in case of timeout or keepalive error. - fn check(&self, cid: ChannelID) -> Result<(), Ctap2StatusCode>; + fn check(&mut self, cid: ChannelID) -> Result<(), Ctap2StatusCode>; } /// Describes what CTAP needs to function. @@ -23,6 +23,7 @@ pub trait Env { type Storage: Storage; type UpgradeStorage: UpgradeStorage; type FirmwareProtection: FirmwareProtection; + type Write: core::fmt::Write; fn rng(&mut self) -> &mut Self::Rng; fn user_presence(&mut self) -> &mut Self::UserPresence; @@ -38,4 +39,12 @@ pub trait Env { fn upgrade_storage(&mut self) -> StorageResult; fn firmware_protection(&mut self) -> &mut Self::FirmwareProtection; + + /// Creates a write instance for debugging. + /// + /// This API doesn't return a reference such that drop may flush. This matches the Tock + /// environment. Non-Tock embedded environments should use the defmt feature (to be implemented + /// using the defmt crate) and ignore this API. Non-embedded environments may either use this + /// API or use the log feature (to be implemented using the log crate). + fn write(&mut self) -> Self::Write; } diff --git a/src/env/test/mod.rs b/src/env/test/mod.rs index e1ace12..78d75e8 100644 --- a/src/env/test/mod.rs +++ b/src/env/test/mod.rs @@ -17,6 +17,14 @@ pub struct TestUserPresence { check: Box Result<(), Ctap2StatusCode>>, } +pub struct TestWrite; + +impl core::fmt::Write for TestWrite { + fn write_str(&mut self, _: &str) -> core::fmt::Result { + Ok(()) + } +} + impl TestEnv { pub fn new() -> Self { let rng = ThreadRng256 {}; @@ -34,7 +42,7 @@ impl TestUserPresence { } impl UserPresence for TestUserPresence { - fn check(&self, cid: ChannelID) -> Result<(), Ctap2StatusCode> { + fn check(&mut self, cid: ChannelID) -> Result<(), Ctap2StatusCode> { (self.check)(cid) } } @@ -51,6 +59,7 @@ impl Env for TestEnv { type Storage = BufferStorage; type UpgradeStorage = BufferUpgradeStorage; type FirmwareProtection = Self; + type Write = TestWrite; fn rng(&mut self) -> &mut Self::Rng { &mut self.rng @@ -82,4 +91,8 @@ impl Env for TestEnv { fn firmware_protection(&mut self) -> &mut Self::FirmwareProtection { self } + + fn write(&mut self) -> Self::Write { + TestWrite + } } diff --git a/src/env/tock/mod.rs b/src/env/tock/mod.rs index 9d27f56..8ccb338 100644 --- a/src/env/tock/mod.rs +++ b/src/env/tock/mod.rs @@ -10,7 +10,6 @@ use core::sync::atomic::{AtomicBool, Ordering}; use crypto::rng256::TockRng256; use libtock_core::result::{CommandError, EALREADY}; use libtock_drivers::buttons::{self, ButtonState}; -#[cfg(feature = "debug_ctap")] use libtock_drivers::console::Console; use libtock_drivers::result::{FlexUnwrap, TockError}; use libtock_drivers::timer::Duration; @@ -58,8 +57,8 @@ pub unsafe fn steal_storage() -> StorageResult { } impl UserPresence for TockEnv { - fn check(&self, cid: ChannelID) -> Result<(), Ctap2StatusCode> { - check_user_presence(cid) + fn check(&mut self, cid: ChannelID) -> Result<(), Ctap2StatusCode> { + check_user_presence(self, cid) } } @@ -82,6 +81,7 @@ impl Env for TockEnv { type Storage = SyscallStorage; type UpgradeStorage = SyscallUpgradeStorage; type FirmwareProtection = Self; + type Write = Console; fn rng(&mut self) -> &mut Self::Rng { &mut self.rng @@ -104,6 +104,10 @@ impl Env for TockEnv { fn firmware_protection(&mut self) -> &mut Self::FirmwareProtection { self } + + fn write(&mut self) -> Self::Write { + Console::new() + } } /// Asserts a boolean is false and sets it to true. @@ -114,10 +118,11 @@ fn assert_once(b: &mut bool) { // Returns whether the keepalive was sent, or false if cancelled. fn send_keepalive_up_needed( + env: &mut TockEnv, cid: ChannelID, timeout: Duration, ) -> Result<(), Ctap2StatusCode> { - let keepalive_msg = CtapHid::keepalive(cid, KeepaliveStatus::UpNeeded); + let keepalive_msg = CtapHid::keepalive(env, cid, KeepaliveStatus::UpNeeded); for mut pkt in keepalive_msg { let status = usb_ctap_hid::send_or_recv_with_timeout(&mut pkt, timeout); match status { @@ -228,13 +233,13 @@ pub fn switch_off_leds() { const KEEPALIVE_DELAY_MS: isize = 100; pub const KEEPALIVE_DELAY: Duration = Duration::from_ms(KEEPALIVE_DELAY_MS); -fn check_user_presence(cid: ChannelID) -> Result<(), Ctap2StatusCode> { +fn check_user_presence(env: &mut TockEnv, cid: ChannelID) -> Result<(), Ctap2StatusCode> { // The timeout is N times the keepalive delay. const TIMEOUT_ITERATIONS: 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. - send_keepalive_up_needed(cid, KEEPALIVE_DELAY)?; + send_keepalive_up_needed(env, cid, KEEPALIVE_DELAY)?; // Listen to the button presses. let button_touched = Cell::new(false); @@ -284,7 +289,7 @@ fn check_user_presence(cid: ChannelID) -> Result<(), Ctap2StatusCode> { // so that LEDs blink with a consistent pattern. if keepalive_expired.get() { // Do not return immediately, because we must clean up still. - keepalive_response = send_keepalive_up_needed(cid, KEEPALIVE_DELAY); + keepalive_response = send_keepalive_up_needed(env, cid, KEEPALIVE_DELAY); } if button_touched.get() || keepalive_response.is_err() { diff --git a/src/lib.rs b/src/lib.rs index c3be33d..f06b843 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,6 +24,23 @@ use crate::ctap::CtapState; use crate::env::Env; use libtock_drivers::timer::ClockValue; +// Those macros should eventually be split into trace, debug, info, warn, and error macros when +// adding either the defmt or log feature and crate dependency. +#[cfg(feature = "debug_ctap")] +macro_rules! debug_ctap { + ($env: expr, $($rest:tt)*) => { + use core::fmt::Write; + writeln!($env.write(), $($rest)*).unwrap(); + }; +} +#[cfg(not(feature = "debug_ctap"))] +macro_rules! debug_ctap { + ($env: expr, $($rest:tt)*) => { + // To avoid unused variable warnings. + let _ = $env; + }; +} + pub mod api; // Implementation details must be public for testing (in particular fuzzing). #[cfg(feature = "std")]