diff --git a/Cargo.lock b/Cargo.lock index a9c4b0c7..7be4f09e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -105,7 +105,6 @@ dependencies = [ "byteorder", "crypto", "ed25519-compact", - "embedded-time", "enum-iterator", "lang_items", "libtock_core", @@ -136,15 +135,6 @@ version = "1.0.15" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "bee9df587982575886a8682edcee11877894349a805f25629c27f63abe3e9ae8" -[[package]] -name = "embedded-time" -version = "0.12.1" -source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "d7a4b4d10ac48d08bfe3db7688c402baadb244721f30a77ce360bd24c3dffe58" -dependencies = [ - "num", -] - [[package]] name = "enum-iterator" version = "0.6.0" @@ -277,69 +267,6 @@ version = "2.5.0" source = "registry+/~https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" -[[package]] -name = "num" -version = "0.3.1" -source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f" -dependencies = [ - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-complex" -version = "0.3.1" -source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg 1.1.0", - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.43" -source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" -dependencies = [ - "autocfg 1.1.0", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.3.2" -source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" -dependencies = [ - "autocfg 1.1.0", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+/~https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg 1.1.0", -] - [[package]] name = "once_cell" version = "1.14.0" diff --git a/Cargo.toml b/Cargo.toml index 91e22116..fdc39dce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,6 @@ persistent_store = { path = "libraries/persistent_store" } byteorder = { version = "1", default-features = false } arrayref = "0.3.6" subtle = { version = "2.2", default-features = false, features = ["nightly"] } -embedded-time = "0.12.1" arbitrary = { version = "0.4.7", features = ["derive"], optional = true } rand = { version = "0.8.4", optional = true } ed25519-compact = { version = "1", default-features = false, optional = true } diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index 29d433d9..0d6dcbbf 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -104,7 +104,6 @@ dependencies = [ "arrayref", "byteorder", "crypto", - "embedded-time", "lang_items", "libtock_core", "libtock_drivers", diff --git a/fuzz/fuzz_helper/src/lib.rs b/fuzz/fuzz_helper/src/lib.rs index 2d389f1d..80799260 100644 --- a/fuzz/fuzz_helper/src/lib.rs +++ b/fuzz/fuzz_helper/src/lib.rs @@ -20,7 +20,6 @@ use arbitrary::{Arbitrary, Unstructured}; use arrayref::array_ref; use core::convert::TryFrom; use ctap2::api::customization::is_valid; -use ctap2::clock::CtapInstant; use ctap2::ctap::command::{ AuthenticatorClientPinParameters, AuthenticatorGetAssertionParameters, AuthenticatorMakeCredentialParameters, Command, @@ -89,12 +88,8 @@ fn initialize(ctap: &mut Ctap) -> ChannelID { let mut assembler_reply = MessageAssembler::new(); let mut result_cid: ChannelID = Default::default(); for pkt_request in HidPacketIterator::new(message).unwrap() { - for pkt_reply in - ctap.process_hid_packet(&pkt_request, Transport::MainHid, CtapInstant::new(0)) - { - if let Ok(Some(result)) = - assembler_reply.parse_packet(ctap.env(), &pkt_reply, CtapInstant::new(0)) - { + for pkt_reply in ctap.process_hid_packet(&pkt_request, Transport::MainHid) { + if let Ok(Some(result)) = assembler_reply.parse_packet(ctap.env(), &pkt_reply) { result_cid.copy_from_slice(&result.payload[8..12]); } } @@ -131,11 +126,9 @@ fn process_message(data: &[u8], ctap: &mut Ctap) { if let Some(hid_packet_iterator) = HidPacketIterator::new(message) { let mut assembler_reply = MessageAssembler::new(); for pkt_request in hid_packet_iterator { - for pkt_reply in - ctap.process_hid_packet(&pkt_request, Transport::MainHid, CtapInstant::new(0)) - { + for pkt_reply in ctap.process_hid_packet(&pkt_request, Transport::MainHid) { // Only checks for assembling crashes, not for semantics. - let _ = assembler_reply.parse_packet(ctap.env(), &pkt_reply, CtapInstant::new(0)); + let _ = assembler_reply.parse_packet(ctap.env(), &pkt_reply); } } } @@ -152,7 +145,7 @@ pub fn process_ctap_any_type(data: &[u8]) -> arbitrary::Result<()> { let data = unstructured.take_rest(); // Initialize ctap state and hid and get the allocated cid. - let mut ctap = Ctap::new(env, CtapInstant::new(0)); + let mut ctap = Ctap::new(env); let cid = initialize(&mut ctap); // Wrap input as message with the allocated cid. let mut command = cid.to_vec(); @@ -179,7 +172,7 @@ fn setup_customization( fn setup_state( unstructured: &mut Unstructured, - state: &mut CtapState, + state: &mut CtapState, env: &mut TestEnv, ) -> FuzzResult<()> { if bool::arbitrary(unstructured)? { @@ -202,7 +195,7 @@ pub fn process_ctap_specific_type(data: &[u8], input_type: InputType) -> arbitra return Ok(()); } // Initialize ctap state and hid and get the allocated cid. - let mut ctap = Ctap::new(env, CtapInstant::new(0)); + let mut ctap = Ctap::new(env); let cid = initialize(&mut ctap); // Wrap input as message with allocated cid and command type. let mut command = cid.to_vec(); @@ -232,7 +225,7 @@ pub fn process_ctap_structured(data: &[u8], input_type: InputType) -> FuzzResult env.rng().seed_from_u64(u64::arbitrary(unstructured)?); setup_customization(unstructured, env.customization_mut())?; - let mut state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut state = CtapState::new(&mut env); setup_state(unstructured, &mut state, &mut env)?; let command = match input_type { @@ -255,7 +248,6 @@ pub fn process_ctap_structured(data: &[u8], input_type: InputType) -> FuzzResult &mut env, command, Channel::MainHid(ChannelID::arbitrary(unstructured)?), - CtapInstant::new(0), ) .ok(); @@ -276,13 +268,10 @@ pub fn split_assemble_hid_packets(data: &[u8]) -> arbitrary::Result<()> { let packets: Vec = hid_packet_iterator.collect(); if let Some((last_packet, first_packets)) = packets.split_last() { for packet in first_packets { - assert_eq!( - assembler.parse_packet(&mut env, packet, CtapInstant::new(0)), - Ok(None) - ); + assert_eq!(assembler.parse_packet(&mut env, packet), Ok(None)); } assert_eq!( - assembler.parse_packet(&mut env, last_packet, CtapInstant::new(0)), + assembler.parse_packet(&mut env, last_packet), Ok(Some(message)) ); } diff --git a/src/api/clock.rs b/src/api/clock.rs new file mode 100644 index 00000000..7cefd167 --- /dev/null +++ b/src/api/clock.rs @@ -0,0 +1,39 @@ +// Copyright 2022-2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub trait Clock { + /// Stores data for the clock to recognize if this timer is elapsed or not. + /// + /// The Clock does not keep track of the timers it creates. Therefore, they should not wrap + /// unexpectedly. A timer that is elapsed may never return to a non-elapsed state. + /// + /// A default Timer should return `true` when checked with `is_elapsed`. + type Timer: Default; + + /// Creates a new timer that expires after the given time in ms. + fn make_timer(&mut self, milliseconds: usize) -> Self::Timer; + + /// Checks whether a given timer is expired. + /// + /// Until a timer expires, this function consistently returns false. Once it expires, this + /// function consistently returns true. In particular, it is valid to continue calling this + /// function after the first time it returns true. + fn is_elapsed(&mut self, timer: &Self::Timer) -> bool; + + /// Timestamp in microseconds. + /// + /// Normal operation only needs relative time, absolute timestamps are useful for debugging. + #[cfg(feature = "debug_ctap")] + fn timestamp_us(&mut self) -> usize; +} diff --git a/src/api/connection.rs b/src/api/connection.rs index 3970b040..4d909b9f 100644 --- a/src/api/connection.rs +++ b/src/api/connection.rs @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::clock::ClockInt; -use embedded_time::duration::Milliseconds; use libtock_drivers::usb_ctap_hid::UsbEndpoint; pub enum SendOrRecvStatus { @@ -27,9 +25,5 @@ pub struct SendOrRecvError; pub type SendOrRecvResult = Result; pub trait HidConnection { - fn send_and_maybe_recv( - &mut self, - buf: &mut [u8; 64], - timeout: Milliseconds, - ) -> SendOrRecvResult; + fn send_and_maybe_recv(&mut self, buf: &mut [u8; 64], timeout_ms: usize) -> SendOrRecvResult; } diff --git a/src/api/mod.rs b/src/api/mod.rs index 1239c112..faaa5e8f 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -18,6 +18,7 @@ //! by a trait. This module gathers the API of those components. pub mod attestation_store; +pub mod clock; pub mod connection; pub mod customization; pub mod firmware_protection; diff --git a/src/api/user_presence.rs b/src/api/user_presence.rs index 989945c6..72531937 100644 --- a/src/api/user_presence.rs +++ b/src/api/user_presence.rs @@ -12,9 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::clock::ClockInt; -use embedded_time::duration::Milliseconds; - #[derive(Debug)] pub enum UserPresenceError { /// User explicitly declined user presence check. @@ -36,7 +33,7 @@ pub trait UserPresence { /// Waits until user presence is confirmed, rejected, or the given timeout expires. /// /// Must be called between calls to [`Self::check_init`] and [`Self::check_complete`]. - fn wait_with_timeout(&mut self, timeout: Milliseconds) -> UserPresenceResult; + fn wait_with_timeout(&mut self, timeout_ms: usize) -> UserPresenceResult; /// Finalizes a user presence check. /// diff --git a/src/clock.rs b/src/clock.rs deleted file mode 100644 index 04a868da..00000000 --- a/src/clock.rs +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2022 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#[cfg(not(feature = "std"))] -use alloc::fmt; -use embedded_time::duration::Milliseconds; -pub use embedded_time::Clock; -#[cfg(not(feature = "std"))] -use libtock_drivers::result::FlexUnwrap; - -#[cfg(not(feature = "std"))] -pub struct LibtockClock(libtock_drivers::timer::Timer<'static>); -#[cfg(not(feature = "std"))] -impl LibtockClock { - pub fn new() -> Self { - let boxed_cb = alloc::boxed::Box::new(libtock_drivers::timer::with_callback(|_, _| {})); - let timer = alloc::boxed::Box::leak(boxed_cb).init().flex_unwrap(); - Self(timer) - } -} -#[cfg(not(feature = "std"))] -impl fmt::Debug for LibtockClock { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("LibtockClock") - .field("CLOCK_FREQUENCY", &CLOCK_FREQUENCY) - .finish() - } -} - -pub const KEEPALIVE_DELAY_MS: ClockInt = 100; -pub const KEEPALIVE_DELAY: Milliseconds = Milliseconds(KEEPALIVE_DELAY_MS); - -#[cfg(target_pointer_width = "32")] -pub type ClockInt = u32; -#[cfg(target_pointer_width = "64")] -pub type ClockInt = u64; - -#[cfg(not(feature = "std"))] -impl embedded_time::Clock for LibtockClock { - // TODO: Implement and use a 24-bits TimeInt for Nordic - type T = ClockInt; - - const SCALING_FACTOR: embedded_time::fraction::Fraction = - ::new(1, CLOCK_FREQUENCY); - - fn try_now(&self) -> Result, embedded_time::clock::Error> { - let timer = &self.0; - let now = timer.get_current_clock().flex_unwrap(); - Ok(embedded_time::Instant::new(now.num_ticks() as Self::T)) - } -} - -#[cfg(not(feature = "std"))] -pub type CtapClock = LibtockClock<32768>; -#[cfg(feature = "std")] -pub type CtapClock = TestClock; - -pub fn new_clock() -> CtapClock { - CtapClock::new() -} - -pub type CtapInstant = embedded_time::Instant; - -#[cfg(feature = "std")] -pub const TEST_CLOCK_FREQUENCY_HZ: u32 = 32768; - -#[cfg(feature = "std")] -#[derive(Default, Clone, Copy, Debug)] -pub struct TestClock; -#[cfg(feature = "std")] -impl TestClock { - pub fn new() -> Self { - TestClock - } -} - -#[cfg(feature = "std")] -impl embedded_time::Clock for TestClock { - type T = u64; - const SCALING_FACTOR: embedded_time::fraction::Fraction = - ::new(1, TEST_CLOCK_FREQUENCY_HZ); - - fn try_now(&self) -> Result, embedded_time::clock::Error> { - Ok(embedded_time::Instant::new(0)) - } -} - -#[cfg(test)] -mod test { - use super::*; - use embedded_time::duration::{Milliseconds, Seconds}; - - #[test] - fn test_checked_add() { - let now = CtapInstant::new(0); - assert_eq!( - now.checked_add(Seconds::new(1 as ClockInt)), - Some(CtapInstant::new(TEST_CLOCK_FREQUENCY_HZ as ClockInt)) - ); - assert_eq!( - now.checked_add(Seconds::new(1 as ClockInt)), - now.checked_add(Milliseconds::new(1000 as ClockInt)) - ); - } - - #[test] - fn test_checked_add_overflow() { - assert_eq!( - CtapInstant::new(u64::MAX).checked_add(Seconds::new(1 as ClockInt)), - Some(CtapInstant::new(TEST_CLOCK_FREQUENCY_HZ as u64 - 1)) - ); - } - - #[test] - fn test_checked_add_error() { - assert!(CtapInstant::new(0) - .checked_add(Seconds::new(u64::MAX / TEST_CLOCK_FREQUENCY_HZ as ClockInt)) - .is_none()); - assert!(CtapInstant::new(0) - .checked_add(Seconds::new( - u64::MAX / TEST_CLOCK_FREQUENCY_HZ as ClockInt / 2 - )) - .is_some()); - assert!(CtapInstant::new(0) - .checked_add(Seconds::new( - u64::MAX / TEST_CLOCK_FREQUENCY_HZ as ClockInt / 2 + 1 - )) - .is_none()); - } - - #[test] - fn test_duration_since() { - let early = CtapInstant::new(0); - let later = CtapInstant::new(1000); - assert_eq!( - later.checked_duration_since(&early).unwrap().integer(), - 1000 - ); - assert_eq!(early.checked_duration_since(&later), None); - } - - #[test] - fn test_duration_since_overflow() { - let early = CtapInstant::new(u64::MAX); - let later = CtapInstant::new(1000); - assert_eq!( - later.checked_duration_since(&early).unwrap().integer(), - 1001 - ); - assert_eq!(early.checked_duration_since(&later), None); - } - - #[test] - #[should_panic] - fn add_panic() { - let _ = - CtapInstant::new(0) + Seconds(u64::MAX / TEST_CLOCK_FREQUENCY_HZ as ClockInt / 2 + 1); - } -} diff --git a/src/ctap/client_pin.rs b/src/ctap/client_pin.rs index 12d5b642..8cfca6ea 100644 --- a/src/ctap/client_pin.rs +++ b/src/ctap/client_pin.rs @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::super::clock::CtapInstant; use super::command::AuthenticatorClientPinParameters; use super::data_formats::{ ok_or_missing, ClientPinSubCommand, CoseKey, GetAssertionHmacSecretInput, PinUvAuthProtocol, @@ -33,7 +32,6 @@ use crypto::sha256::Sha256; use crypto::Hash256; #[cfg(test)] use enum_iterator::IntoEnumIterator; -use rng256::Rng256; use subtle::ConstantTimeEq; /// The prefix length of the PIN hash that is stored and compared. @@ -106,18 +104,18 @@ pub enum PinPermission { AuthenticatorConfiguration = 0x20, } -pub struct ClientPin { +pub struct ClientPin { pin_protocol_v1: PinProtocol, pin_protocol_v2: PinProtocol, consecutive_pin_mismatches: u8, - pin_uv_auth_token_state: PinUvAuthTokenState, + pin_uv_auth_token_state: PinUvAuthTokenState, } -impl ClientPin { - pub fn new(rng: &mut impl Rng256) -> ClientPin { +impl ClientPin { + pub fn new(env: &mut E) -> Self { ClientPin { - pin_protocol_v1: PinProtocol::new(rng), - pin_protocol_v2: PinProtocol::new(rng), + pin_protocol_v1: PinProtocol::new(env.rng()), + pin_protocol_v2: PinProtocol::new(env.rng()), consecutive_pin_mismatches: 0, pin_uv_auth_token_state: PinUvAuthTokenState::new(), } @@ -159,7 +157,7 @@ impl ClientPin { /// Also, in case of failure, the key agreement key is randomly reset. fn verify_pin_hash_enc( &mut self, - env: &mut impl Env, + env: &mut E, pin_uv_auth_protocol: PinUvAuthProtocol, shared_secret: &dyn SharedSecret, pin_hash_enc: Vec, @@ -197,7 +195,7 @@ impl ClientPin { fn process_get_pin_retries( &self, - env: &mut impl Env, + env: &mut E, ) -> Result { Ok(AuthenticatorClientPinResponse { key_agreement: None, @@ -225,7 +223,7 @@ impl ClientPin { fn process_set_pin( &mut self, - env: &mut impl Env, + env: &mut E, client_pin_params: AuthenticatorClientPinParameters, ) -> Result<(), Ctap2StatusCode> { let AuthenticatorClientPinParameters { @@ -252,7 +250,7 @@ impl ClientPin { fn process_change_pin( &mut self, - env: &mut impl Env, + env: &mut E, client_pin_params: AuthenticatorClientPinParameters, ) -> Result<(), Ctap2StatusCode> { let AuthenticatorClientPinParameters { @@ -290,9 +288,8 @@ impl ClientPin { fn process_get_pin_token( &mut self, - env: &mut impl Env, + env: &mut E, client_pin_params: AuthenticatorClientPinParameters, - now: CtapInstant, ) -> Result { let AuthenticatorClientPinParameters { pin_uv_auth_protocol, @@ -325,7 +322,7 @@ impl ClientPin { self.pin_protocol_v1.reset_pin_uv_auth_token(env.rng()); self.pin_protocol_v2.reset_pin_uv_auth_token(env.rng()); self.pin_uv_auth_token_state - .begin_using_pin_uv_auth_token(now); + .begin_using_pin_uv_auth_token(env); self.pin_uv_auth_token_state.set_default_permissions(); let pin_uv_auth_token = shared_secret.encrypt( env.rng(), @@ -358,9 +355,8 @@ impl ClientPin { fn process_get_pin_uv_auth_token_using_pin_with_permissions( &mut self, - env: &mut impl Env, + env: &mut E, mut client_pin_params: AuthenticatorClientPinParameters, - now: CtapInstant, ) -> Result { // Mutating client_pin_params is just an optimization to move it into // process_get_pin_token, without cloning permissions_rp_id here. @@ -376,7 +372,7 @@ impl ClientPin { return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER); } - let response = self.process_get_pin_token(env, client_pin_params, now)?; + let response = self.process_get_pin_token(env, client_pin_params)?; self.pin_uv_auth_token_state.set_permissions(permissions); self.pin_uv_auth_token_state .set_permissions_rp_id(permissions_rp_id); @@ -387,9 +383,8 @@ impl ClientPin { /// Processes the authenticatorClientPin command. pub fn process_command( &mut self, - env: &mut impl Env, + env: &mut E, client_pin_params: AuthenticatorClientPinParameters, - now: CtapInstant, ) -> Result { if !env.customization().allows_pin_protocol_v1() && client_pin_params.pin_uv_auth_protocol == PinUvAuthProtocol::V1 @@ -410,7 +405,7 @@ impl ClientPin { None } ClientPinSubCommand::GetPinToken => { - Some(self.process_get_pin_token(env, client_pin_params, now)?) + Some(self.process_get_pin_token(env, client_pin_params)?) } ClientPinSubCommand::GetPinUvAuthTokenUsingUvWithPermissions => Some( self.process_get_pin_uv_auth_token_using_uv_with_permissions(client_pin_params)?, @@ -420,7 +415,6 @@ impl ClientPin { self.process_get_pin_uv_auth_token_using_pin_with_permissions( env, client_pin_params, - now, )?, ), }; @@ -447,11 +441,11 @@ impl ClientPin { } /// Resets all held state. - pub fn reset(&mut self, rng: &mut impl Rng256) { - self.pin_protocol_v1.regenerate(rng); - self.pin_protocol_v1.reset_pin_uv_auth_token(rng); - self.pin_protocol_v2.regenerate(rng); - self.pin_protocol_v2.reset_pin_uv_auth_token(rng); + pub fn reset(&mut self, env: &mut E) { + self.pin_protocol_v1.regenerate(env.rng()); + self.pin_protocol_v1.reset_pin_uv_auth_token(env.rng()); + self.pin_protocol_v2.regenerate(env.rng()); + self.pin_protocol_v2.reset_pin_uv_auth_token(env.rng()); self.consecutive_pin_mismatches = 0; self.pin_uv_auth_token_state.stop_using_pin_uv_auth_token(); } @@ -466,7 +460,7 @@ impl ClientPin { /// 32 byte. pub fn process_hmac_secret( &self, - rng: &mut impl Rng256, + env: &mut E, hmac_secret_input: GetAssertionHmacSecretInput, cred_random: &[u8; 32], ) -> Result, Ctap2StatusCode> { @@ -490,7 +484,7 @@ impl ClientPin { let mut output2 = hmac_256::(cred_random, &decrypted_salts[32..]).to_vec(); output.append(&mut output2); } - shared_secret.encrypt(rng, &output) + shared_secret.encrypt(env.rng(), &output) } /// Consumes flags and permissions related to the pinUvAuthToken. @@ -501,9 +495,9 @@ impl ClientPin { } /// Updates the running timers, triggers timeout events. - pub fn update_timeouts(&mut self, now: CtapInstant) { + pub fn update_timeouts(&mut self, env: &mut E) { self.pin_uv_auth_token_state - .pin_uv_auth_token_usage_timer_observer(now); + .pin_uv_auth_token_usage_timer_observer(env); } /// Checks if user verification is cached for use of the pinUvAuthToken. @@ -563,19 +557,19 @@ impl ClientPin { #[cfg(test)] pub fn new_test( + env: &mut E, key_agreement_key: crypto::ecdh::SecKey, pin_uv_auth_token: [u8; PIN_TOKEN_LENGTH], pin_uv_auth_protocol: PinUvAuthProtocol, - ) -> ClientPin { - let mut env = crate::env::test::TestEnv::new(); + ) -> Self { let (key_agreement_key_v1, key_agreement_key_v2) = match pin_uv_auth_protocol { PinUvAuthProtocol::V1 => (key_agreement_key, crypto::ecdh::SecKey::gensk(env.rng())), PinUvAuthProtocol::V2 => (crypto::ecdh::SecKey::gensk(env.rng()), key_agreement_key), }; let mut pin_uv_auth_token_state = PinUvAuthTokenState::new(); pin_uv_auth_token_state.set_permissions(0xFF); - pin_uv_auth_token_state.begin_using_pin_uv_auth_token(CtapInstant::new(0)); - ClientPin { + pin_uv_auth_token_state.begin_using_pin_uv_auth_token(env); + Self { pin_protocol_v1: PinProtocol::new_test(key_agreement_key_v1, pin_uv_auth_token), pin_protocol_v2: PinProtocol::new_test(key_agreement_key_v2, pin_uv_auth_token), consecutive_pin_mismatches: 0, @@ -590,7 +584,6 @@ mod test { use super::*; use crate::env::test::TestEnv; use alloc::vec; - use embedded_time::duration::Milliseconds; /// Stores a PIN hash corresponding to the dummy PIN "1234". fn set_standard_pin(env: &mut TestEnv) { @@ -618,14 +611,18 @@ mod test { /// should fail. fn create_client_pin_and_shared_secret( pin_uv_auth_protocol: PinUvAuthProtocol, - ) -> (ClientPin, Box) { + ) -> (ClientPin, Box) { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pk = key_agreement_key.genpk(); let key_agreement = CoseKey::from(pk); let pin_uv_auth_token = [0x91; PIN_TOKEN_LENGTH]; - let client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol); + let client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + pin_uv_auth_protocol, + ); let shared_secret = client_pin .get_pin_protocol(pin_uv_auth_protocol) .decapsulate(key_agreement, pin_uv_auth_protocol) @@ -639,7 +636,7 @@ mod test { fn create_client_pin_and_parameters( pin_uv_auth_protocol: PinUvAuthProtocol, sub_command: ClientPinSubCommand, - ) -> (ClientPin, AuthenticatorClientPinParameters) { + ) -> (ClientPin, AuthenticatorClientPinParameters) { let mut env = TestEnv::new(); let (client_pin, shared_secret) = create_client_pin_and_shared_secret(pin_uv_auth_protocol); @@ -683,7 +680,7 @@ mod test { #[test] fn test_mix_pin_protocols() { let mut env = TestEnv::new(); - let client_pin = ClientPin::new(env.rng()); + let client_pin = ClientPin::::new(&mut env); let pin_protocol_v1 = client_pin.get_pin_protocol(PinUvAuthProtocol::V1); let pin_protocol_v2 = client_pin.get_pin_protocol(PinUvAuthProtocol::V2); let message = vec![0xAA; 16]; @@ -724,7 +721,7 @@ mod test { fn test_helper_verify_pin_hash_enc(pin_uv_auth_protocol: PinUvAuthProtocol) { let mut env = TestEnv::new(); - let mut client_pin = ClientPin::new(env.rng()); + let mut client_pin = ClientPin::::new(&mut env); let pin_protocol = client_pin.get_pin_protocol(pin_uv_auth_protocol); let shared_secret = pin_protocol .decapsulate(pin_protocol.get_public_key(), pin_uv_auth_protocol) @@ -823,7 +820,7 @@ mod test { power_cycle_state: Some(false), }); assert_eq!( - client_pin.process_command(&mut env, params.clone(), CtapInstant::new(0)), + client_pin.process_command(&mut env, params.clone()), Ok(ResponseData::AuthenticatorClientPin(expected_response)) ); @@ -835,7 +832,7 @@ mod test { power_cycle_state: Some(true), }); assert_eq!( - client_pin.process_command(&mut env, params, CtapInstant::new(0)), + client_pin.process_command(&mut env, params), Ok(ResponseData::AuthenticatorClientPin(expected_response)) ); } @@ -863,7 +860,7 @@ mod test { power_cycle_state: None, }); assert_eq!( - client_pin.process_command(&mut env, params, CtapInstant::new(0)), + client_pin.process_command(&mut env, params), Ok(ResponseData::AuthenticatorClientPin(expected_response)) ); } @@ -887,7 +884,7 @@ mod test { let mut env = TestEnv::new(); env.customization_mut().set_allows_pin_protocol_v1(false); assert_eq!( - client_pin.process_command(&mut env, params, CtapInstant::new(0)), + client_pin.process_command(&mut env, params), Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER) ); } @@ -897,7 +894,7 @@ mod test { create_client_pin_and_parameters(pin_uv_auth_protocol, ClientPinSubCommand::SetPin); let mut env = TestEnv::new(); assert_eq!( - client_pin.process_command(&mut env, params, CtapInstant::new(0)), + client_pin.process_command(&mut env, params), Ok(ResponseData::AuthenticatorClientPin(None)) ); } @@ -930,14 +927,14 @@ mod test { let pin_uv_auth_param = shared_secret.authenticate(&auth_param_data); params.pin_uv_auth_param = Some(pin_uv_auth_param); assert_eq!( - client_pin.process_command(&mut env, params.clone(), CtapInstant::new(0)), + client_pin.process_command(&mut env, params.clone()), Ok(ResponseData::AuthenticatorClientPin(None)) ); let mut bad_params = params.clone(); bad_params.pin_hash_enc = Some(vec![0xEE; 16]); assert_eq!( - client_pin.process_command(&mut env, bad_params, CtapInstant::new(0)), + client_pin.process_command(&mut env, bad_params), Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID) ); @@ -945,7 +942,7 @@ mod test { storage::decr_pin_retries(&mut env).unwrap(); } assert_eq!( - client_pin.process_command(&mut env, params, CtapInstant::new(0)), + client_pin.process_command(&mut env, params), Err(Ctap2StatusCode::CTAP2_ERR_PIN_BLOCKED) ); } @@ -976,7 +973,7 @@ mod test { set_standard_pin(&mut env); let response = client_pin - .process_command(&mut env, params.clone(), CtapInstant::new(0)) + .process_command(&mut env, params.clone()) .unwrap(); let encrypted_token = match response { ResponseData::AuthenticatorClientPin(Some(response)) => { @@ -1012,7 +1009,7 @@ mod test { let mut bad_params = params; bad_params.pin_hash_enc = Some(vec![0xEE; 16]); assert_eq!( - client_pin.process_command(&mut env, bad_params, CtapInstant::new(0)), + client_pin.process_command(&mut env, bad_params), Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID) ); } @@ -1037,7 +1034,7 @@ mod test { assert_eq!(storage::force_pin_change(&mut env), Ok(())); assert_eq!( - client_pin.process_command(&mut env, params, CtapInstant::new(0)), + client_pin.process_command(&mut env, params), Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID), ); } @@ -1070,7 +1067,7 @@ mod test { set_standard_pin(&mut env); let response = client_pin - .process_command(&mut env, params.clone(), CtapInstant::new(0)) + .process_command(&mut env, params.clone()) .unwrap(); let encrypted_token = match response { ResponseData::AuthenticatorClientPin(Some(response)) => { @@ -1106,21 +1103,21 @@ mod test { let mut bad_params = params.clone(); bad_params.permissions = Some(0x00); assert_eq!( - client_pin.process_command(&mut env, bad_params, CtapInstant::new(0)), + client_pin.process_command(&mut env, bad_params), Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER) ); let mut bad_params = params.clone(); bad_params.permissions_rp_id = None; assert_eq!( - client_pin.process_command(&mut env, bad_params, CtapInstant::new(0)), + client_pin.process_command(&mut env, bad_params), Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER) ); let mut bad_params = params; bad_params.pin_hash_enc = Some(vec![0xEE; 16]); assert_eq!( - client_pin.process_command(&mut env, bad_params, CtapInstant::new(0)), + client_pin.process_command(&mut env, bad_params), Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID) ); } @@ -1147,7 +1144,7 @@ mod test { assert_eq!(storage::force_pin_change(&mut env), Ok(())); assert_eq!( - client_pin.process_command(&mut env, params, CtapInstant::new(0)), + client_pin.process_command(&mut env, params), Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID) ); } @@ -1281,7 +1278,7 @@ mod test { salt_auth, pin_uv_auth_protocol, }; - let output = client_pin.process_hmac_secret(env.rng(), hmac_secret_input, cred_random); + let output = client_pin.process_hmac_secret(&mut env, hmac_secret_input, cred_random); output.map(|v| shared_secret.as_ref().decrypt(&v).unwrap()) } @@ -1301,7 +1298,7 @@ mod test { salt_auth, pin_uv_auth_protocol, }; - let output = client_pin.process_hmac_secret(env.rng(), hmac_secret_input, &cred_random); + let output = client_pin.process_hmac_secret(&mut env, hmac_secret_input, &cred_random); assert_eq!(output, Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)); } @@ -1403,7 +1400,7 @@ mod test { #[test] fn test_has_permission() { let mut env = TestEnv::new(); - let mut client_pin = ClientPin::new(env.rng()); + let mut client_pin = ClientPin::::new(&mut env); client_pin.pin_uv_auth_token_state.set_permissions(0x7F); for permission in PinPermission::into_enum_iter() { assert_eq!( @@ -1427,7 +1424,7 @@ mod test { #[test] fn test_has_no_rp_id_permission() { let mut env = TestEnv::new(); - let mut client_pin = ClientPin::new(env.rng()); + let mut client_pin = ClientPin::::new(&mut env); assert_eq!(client_pin.has_no_rp_id_permission(), Ok(())); client_pin .pin_uv_auth_token_state @@ -1441,7 +1438,7 @@ mod test { #[test] fn test_has_no_or_rp_id_permission() { let mut env = TestEnv::new(); - let mut client_pin = ClientPin::new(env.rng()); + let mut client_pin = ClientPin::::new(&mut env); assert_eq!(client_pin.has_no_or_rp_id_permission("example.com"), Ok(())); client_pin .pin_uv_auth_token_state @@ -1456,7 +1453,7 @@ mod test { #[test] fn test_has_no_or_rp_id_hash_permission() { let mut env = TestEnv::new(); - let mut client_pin = ClientPin::new(env.rng()); + let mut client_pin = ClientPin::::new(&mut env); let rp_id_hash = Sha256::hash(b"example.com"); assert_eq!( client_pin.has_no_or_rp_id_hash_permission(&rp_id_hash), @@ -1478,7 +1475,7 @@ mod test { #[test] fn test_ensure_rp_id_permission() { let mut env = TestEnv::new(); - let mut client_pin = ClientPin::new(env.rng()); + let mut client_pin = ClientPin::::new(&mut env); assert_eq!(client_pin.ensure_rp_id_permission("example.com"), Ok(())); assert_eq!( client_pin @@ -1496,11 +1493,11 @@ mod test { #[test] fn test_verify_pin_uv_auth_token() { let mut env = TestEnv::new(); - let mut client_pin = ClientPin::new(env.rng()); + let mut client_pin = ClientPin::::new(&mut env); let message = [0xAA]; client_pin .pin_uv_auth_token_state - .begin_using_pin_uv_auth_token(CtapInstant::new(0)); + .begin_using_pin_uv_auth_token(&mut env); let pin_uv_auth_token_v1 = client_pin .get_pin_protocol(PinUvAuthProtocol::V1) @@ -1570,7 +1567,7 @@ mod test { #[test] fn test_verify_pin_uv_auth_token_not_in_use() { let mut env = TestEnv::new(); - let client_pin = ClientPin::new(env.rng()); + let client_pin = ClientPin::::new(&mut env); let message = [0xAA]; let pin_uv_auth_token_v1 = client_pin @@ -1592,7 +1589,7 @@ mod test { #[test] fn test_reset() { let mut env = TestEnv::new(); - let mut client_pin = ClientPin::new(env.rng()); + let mut client_pin = ClientPin::::new(&mut env); let public_key_v1 = client_pin.pin_protocol_v1.get_public_key(); let public_key_v2 = client_pin.pin_protocol_v2.get_public_key(); let token_v1 = *client_pin.pin_protocol_v1.get_pin_uv_auth_token(); @@ -1601,7 +1598,7 @@ mod test { client_pin .pin_uv_auth_token_state .set_permissions_rp_id(Some(String::from("example.com"))); - client_pin.reset(env.rng()); + client_pin.reset(&mut env); assert_ne!(public_key_v1, client_pin.pin_protocol_v1.get_public_key()); assert_ne!(public_key_v2, client_pin.pin_protocol_v2.get_public_key()); assert_ne!( @@ -1631,9 +1628,7 @@ mod test { set_standard_pin(&mut env); params.permissions = Some(0xFF); - assert!(client_pin - .process_command(&mut env, params, CtapInstant::new(0)) - .is_ok()); + assert!(client_pin.process_command(&mut env, params).is_ok()); for permission in PinPermission::into_enum_iter() { assert_eq!( client_pin @@ -1649,8 +1644,8 @@ mod test { Ok(()) ); - let timeout = CtapInstant::new(0) + Milliseconds::new(30001_u32); - client_pin.update_timeouts(timeout); + env.clock().advance(30001); + client_pin.update_timeouts(&mut env); for permission in PinPermission::into_enum_iter() { assert_eq!( client_pin @@ -1677,9 +1672,7 @@ mod test { set_standard_pin(&mut env); params.permissions = Some(0xFF); - assert!(client_pin - .process_command(&mut env, params, CtapInstant::new(0)) - .is_ok()); + assert!(client_pin.process_command(&mut env, params).is_ok()); for permission in PinPermission::into_enum_iter() { assert_eq!( client_pin diff --git a/src/ctap/config_command.rs b/src/ctap/config_command.rs index 2fad2719..8deba2b3 100644 --- a/src/ctap/config_command.rs +++ b/src/ctap/config_command.rs @@ -73,9 +73,9 @@ fn process_set_min_pin_length( } /// Processes the AuthenticatorConfig command. -pub fn process_config( - env: &mut impl Env, - client_pin: &mut ClientPin, +pub fn process_config( + env: &mut E, + client_pin: &mut ClientPin, params: AuthenticatorConfigParameters, ) -> Result { let AuthenticatorConfigParameters { @@ -133,8 +133,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let config_params = AuthenticatorConfigParameters { sub_command: ConfigSubCommand::EnableEnterpriseAttestation, @@ -160,8 +164,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let config_params = AuthenticatorConfigParameters { sub_command: ConfigSubCommand::ToggleAlwaysUv, @@ -195,8 +203,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + pin_uv_auth_protocol, + ); storage::set_pin(&mut env, &[0x88; 16], 4).unwrap(); let mut config_data = vec![0xFF; 32]; @@ -265,8 +277,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); // First, increase minimum PIN length from 4 to 6 without PIN auth. let min_pin_length = 6; @@ -309,8 +325,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); // First, set RP IDs without PIN auth. let min_pin_length = 6; @@ -385,8 +405,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); storage::set_pin(&mut env, &[0x88; 16], 4).unwrap(); // Increase min PIN, force PIN change. @@ -408,8 +432,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); storage::set_pin(&mut env, &[0x88; 16], 4).unwrap(); let pin_uv_auth_param = Some(vec![ @@ -439,8 +467,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let config_params = AuthenticatorConfigParameters { sub_command: ConfigSubCommand::VendorPrototype, diff --git a/src/ctap/credential_management.rs b/src/ctap/credential_management.rs index aee9e3af..f560ee1e 100644 --- a/src/ctap/credential_management.rs +++ b/src/ctap/credential_management.rs @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::super::clock::CtapInstant; use super::client_pin::{ClientPin, PinPermission}; use super::command::AuthenticatorCredentialManagementParameters; use super::data_formats::{ @@ -108,9 +107,9 @@ fn enumerate_credentials_response( /// Check if the token permissions have the correct associated RP ID. /// /// Either no RP ID is associated, or the RP ID matches the stored credential. -fn check_rp_id_permissions( - env: &mut impl Env, - client_pin: &mut ClientPin, +fn check_rp_id_permissions( + env: &mut E, + client_pin: &mut ClientPin, credential_id: &[u8], ) -> Result<(), Ctap2StatusCode> { // Pre-check a sufficient condition before calling the store. @@ -135,17 +134,16 @@ fn process_get_creds_metadata( } /// Processes the subcommand enumerateRPsBegin for CredentialManagement. -fn process_enumerate_rps_begin( - env: &mut impl Env, - stateful_command_permission: &mut StatefulPermission, +fn process_enumerate_rps_begin( + env: &mut E, + stateful_command_permission: &mut StatefulPermission, channel: Channel, - now: CtapInstant, ) -> Result { let rp_set = get_stored_rp_ids(env)?; let total_rps = rp_set.len(); if total_rps > 1 { - stateful_command_permission.set_command(now, StatefulCommand::EnumerateRps(1), channel); + stateful_command_permission.set_command(env, StatefulCommand::EnumerateRps(1), channel); } // TODO /~https://github.com/rust-lang/rust/issues/62924 replace with pop_first() let rp_id = rp_set @@ -156,9 +154,9 @@ fn process_enumerate_rps_begin( } /// Processes the subcommand enumerateRPsGetNextRP for CredentialManagement. -fn process_enumerate_rps_get_next_rp( - env: &mut impl Env, - stateful_command_permission: &mut StatefulPermission, +fn process_enumerate_rps_get_next_rp( + env: &mut E, + stateful_command_permission: &mut StatefulPermission, ) -> Result { let rp_id_index = stateful_command_permission.next_enumerate_rp()?; let rp_set = get_stored_rp_ids(env)?; @@ -171,13 +169,12 @@ fn process_enumerate_rps_get_next_rp( } /// Processes the subcommand enumerateCredentialsBegin for CredentialManagement. -fn process_enumerate_credentials_begin( - env: &mut impl Env, - stateful_command_permission: &mut StatefulPermission, - client_pin: &mut ClientPin, +fn process_enumerate_credentials_begin( + env: &mut E, + stateful_command_permission: &mut StatefulPermission, + client_pin: &mut ClientPin, sub_command_params: CredentialManagementSubCommandParameters, channel: Channel, - now: CtapInstant, ) -> Result { let rp_id_hash = sub_command_params .rp_id_hash @@ -203,7 +200,7 @@ fn process_enumerate_credentials_begin( let credential = storage::get_credential(env, current_key)?; if total_credentials > 1 { stateful_command_permission.set_command( - now, + env, StatefulCommand::EnumerateCredentials(rp_credentials), channel, ); @@ -212,9 +209,9 @@ fn process_enumerate_credentials_begin( } /// Processes the subcommand enumerateCredentialsGetNextCredential for CredentialManagement. -fn process_enumerate_credentials_get_next_credential( - env: &mut impl Env, - stateful_command_permission: &mut StatefulPermission, +fn process_enumerate_credentials_get_next_credential( + env: &mut E, + stateful_command_permission: &mut StatefulPermission, ) -> Result { let credential_key = stateful_command_permission.next_enumerate_credential()?; let credential = storage::get_credential(env, credential_key)?; @@ -222,9 +219,9 @@ fn process_enumerate_credentials_get_next_credential( } /// Processes the subcommand deleteCredential for CredentialManagement. -fn process_delete_credential( - env: &mut impl Env, - client_pin: &mut ClientPin, +fn process_delete_credential( + env: &mut E, + client_pin: &mut ClientPin, sub_command_params: CredentialManagementSubCommandParameters, ) -> Result<(), Ctap2StatusCode> { let credential_id = sub_command_params @@ -236,9 +233,9 @@ fn process_delete_credential( } /// Processes the subcommand updateUserInformation for CredentialManagement. -fn process_update_user_information( - env: &mut impl Env, - client_pin: &mut ClientPin, +fn process_update_user_information( + env: &mut E, + client_pin: &mut ClientPin, sub_command_params: CredentialManagementSubCommandParameters, ) -> Result<(), Ctap2StatusCode> { let credential_id = sub_command_params @@ -253,13 +250,12 @@ fn process_update_user_information( } /// Processes the CredentialManagement command and all its subcommands. -pub fn process_credential_management( - env: &mut impl Env, - stateful_command_permission: &mut StatefulPermission, - client_pin: &mut ClientPin, +pub fn process_credential_management( + env: &mut E, + stateful_command_permission: &mut StatefulPermission, + client_pin: &mut ClientPin, cred_management_params: AuthenticatorCredentialManagementParameters, channel: Channel, - now: CtapInstant, ) -> Result { let AuthenticatorCredentialManagementParameters { sub_command, @@ -319,7 +315,6 @@ pub fn process_credential_management( env, stateful_command_permission, channel, - now, )?) } CredentialManagementSubCommand::EnumerateRpsGetNextRp => Some( @@ -332,7 +327,6 @@ pub fn process_credential_management( client_pin, sub_command_params.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?, channel, - now, )?) } CredentialManagementSubCommand::EnumerateCredentialsGetNextCredential => Some( @@ -392,11 +386,15 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol); + let client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + pin_uv_auth_protocol, + ); let credential_source = create_credential_source(&mut env); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::new(&mut env); ctap_state.client_pin = client_pin; storage::set_pin(&mut env, &[0u8; 16], 4).unwrap(); @@ -419,7 +417,6 @@ mod test { &mut ctap_state.client_pin, cred_management_params, DUMMY_CHANNEL, - CtapInstant::new(0), ); let initial_capacity = match cred_management_response.unwrap() { ResponseData::AuthenticatorCredentialManagement(Some(response)) => { @@ -445,7 +442,6 @@ mod test { &mut ctap_state.client_pin, cred_management_params, DUMMY_CHANNEL, - CtapInstant::new(0), ); match cred_management_response.unwrap() { ResponseData::AuthenticatorCredentialManagement(Some(response)) => { @@ -474,13 +470,17 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let credential_source1 = create_credential_source(&mut env); let mut credential_source2 = create_credential_source(&mut env); credential_source2.rp_id = "another.example.com".to_string(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::new(&mut env); ctap_state.client_pin = client_pin; storage::store_credential(&mut env, credential_source1).unwrap(); @@ -504,7 +504,6 @@ mod test { &mut ctap_state.client_pin, cred_management_params, DUMMY_CHANNEL, - CtapInstant::new(0), ); let first_rp_id = match cred_management_response.unwrap() { ResponseData::AuthenticatorCredentialManagement(Some(response)) => { @@ -529,7 +528,6 @@ mod test { &mut ctap_state.client_pin, cred_management_params, DUMMY_CHANNEL, - CtapInstant::new(0), ); let second_rp_id = match cred_management_response.unwrap() { ResponseData::AuthenticatorCredentialManagement(Some(response)) => { @@ -555,7 +553,6 @@ mod test { &mut ctap_state.client_pin, cred_management_params, DUMMY_CHANNEL, - CtapInstant::new(0), ); assert_eq!( cred_management_response, @@ -568,11 +565,15 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let credential_source = create_credential_source(&mut env); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::new(&mut env); ctap_state.client_pin = client_pin; const NUM_CREDENTIALS: usize = 20; @@ -605,7 +606,6 @@ mod test { &mut ctap_state.client_pin, cred_management_params, DUMMY_CHANNEL, - CtapInstant::new(0), ); match cred_management_response.unwrap() { ResponseData::AuthenticatorCredentialManagement(Some(response)) => { @@ -636,7 +636,6 @@ mod test { &mut ctap_state.client_pin, cred_management_params, DUMMY_CHANNEL, - CtapInstant::new(0), ); assert_eq!( cred_management_response, @@ -649,8 +648,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let credential_source1 = create_credential_source(&mut env); let mut credential_source2 = create_credential_source(&mut env); credential_source2.user_handle = vec![0x02]; @@ -658,7 +661,7 @@ mod test { credential_source2.user_display_name = Some("User Two".to_string()); credential_source2.user_icon = Some("icon2".to_string()); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::new(&mut env); ctap_state.client_pin = client_pin; storage::store_credential(&mut env, credential_source1).unwrap(); @@ -689,7 +692,6 @@ mod test { &mut ctap_state.client_pin, cred_management_params, DUMMY_CHANNEL, - CtapInstant::new(0), ); let first_credential_id = match cred_management_response.unwrap() { ResponseData::AuthenticatorCredentialManagement(Some(response)) => { @@ -713,7 +715,6 @@ mod test { &mut ctap_state.client_pin, cred_management_params, DUMMY_CHANNEL, - CtapInstant::new(0), ); let second_credential_id = match cred_management_response.unwrap() { ResponseData::AuthenticatorCredentialManagement(Some(response)) => { @@ -738,7 +739,6 @@ mod test { &mut ctap_state.client_pin, cred_management_params, DUMMY_CHANNEL, - CtapInstant::new(0), ); assert_eq!( cred_management_response, @@ -751,12 +751,16 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let mut credential_source = create_credential_source(&mut env); credential_source.credential_id = vec![0x1D; 32]; - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::new(&mut env); ctap_state.client_pin = client_pin; storage::store_credential(&mut env, credential_source).unwrap(); @@ -789,7 +793,6 @@ mod test { &mut ctap_state.client_pin, cred_management_params, DUMMY_CHANNEL, - CtapInstant::new(0), ); assert_eq!( cred_management_response, @@ -808,7 +811,6 @@ mod test { &mut ctap_state.client_pin, cred_management_params, DUMMY_CHANNEL, - CtapInstant::new(0), ); assert_eq!( cred_management_response, @@ -821,12 +823,16 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let mut credential_source = create_credential_source(&mut env); credential_source.credential_id = vec![0x1D; 32]; - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::new(&mut env); ctap_state.client_pin = client_pin; storage::store_credential(&mut env, credential_source).unwrap(); @@ -865,7 +871,6 @@ mod test { &mut ctap_state.client_pin, cred_management_params, DUMMY_CHANNEL, - CtapInstant::new(0), ); assert_eq!( cred_management_response, @@ -887,7 +892,7 @@ mod test { #[test] fn test_process_credential_management_invalid_pin_uv_auth_param() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::new(&mut env); storage::set_pin(&mut env, &[0u8; 16], 4).unwrap(); @@ -903,7 +908,6 @@ mod test { &mut ctap_state.client_pin, cred_management_params, DUMMY_CHANNEL, - CtapInstant::new(0), ); assert_eq!( cred_management_response, diff --git a/src/ctap/ctap1.rs b/src/ctap/ctap1.rs index 5752d05b..ae80eddc 100644 --- a/src/ctap/ctap1.rs +++ b/src/ctap/ctap1.rs @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::super::clock::CtapInstant; use super::apdu::{Apdu, ApduStatusCode}; use super::credential_id::{decrypt_credential_id, encrypt_to_credential_id}; use super::crypto_wrapper::PrivateKey; @@ -174,11 +173,10 @@ impl Ctap1Command { const VENDOR_SPECIFIC_FIRST: u8 = 0x40; const VENDOR_SPECIFIC_LAST: u8 = 0xBF; - pub fn process_command( - env: &mut impl Env, + pub fn process_command( + env: &mut E, message: &[u8], - ctap_state: &mut CtapState, - clock_value: CtapInstant, + ctap_state: &mut CtapState, ) -> Result, Ctap1StatusCode> { if !ctap_state .allows_ctap1(env) @@ -192,7 +190,7 @@ impl Ctap1Command { challenge, application, } => { - if !ctap_state.u2f_up_state.consume_up(clock_value) { + if !ctap_state.u2f_up_state.consume_up(env) { return Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED); } Ctap1Command::process_register(env, challenge, application) @@ -205,8 +203,7 @@ impl Ctap1Command { flags, } => { // The order is important due to side effects of checking user presence. - if flags == Ctap1Flags::EnforceUpAndSign - && !ctap_state.u2f_up_state.consume_up(clock_value) + if flags == Ctap1Flags::EnforceUpAndSign && !ctap_state.u2f_up_state.consume_up(env) { return Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED); } @@ -301,13 +298,13 @@ impl Ctap1Command { // +-------------------+---------+--------------+-----------------+ // + application (32B) | UP (1B) | Counter (4B) | challenge (32B) | // +-------------------+---------+--------------+-----------------+ - fn process_authenticate( - env: &mut impl Env, + fn process_authenticate( + env: &mut E, challenge: [u8; 32], application: [u8; 32], key_handle: Vec, flags: Ctap1Flags, - ctap_state: &mut CtapState, + ctap_state: &mut CtapState, ) -> Result, Ctap1StatusCode> { let credential_source = decrypt_credential_id(env, key_handle, &application) .map_err(|_| Ctap1StatusCode::SW_WRONG_DATA)?; @@ -345,9 +342,9 @@ impl Ctap1Command { mod test { use super::super::credential_id::CBOR_CREDENTIAL_ID_SIZE; use super::super::data_formats::SignatureAlgorithm; + use super::super::TOUCH_TIMEOUT_MS; use super::*; use crate::api::customization::Customization; - use crate::clock::TEST_CLOCK_FREQUENCY_HZ; use crate::ctap::storage; use crate::env::test::TestEnv; use crypto::Hash256; @@ -394,15 +391,14 @@ mod test { let mut env = TestEnv::new(); env.user_presence() .set(|| panic!("Unexpected user presence check in CTAP1")); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::new(&mut env); storage::toggle_always_uv(&mut env).unwrap(); let application = [0x0A; 32]; let message = create_register_message(&application); - ctap_state.u2f_up_state.consume_up(CtapInstant::new(0)); - ctap_state.u2f_up_state.grant_up(CtapInstant::new(0)); - let response = - Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0)); + ctap_state.u2f_up_state.consume_up(&mut env); + ctap_state.u2f_up_state.grant_up(&mut env); + let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state); assert_eq!(response, Err(Ctap1StatusCode::SW_COMMAND_NOT_ALLOWED)); } @@ -411,14 +407,13 @@ mod test { let mut env = TestEnv::new(); env.user_presence() .set(|| panic!("Unexpected user presence check in CTAP1")); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::new(&mut env); let application = [0x0A; 32]; let message = create_register_message(&application); - ctap_state.u2f_up_state.consume_up(CtapInstant::new(0)); - ctap_state.u2f_up_state.grant_up(CtapInstant::new(0)); - let response = - Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0)); + ctap_state.u2f_up_state.consume_up(&mut env); + ctap_state.u2f_up_state.grant_up(&mut env); + let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state); // Certificate and private key are missing assert_eq!(response, Err(Ctap1StatusCode::SW_INTERNAL_EXCEPTION)); @@ -429,11 +424,9 @@ mod test { env.attestation_store() .set(&attestation_store::Id::Batch, Some(&attestation)) .unwrap(); - ctap_state.u2f_up_state.consume_up(CtapInstant::new(0)); - ctap_state.u2f_up_state.grant_up(CtapInstant::new(0)); - let response = - Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0)) - .unwrap(); + ctap_state.u2f_up_state.consume_up(&mut env); + ctap_state.u2f_up_state.grant_up(&mut env); + let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state).unwrap(); assert_eq!(response[0], Ctap1Command::LEGACY_BYTE); assert_eq!(response[66], CBOR_CREDENTIAL_ID_SIZE as u8); assert!(decrypt_credential_id( @@ -455,16 +448,12 @@ mod test { let mut env = TestEnv::new(); env.user_presence() .set(|| panic!("Unexpected user presence check in CTAP1")); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::new(&mut env); let application = [0x0A; 32]; let message = create_register_message(&application); - let response = Ctap1Command::process_command( - &mut env, - &message[..message.len() - 1], - &mut ctap_state, - CtapInstant::new(0), - ); + let response = + Ctap1Command::process_command(&mut env, &message[..message.len() - 1], &mut ctap_state); assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_LENGTH)); } @@ -477,14 +466,12 @@ mod test { let mut env = TestEnv::new(); env.user_presence() .set(|| panic!("Unexpected user presence check in CTAP1")); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::new(&mut env); - ctap_state.u2f_up_state.consume_up(CtapInstant::new(0)); - ctap_state.u2f_up_state.grant_up(CtapInstant::new(0)); - let timeout_clock_value: CtapInstant = - CtapInstant::new((30001 * TEST_CLOCK_FREQUENCY_HZ as u64) / 1000); - let response = - Ctap1Command::process_command(&mut env, &message, &mut ctap_state, timeout_clock_value); + ctap_state.u2f_up_state.consume_up(&mut env); + ctap_state.u2f_up_state.grant_up(&mut env); + env.clock().advance(TOUCH_TIMEOUT_MS); + let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state); assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED)); } @@ -494,15 +481,14 @@ mod test { env.user_presence() .set(|| panic!("Unexpected user presence check in CTAP1")); let sk = PrivateKey::new(&mut env, SignatureAlgorithm::Es256); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::new(&mut env); let rp_id = "example.com"; let application = crypto::sha256::Sha256::hash(rp_id.as_bytes()); let key_handle = encrypt_to_credential_id(&mut env, &sk, &application, None, None).unwrap(); let message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle); - let response = - Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0)); + let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state); assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED)); } @@ -512,7 +498,7 @@ mod test { env.user_presence() .set(|| panic!("Unexpected user presence check in CTAP1")); let sk = PrivateKey::new(&mut env, SignatureAlgorithm::Es256); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::new(&mut env); let rp_id = "example.com"; let application = crypto::sha256::Sha256::hash(rp_id.as_bytes()); @@ -520,8 +506,7 @@ mod test { let application = [0x55; 32]; let message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle); - let response = - Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0)); + let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state); assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_DATA)); } @@ -531,7 +516,7 @@ mod test { env.user_presence() .set(|| panic!("Unexpected user presence check in CTAP1")); let sk = PrivateKey::new(&mut env, SignatureAlgorithm::Es256); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::new(&mut env); let rp_id = "example.com"; let application = crypto::sha256::Sha256::hash(rp_id.as_bytes()); @@ -543,23 +528,19 @@ mod test { ); message.push(0x00); - let response = - Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0)); + let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state); assert!(response.is_ok()); message.push(0x00); - let response = - Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0)); + let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state); assert!(response.is_ok()); message.push(0x00); - let response = - Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0)); + let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state); assert!(response.is_ok()); message.push(0x00); - let response = - Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0)); + let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state); assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_LENGTH)); } @@ -569,7 +550,7 @@ mod test { env.user_presence() .set(|| panic!("Unexpected user presence check in CTAP1")); let sk = PrivateKey::new(&mut env, SignatureAlgorithm::Es256); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::new(&mut env); let rp_id = "example.com"; let application = crypto::sha256::Sha256::hash(rp_id.as_bytes()); @@ -578,8 +559,7 @@ mod test { create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle); message[0] = 0xEE; - let response = - Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0)); + let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state); assert_eq!(response, Err(Ctap1StatusCode::SW_CLA_INVALID)); } @@ -589,7 +569,7 @@ mod test { env.user_presence() .set(|| panic!("Unexpected user presence check in CTAP1")); let sk = PrivateKey::new(&mut env, SignatureAlgorithm::Es256); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::new(&mut env); let rp_id = "example.com"; let application = crypto::sha256::Sha256::hash(rp_id.as_bytes()); @@ -598,8 +578,7 @@ mod test { create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle); message[1] = 0xEE; - let response = - Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0)); + let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state); assert_eq!(response, Err(Ctap1StatusCode::SW_INS_INVALID)); } @@ -609,7 +588,7 @@ mod test { env.user_presence() .set(|| panic!("Unexpected user presence check in CTAP1")); let sk = PrivateKey::new(&mut env, SignatureAlgorithm::Es256); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::new(&mut env); let rp_id = "example.com"; let application = crypto::sha256::Sha256::hash(rp_id.as_bytes()); @@ -618,8 +597,7 @@ mod test { create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle); message[2] = 0xEE; - let response = - Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0)); + let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state); assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_DATA)); } @@ -637,7 +615,7 @@ mod test { env.user_presence() .set(|| panic!("Unexpected user presence check in CTAP1")); let sk = PrivateKey::new(&mut env, SignatureAlgorithm::Es256); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::new(&mut env); let rp_id = "example.com"; let application = crypto::sha256::Sha256::hash(rp_id.as_bytes()); @@ -645,11 +623,9 @@ mod test { let message = create_authenticate_message(&application, Ctap1Flags::EnforceUpAndSign, &key_handle); - ctap_state.u2f_up_state.consume_up(CtapInstant::new(0)); - ctap_state.u2f_up_state.grant_up(CtapInstant::new(0)); - let response = - Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0)) - .unwrap(); + ctap_state.u2f_up_state.consume_up(&mut env); + ctap_state.u2f_up_state.grant_up(&mut env); + let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state).unwrap(); assert_eq!(response[0], 0x01); let global_signature_counter = storage::global_signature_counter(&mut env).unwrap(); check_signature_counter( @@ -665,7 +641,7 @@ mod test { env.user_presence() .set(|| panic!("Unexpected user presence check in CTAP1")); let sk = PrivateKey::new(&mut env, SignatureAlgorithm::Es256); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::new(&mut env); let rp_id = "example.com"; let application = crypto::sha256::Sha256::hash(rp_id.as_bytes()); @@ -676,13 +652,8 @@ mod test { &key_handle, ); - let response = Ctap1Command::process_command( - &mut env, - &message, - &mut ctap_state, - CtapInstant::new((30001 * TEST_CLOCK_FREQUENCY_HZ as u64) / 1000), - ) - .unwrap(); + env.clock().advance(TOUCH_TIMEOUT_MS); + let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state).unwrap(); assert_eq!(response[0], 0x01); let global_signature_counter = storage::global_signature_counter(&mut env).unwrap(); check_signature_counter( @@ -702,12 +673,11 @@ mod test { let mut env = TestEnv::new(); env.user_presence() .set(|| panic!("Unexpected user presence check in CTAP1")); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::new(&mut env); - ctap_state.u2f_up_state.consume_up(CtapInstant::new(0)); - ctap_state.u2f_up_state.grant_up(CtapInstant::new(0)); - let response = - Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0)); + ctap_state.u2f_up_state.consume_up(&mut env); + ctap_state.u2f_up_state.grant_up(&mut env); + let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state); assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_DATA)); } @@ -721,16 +691,12 @@ mod test { let mut env = TestEnv::new(); env.user_presence() .set(|| panic!("Unexpected user presence check in CTAP1")); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::new(&mut env); - ctap_state.u2f_up_state.consume_up(CtapInstant::new(0)); - ctap_state.u2f_up_state.grant_up(CtapInstant::new(0)); - let response = Ctap1Command::process_command( - &mut env, - &message, - &mut ctap_state, - CtapInstant::new((30001 * TEST_CLOCK_FREQUENCY_HZ as u64) / 1000), - ); + ctap_state.u2f_up_state.consume_up(&mut env); + ctap_state.u2f_up_state.grant_up(&mut env); + env.clock().advance(TOUCH_TIMEOUT_MS); + let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state); assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED)); } } diff --git a/src/ctap/hid/mod.rs b/src/ctap/hid/mod.rs index 81dab017..5d329037 100644 --- a/src/ctap/hid/mod.rs +++ b/src/ctap/hid/mod.rs @@ -21,16 +21,29 @@ pub use self::receive::MessageAssembler; #[cfg(not(feature = "std"))] use self::receive::MessageAssembler; pub use self::send::HidPacketIterator; -use super::super::clock::{ClockInt, CtapInstant}; use super::status_code::Ctap2StatusCode; +#[cfg(test)] +use crate::env::test::TestEnv; use crate::env::Env; use alloc::vec; use alloc::vec::Vec; use arrayref::{array_ref, array_refs}; -use embedded_time::duration::Milliseconds; #[cfg(test)] use enum_iterator::IntoEnumIterator; +// We implement CTAP 2.1 from 2021-06-15. Please see section +// 11.2. USB Human Interface Device (USB HID) +const CHANNEL_RESERVED: ChannelID = [0, 0, 0, 0]; +const CHANNEL_BROADCAST: ChannelID = [0xFF, 0xFF, 0xFF, 0xFF]; +const PACKET_TYPE_MASK: u8 = 0x80; + +// See section 11.2.9.1.3. CTAPHID_INIT (0x06). +const PROTOCOL_VERSION: u8 = 2; +// The device version number is vendor-defined. +const DEVICE_VERSION_MAJOR: u8 = 1; +const DEVICE_VERSION_MINOR: u8 = 0; +const DEVICE_VERSION_BUILD: u8 = 0; + pub type HidPacket = [u8; 64]; pub type ChannelID = [u8; 4]; @@ -164,8 +177,8 @@ pub enum KeepaliveStatus { /// 2. If you didn't receive any message or preprocessing discarded it, stop. /// 3. Handles all CTAP protocol interactions. /// 4. `split_message` creates packets out of the response message. -pub struct CtapHid { - assembler: MessageAssembler, +pub struct CtapHid { + assembler: MessageAssembler, // The specification only requires unique CIDs, the allocation algorithm is vendor specific. // We allocate them incrementally, that is all `cid` such that 1 <= cid <= allocated_cids are // allocated. @@ -176,34 +189,17 @@ pub struct CtapHid { capabilities: u8, } -impl CtapHid { - // We implement CTAP 2.1 from 2021-06-15. Please see section - // 11.2. USB Human Interface Device (USB HID) - const CHANNEL_RESERVED: ChannelID = [0, 0, 0, 0]; - const CHANNEL_BROADCAST: ChannelID = [0xFF, 0xFF, 0xFF, 0xFF]; - const TYPE_INIT_BIT: u8 = 0x80; - const PACKET_TYPE_MASK: u8 = 0x80; - - // See section 11.2.9.1.3. CTAPHID_INIT (0x06). - const PROTOCOL_VERSION: u8 = 2; - // The device version number is vendor-defined. - const DEVICE_VERSION_MAJOR: u8 = 1; - const DEVICE_VERSION_MINOR: u8 = 0; - const DEVICE_VERSION_BUILD: u8 = 0; - +impl CtapHid { pub const CAPABILITY_WINK: u8 = 0x01; pub const CAPABILITY_CBOR: u8 = 0x04; #[cfg(any(not(feature = "with_ctap1"), feature = "vendor_hid"))] pub const CAPABILITY_NMSG: u8 = 0x08; - // TODO: Is this timeout duration specified? - const TIMEOUT_DURATION: Milliseconds = Milliseconds(100 as ClockInt); - /// Creates a new CTAP HID packet parser. /// /// The capabilities passed in are reported to the client in Init. - pub fn new(capabilities: u8) -> CtapHid { - CtapHid { + pub fn new(capabilities: u8) -> CtapHid { + Self { assembler: MessageAssembler::new(), allocated_cids: 0, capabilities, @@ -228,14 +224,9 @@ impl CtapHid { /// You may ignore PING, it's behaving correctly by default (input == output). /// Ignoring the others is incorrect behavior. You have to at least replace them with an error /// message: - /// `CtapHid::error_message(message.cid, CtapHid::ERR_INVALID_CMD)` - pub fn parse_packet( - &mut self, - env: &mut impl Env, - packet: &HidPacket, - clock_value: CtapInstant, - ) -> Option { - match self.assembler.parse_packet(env, packet, clock_value) { + /// `Self::error_message(message.cid, CtapHidError::InvalidCmd)` + pub fn parse_packet(&mut self, env: &mut E, packet: &HidPacket) -> Option { + match self.assembler.parse_packet(env, packet) { Ok(Some(message)) => { debug_ctap!(env, "Received message: {:02x?}", message); self.preprocess_message(message) @@ -248,9 +239,9 @@ impl CtapHid { if matches!(error, CtapHidError::UnexpectedContinuation) { None } else if !self.is_allocated_channel(cid) { - Some(CtapHid::error_message(cid, CtapHidError::InvalidChannel)) + Some(Self::error_message(cid, CtapHidError::InvalidChannel)) } else { - Some(CtapHid::error_message(cid, error)) + Some(Self::error_message(cid, error)) } } } @@ -267,7 +258,7 @@ impl CtapHid { fn preprocess_message(&mut self, message: Message) -> Option { let cid = message.cid; if !self.has_valid_channel(&message) { - return Some(CtapHid::error_message(cid, CtapHidError::InvalidChannel)); + return Some(Self::error_message(cid, CtapHidError::InvalidChannel)); } match message.cmd { @@ -276,10 +267,10 @@ impl CtapHid { // CTAP 2.1 from 2021-06-15, section 11.2.9.1.3. CtapHidCommand::Init => { if message.payload.len() != 8 { - return Some(CtapHid::error_message(cid, CtapHidError::InvalidLen)); + return Some(Self::error_message(cid, CtapHidError::InvalidLen)); } - let new_cid = if cid == CtapHid::CHANNEL_BROADCAST { + let new_cid = if cid == CHANNEL_BROADCAST { // TODO: Prevent allocating 2^32 channels. self.allocated_cids += 1; (self.allocated_cids as u32).to_be_bytes() @@ -291,10 +282,10 @@ impl CtapHid { let mut payload = vec![0; 17]; payload[..8].copy_from_slice(&message.payload); payload[8..12].copy_from_slice(&new_cid); - payload[12] = CtapHid::PROTOCOL_VERSION; - payload[13] = CtapHid::DEVICE_VERSION_MAJOR; - payload[14] = CtapHid::DEVICE_VERSION_MINOR; - payload[15] = CtapHid::DEVICE_VERSION_BUILD; + payload[12] = PROTOCOL_VERSION; + payload[13] = DEVICE_VERSION_MAJOR; + payload[14] = DEVICE_VERSION_MINOR; + payload[15] = DEVICE_VERSION_BUILD; payload[16] = self.capabilities; Some(Message { @@ -317,7 +308,7 @@ impl CtapHid { CtapHidCommand::Wink => Some(message), _ => { // Unknown or unsupported command. - Some(CtapHid::error_message(cid, CtapHidError::InvalidCmd)) + Some(Self::error_message(cid, CtapHidError::InvalidCmd)) } } } @@ -325,14 +316,14 @@ impl CtapHid { fn has_valid_channel(&self, message: &Message) -> bool { match message.cid { // Only INIT commands use the broadcast channel. - CtapHid::CHANNEL_BROADCAST => message.cmd == CtapHidCommand::Init, + CHANNEL_BROADCAST => message.cmd == CtapHidCommand::Init, // Check that the channel is allocated. _ => self.is_allocated_channel(message.cid), } } fn is_allocated_channel(&self, cid: ChannelID) -> bool { - cid != CtapHid::CHANNEL_RESERVED && u32::from_be_bytes(cid) as usize <= self.allocated_cids + cid != CHANNEL_RESERVED && u32::from_be_bytes(cid) as usize <= self.allocated_cids } pub fn error_message(cid: ChannelID, error_code: CtapHidError) -> Message { @@ -346,8 +337,8 @@ impl CtapHid { /// Helper function to parse a raw packet. pub fn process_single_packet(packet: &HidPacket) -> (ChannelID, ProcessedPacket) { let (&cid, rest) = array_refs![packet, 4, 60]; - if rest[0] & CtapHid::PACKET_TYPE_MASK != 0 { - let cmd = rest[0] & !CtapHid::PACKET_TYPE_MASK; + if rest[0] & PACKET_TYPE_MASK != 0 { + let cmd = rest[0] & !PACKET_TYPE_MASK; let len = (rest[1] as usize) << 8 | (rest[2] as usize); ( cid, @@ -394,7 +385,7 @@ impl CtapHid { /// Generates the HID response packets for a keepalive status. pub fn keepalive(cid: ChannelID, status: KeepaliveStatus) -> HidPacketIterator { - CtapHid::split_message(Message { + Self::split_message(Message { cid, cmd: CtapHidCommand::Keepalive, payload: vec![status as u8], @@ -402,9 +393,9 @@ impl CtapHid { } #[cfg(test)] - pub fn new_initialized() -> (CtapHid, ChannelID) { + pub fn new_initialized() -> (Self, ChannelID) { ( - CtapHid { + Self { assembler: MessageAssembler::new(), allocated_cids: 1, capabilities: 0x0D, @@ -417,7 +408,6 @@ impl CtapHid { #[cfg(test)] mod test { use super::*; - use crate::env::test::TestEnv; #[test] fn test_split_assemble() { @@ -430,9 +420,9 @@ mod test { }; let mut messages = Vec::new(); - let mut assembler = MessageAssembler::new(); + let mut assembler = MessageAssembler::::new(); for packet in HidPacketIterator::new(message.clone()).unwrap() { - match assembler.parse_packet(&mut env, &packet, CtapInstant::new(0)) { + match assembler.parse_packet(&mut env, &packet) { Ok(Some(msg)) => messages.push(msg), Ok(None) => (), Err(_) => panic!("Couldn't assemble packet: {:02x?}", &packet as &[u8]), @@ -446,21 +436,18 @@ mod test { #[test] fn test_spurious_continuation_packet() { let mut env = TestEnv::new(); - let mut ctap_hid = CtapHid::new(0x0D); + let mut ctap_hid = CtapHid::::new(0x0D); let mut packet = [0x00; 64]; packet[0..7].copy_from_slice(&[0xC1, 0xC1, 0xC1, 0xC1, 0x00, 0x51, 0x51]); // Continuation packets are silently ignored. - assert_eq!( - ctap_hid.parse_packet(&mut env, &packet, CtapInstant::new(0)), - None - ); + assert_eq!(ctap_hid.parse_packet(&mut env, &packet), None); } #[test] fn test_command_init() { - let mut ctap_hid = CtapHid::new(0x0D); + let mut ctap_hid = CtapHid::::new(0x0D); let init_message = Message { - cid: CtapHid::CHANNEL_BROADCAST, + cid: CHANNEL_BROADCAST, cmd: CtapHidCommand::Init, payload: vec![0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0], }; @@ -468,7 +455,7 @@ mod test { assert_eq!( reply, Some(Message { - cid: CtapHid::CHANNEL_BROADCAST, + cid: CHANNEL_BROADCAST, cmd: CtapHidCommand::Init, payload: vec![ 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, // Nonce @@ -484,7 +471,7 @@ mod test { #[test] fn test_command_init_for_sync() { let mut env = TestEnv::new(); - let (mut ctap_hid, cid) = CtapHid::new_initialized(); + let (mut ctap_hid, cid) = CtapHid::::new_initialized(); // Ping packet with a length longer than one packet. let mut packet1 = [0x51; 64]; @@ -496,12 +483,9 @@ mod test { packet2[4..15].copy_from_slice(&[ 0x86, 0x00, 0x08, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, ]); + assert_eq!(ctap_hid.parse_packet(&mut env, &packet1), None); assert_eq!( - ctap_hid.parse_packet(&mut env, &packet1, CtapInstant::new(0)), - None - ); - assert_eq!( - ctap_hid.parse_packet(&mut env, &packet2, CtapInstant::new(0)), + ctap_hid.parse_packet(&mut env, &packet2), Some(Message { cid, cmd: CtapHidCommand::Init, @@ -519,13 +503,13 @@ mod test { #[test] fn test_command_ping() { let mut env = TestEnv::new(); - let (mut ctap_hid, cid) = CtapHid::new_initialized(); + let (mut ctap_hid, cid) = CtapHid::::new_initialized(); let mut ping_packet = [0x00; 64]; ping_packet[..4].copy_from_slice(&cid); ping_packet[4..9].copy_from_slice(&[0x81, 0x00, 0x02, 0x99, 0x99]); assert_eq!( - ctap_hid.parse_packet(&mut env, &ping_packet, CtapInstant::new(0)), + ctap_hid.parse_packet(&mut env, &ping_packet), Some(Message { cid, cmd: CtapHidCommand::Ping, @@ -537,13 +521,13 @@ mod test { #[test] fn test_command_cancel() { let mut env = TestEnv::new(); - let (mut ctap_hid, cid) = CtapHid::new_initialized(); + let (mut ctap_hid, cid) = CtapHid::::new_initialized(); let mut cancel_packet = [0x00; 64]; cancel_packet[..4].copy_from_slice(&cid); cancel_packet[4..7].copy_from_slice(&[0x91, 0x00, 0x00]); - let response = ctap_hid.parse_packet(&mut env, &cancel_packet, CtapInstant::new(0)); + let response = ctap_hid.parse_packet(&mut env, &cancel_packet); assert_eq!(response, None); } @@ -554,7 +538,7 @@ mod test { cmd: CtapHidCommand::Ping, payload: vec![0x99, 0x99], }; - let mut response = CtapHid::split_message(message); + let mut response = CtapHid::::split_message(message); let mut expected_packet = [0x00; 64]; expected_packet[..9] .copy_from_slice(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x02, 0x99, 0x99]); @@ -570,7 +554,7 @@ mod test { cmd: CtapHidCommand::Cbor, payload, }; - let mut response = CtapHid::split_message(message); + let mut response = CtapHid::::split_message(message); let mut expected_packet = [0x00; 64]; expected_packet[..8].copy_from_slice(&[0x12, 0x34, 0x56, 0x78, 0x90, 0x00, 0x01, 0xF2]); assert_eq!(response.next(), Some(expected_packet)); @@ -581,7 +565,7 @@ mod test { fn test_keepalive() { for &status in [KeepaliveStatus::Processing, KeepaliveStatus::UpNeeded].iter() { let cid = [0x12, 0x34, 0x56, 0x78]; - let mut response = CtapHid::keepalive(cid, status); + let mut response = CtapHid::::keepalive(cid, status); let mut expected_packet = [0x00; 64]; expected_packet[..8].copy_from_slice(&[ 0x12, @@ -604,7 +588,7 @@ mod test { let mut packet = [0x00; 64]; packet[..4].copy_from_slice(&cid); packet[4..9].copy_from_slice(&[0x81, 0x00, 0x02, 0x99, 0x99]); - let (processed_cid, processed_packet) = CtapHid::process_single_packet(&packet); + let (processed_cid, processed_packet) = CtapHid::::process_single_packet(&packet); assert_eq!(processed_cid, cid); let expected_packet = ProcessedPacket::InitPacket { cmd: CtapHidCommand::Ping as u8, @@ -627,7 +611,7 @@ mod test { fn test_error_message() { let cid = [0x12, 0x34, 0x56, 0x78]; assert_eq!( - CtapHid::error_message(cid, CtapHidError::InvalidCmd), + CtapHid::::error_message(cid, CtapHidError::InvalidCmd), Message { cid, cmd: CtapHidCommand::Error, diff --git a/src/ctap/hid/receive.rs b/src/ctap/hid/receive.rs index f026b2bd..539f81f3 100644 --- a/src/ctap/hid/receive.rs +++ b/src/ctap/hid/receive.rs @@ -15,20 +15,23 @@ use super::{ ChannelID, CtapHid, CtapHidCommand, CtapHidError, HidPacket, Message, ProcessedPacket, }; +use crate::api::clock::Clock; use crate::api::customization::Customization; -use crate::clock::CtapInstant; use crate::env::Env; use alloc::vec::Vec; use core::mem::swap; +// TODO: Is this timeout duration specified? +const TIMEOUT_DURATION_MS: usize = 100; + /// A structure to assemble CTAPHID commands from a series of incoming USB HID packets. -pub struct MessageAssembler { +pub struct MessageAssembler { // Whether this is waiting to receive an initialization packet. idle: bool, // Current channel ID. cid: ChannelID, // Timestamp of the last packet received on the current channel. - last_timestamp: CtapInstant, + timer: ::Timer, // Current command. cmd: u8, // Sequence number expected for the next packet. @@ -39,12 +42,12 @@ pub struct MessageAssembler { payload: Vec, } -impl MessageAssembler { - pub fn new() -> MessageAssembler { +impl MessageAssembler { + pub fn new() -> MessageAssembler { MessageAssembler { idle: true, cid: [0, 0, 0, 0], - last_timestamp: CtapInstant::new(0), + timer: ::Timer::default(), cmd: 0, seq: 0, remaining_payload_len: 0, @@ -57,7 +60,7 @@ impl MessageAssembler { fn reset(&mut self) { self.idle = true; self.cid = [0, 0, 0, 0]; - self.last_timestamp = CtapInstant::new(0); + self.timer = ::Timer::default(); self.cmd = 0; self.seq = 0; self.remaining_payload_len = 0; @@ -69,19 +72,16 @@ impl MessageAssembler { // full message was assembled after this packet, or None if more packets are needed to fill the // message. // - An Err() result if there was a parsing error. - // TODO: Implement timeouts. For example, have the caller pass us a timestamp of when this - // packet was received. pub fn parse_packet( &mut self, - env: &mut impl Env, + env: &mut E, packet: &HidPacket, - timestamp: CtapInstant, ) -> Result, (ChannelID, CtapHidError)> { // TODO: Support non-full-speed devices (i.e. packet len != 64)? This isn't recommended by // section 8.8.1 - let (cid, processed_packet) = CtapHid::process_single_packet(packet); + let (cid, processed_packet) = CtapHid::::process_single_packet(packet); - if !self.idle && timestamp >= self.last_timestamp + CtapHid::TIMEOUT_DURATION { + if !self.idle && env.clock().is_elapsed(&self.timer) { // The current channel timed out. // Save the channel ID and reset the state. let current_cid = self.cid; @@ -98,7 +98,7 @@ impl MessageAssembler { // Expecting an initialization packet. match processed_packet { ProcessedPacket::InitPacket { cmd, len, data } => { - self.parse_init_packet(env, cid, cmd, len, data, timestamp) + self.parse_init_packet(env, cid, cmd, len, data) } ProcessedPacket::ContinuationPacket { .. } => { // CTAP specification (version 20190130) section 8.1.5.4 @@ -120,7 +120,7 @@ impl MessageAssembler { ProcessedPacket::InitPacket { cmd, len, data } => { self.reset(); if cmd == CtapHidCommand::Init as u8 { - self.parse_init_packet(env, cid, cmd, len, data, timestamp) + self.parse_init_packet(env, cid, cmd, len, data) } else { Err((cid, CtapHidError::InvalidSeq)) } @@ -132,7 +132,7 @@ impl MessageAssembler { Err((cid, CtapHidError::InvalidSeq)) } else { // Update the last timestamp. - self.last_timestamp = timestamp; + self.timer = env.clock().make_timer(TIMEOUT_DURATION_MS); // Increment the sequence number for the next packet. self.seq += 1; Ok(self.append_payload(data)) @@ -144,12 +144,11 @@ impl MessageAssembler { fn parse_init_packet( &mut self, - env: &mut impl Env, + env: &mut E, cid: ChannelID, cmd: u8, len: usize, data: &[u8], - timestamp: CtapInstant, ) -> Result, (ChannelID, CtapHidError)> { // Reject invalid lengths early to reduce the risk of running out of memory. // TODO: also reject invalid commands early? @@ -157,7 +156,7 @@ impl MessageAssembler { return Err((cid, CtapHidError::InvalidLen)); } self.cid = cid; - self.last_timestamp = timestamp; + self.timer = env.clock().make_timer(TIMEOUT_DURATION_MS); self.cmd = cmd; self.seq = 0; self.remaining_payload_len = len; @@ -187,9 +186,7 @@ impl MessageAssembler { #[cfg(test)] mod test { - use crate::ctap::hid::CtapHid; use crate::env::test::TestEnv; - use embedded_time::duration::Milliseconds; use super::*; @@ -212,14 +209,8 @@ mod test { fn test_empty_payload() { let mut env = TestEnv::new(); let mut assembler = MessageAssembler::new(); - // Except for tests that exercise timeouts, all packets are synchronized at the same dummy - // timestamp. assert_eq!( - assembler.parse_packet( - &mut env, - &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x90]), - CtapInstant::new(0) - ), + assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x90])), Ok(Some(Message { cid: [0x12, 0x34, 0x56, 0x78], cmd: CtapHidCommand::Cbor, @@ -236,7 +227,6 @@ mod test { assembler.parse_packet( &mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x90, 0x00, 0x10]), - CtapInstant::new(0) ), Ok(Some(Message { cid: [0x12, 0x34, 0x56, 0x78], @@ -257,7 +247,6 @@ mod test { assembler.parse_packet( &mut env, &byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x90, 0x00, 0x10], 0xFF), - CtapInstant::new(0) ), Ok(Some(Message { cid: [0x12, 0x34, 0x56, 0x78], @@ -275,16 +264,11 @@ mod test { assembler.parse_packet( &mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]), - CtapInstant::new(0) ), Ok(None) ); assert_eq!( - assembler.parse_packet( - &mut env, - &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]), - CtapInstant::new(0) - ), + assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00])), Ok(Some(Message { cid: [0x12, 0x34, 0x56, 0x78], cmd: CtapHidCommand::Ping, @@ -301,24 +285,15 @@ mod test { assembler.parse_packet( &mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x80]), - CtapInstant::new(0) ), Ok(None) ); assert_eq!( - assembler.parse_packet( - &mut env, - &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]), - CtapInstant::new(0) - ), + assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00])), Ok(None) ); assert_eq!( - assembler.parse_packet( - &mut env, - &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x01]), - CtapInstant::new(0) - ), + assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x01])), Ok(Some(Message { cid: [0x12, 0x34, 0x56, 0x78], cmd: CtapHidCommand::Ping, @@ -335,26 +310,17 @@ mod test { assembler.parse_packet( &mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x1D, 0xB9]), - CtapInstant::new(0) ), Ok(None) ); for seq in 0..0x7F { assert_eq!( - assembler.parse_packet( - &mut env, - &zero_extend(&[0x12, 0x34, 0x56, 0x78, seq]), - CtapInstant::new(0) - ), + assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, seq])), Ok(None) ); } assert_eq!( - assembler.parse_packet( - &mut env, - &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x7F]), - CtapInstant::new(0) - ), + assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x7F])), Ok(Some(Message { cid: [0x12, 0x34, 0x56, 0x78], cmd: CtapHidCommand::Ping, @@ -380,7 +346,6 @@ mod test { &[0x12, 0x34, 0x56, 0x78, 0x80 | cmd as u8, 0x00, 0x80], byte ), - CtapInstant::new(0) ), Ok(None) ); @@ -388,7 +353,6 @@ mod test { assembler.parse_packet( &mut env, &byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x00], byte), - CtapInstant::new(0) ), Ok(None) ); @@ -396,7 +360,6 @@ mod test { assembler.parse_packet( &mut env, &byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x01], byte), - CtapInstant::new(0) ), Ok(Some(Message { cid: [0x12, 0x34, 0x56, 0x78], @@ -422,24 +385,17 @@ mod test { assembler.parse_packet( &mut env, &byte_extend(&[0x12, 0x34, 0x56, cid, 0x80 | cmd as u8, 0x00, 0x80], byte), - CtapInstant::new(0) ), Ok(None) ); assert_eq!( - assembler.parse_packet( - &mut env, - &byte_extend(&[0x12, 0x34, 0x56, cid, 0x00], byte), - CtapInstant::new(0) - ), + assembler + .parse_packet(&mut env, &byte_extend(&[0x12, 0x34, 0x56, cid, 0x00], byte)), Ok(None) ); assert_eq!( - assembler.parse_packet( - &mut env, - &byte_extend(&[0x12, 0x34, 0x56, cid, 0x01], byte), - CtapInstant::new(0) - ), + assembler + .parse_packet(&mut env, &byte_extend(&[0x12, 0x34, 0x56, cid, 0x01], byte)), Ok(Some(Message { cid: [0x12, 0x34, 0x56, cid], cmd, @@ -457,7 +413,6 @@ mod test { assembler.parse_packet( &mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]), - CtapInstant::new(0) ), Ok(None) ); @@ -470,7 +425,6 @@ mod test { assembler.parse_packet( &mut env, &byte_extend(&[0x12, 0x34, 0x56, 0x9A, cmd as u8, 0x00], byte), - CtapInstant::new(0) ), Err(([0x12, 0x34, 0x56, 0x9A], CtapHidError::ChannelBusy)) ); @@ -478,11 +432,7 @@ mod test { } assert_eq!( - assembler.parse_packet( - &mut env, - &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]), - CtapInstant::new(0) - ), + assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00])), Ok(Some(Message { cid: [0x12, 0x34, 0x56, 0x78], cmd: CtapHidCommand::Ping, @@ -505,7 +455,6 @@ mod test { assembler.parse_packet( &mut env, &byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x10], byte), - CtapInstant::new(0) ), Ok(Some(Message { cid: [0x12, 0x34, 0x56, 0x78], @@ -517,11 +466,7 @@ mod test { // Spurious continuation packet. let seq = i; assert_eq!( - assembler.parse_packet( - &mut env, - &zero_extend(&[0x12, 0x34, 0x56, 0x78, seq]), - CtapInstant::new(0) - ), + assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, seq])), Err(( [0x12, 0x34, 0x56, 0x78], CtapHidError::UnexpectedContinuation @@ -538,16 +483,11 @@ mod test { assembler.parse_packet( &mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]), - CtapInstant::new(0) ), Ok(None) ); assert_eq!( - assembler.parse_packet( - &mut env, - &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x80]), - CtapInstant::new(0) - ), + assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x80])), Err(([0x12, 0x34, 0x56, 0x78], CtapHidError::InvalidSeq)) ); } @@ -560,16 +500,11 @@ mod test { assembler.parse_packet( &mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]), - CtapInstant::new(0) ), Ok(None) ); assert_eq!( - assembler.parse_packet( - &mut env, - &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x01]), - CtapInstant::new(0) - ), + assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x01])), Err(([0x12, 0x34, 0x56, 0x78], CtapHidError::InvalidSeq)) ); } @@ -582,16 +517,12 @@ mod test { assembler.parse_packet( &mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]), - CtapInstant::new(0) ), Ok(None) ); + env.clock().advance(TIMEOUT_DURATION_MS); assert_eq!( - assembler.parse_packet( - &mut env, - &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]), - CtapInstant::new(0) + CtapHid::TIMEOUT_DURATION - ), + assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00])), Err(([0x12, 0x34, 0x56, 0x78], CtapHidError::MsgTimeout)) ); } @@ -599,37 +530,27 @@ mod test { #[test] fn test_just_in_time_packets() { let mut env = TestEnv::new(); - let mut timestamp: CtapInstant = CtapInstant::new(0); // Delay between each packet is just below the threshold. - let delay = CtapHid::TIMEOUT_DURATION - Milliseconds(1_u32); + let delay = TIMEOUT_DURATION_MS - 1; let mut assembler = MessageAssembler::new(); assert_eq!( assembler.parse_packet( &mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x1D, 0xB9]), - timestamp ), Ok(None) ); for seq in 0..0x7F { - timestamp = timestamp + delay; + env.clock().advance(delay); assert_eq!( - assembler.parse_packet( - &mut env, - &zero_extend(&[0x12, 0x34, 0x56, 0x78, seq]), - timestamp - ), + assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, seq])), Ok(None) ); } - timestamp = timestamp + delay; + env.clock().advance(delay); assert_eq!( - assembler.parse_packet( - &mut env, - &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x7F]), - timestamp - ), + assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x7F])), Ok(Some(Message { cid: [0x12, 0x34, 0x56, 0x78], cmd: CtapHidCommand::Ping, @@ -647,7 +568,6 @@ mod test { assembler.parse_packet( &mut env, &byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x02, 0x00], 0x51), - CtapInstant::new(0) ), Ok(None) ); @@ -659,7 +579,6 @@ mod test { 0x12, 0x34, 0x56, 0x78, 0x86, 0x00, 0x08, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0 ]), - CtapInstant::new(0) ), Ok(Some(Message { cid: [0x12, 0x34, 0x56, 0x78], diff --git a/src/ctap/hid/send.rs b/src/ctap/hid/send.rs index afe49ecb..4869355a 100644 --- a/src/ctap/hid/send.rs +++ b/src/ctap/hid/send.rs @@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::{CtapHid, HidPacket, Message}; +use super::{HidPacket, Message}; + +const TYPE_INIT_BIT: u8 = 0x80; /// Iterator for HID packets. /// @@ -121,7 +123,7 @@ impl Iterator for MessageSplitter { match self.seq { None => { // First, send an initialization packet. - self.packet[4] = self.message.cmd as u8 | CtapHid::TYPE_INIT_BIT; + self.packet[4] = self.message.cmd as u8 | TYPE_INIT_BIT; self.packet[5] = (payload_len >> 8) as u8; self.packet[6] = payload_len as u8; diff --git a/src/ctap/large_blobs.rs b/src/ctap/large_blobs.rs index d8588dea..2ef68c78 100644 --- a/src/ctap/large_blobs.rs +++ b/src/ctap/large_blobs.rs @@ -45,10 +45,10 @@ impl LargeBlobs { } /// Process the large blob command. - pub fn process_command( + pub fn process_command( &mut self, - env: &mut impl Env, - client_pin: &mut ClientPin, + env: &mut E, + client_pin: &mut ClientPin, large_blobs_params: AuthenticatorLargeBlobsParameters, ) -> Result { let AuthenticatorLargeBlobsParameters { @@ -147,8 +147,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let mut large_blobs = LargeBlobs::new(); let large_blob = vec![ @@ -178,8 +182,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let mut large_blobs = LargeBlobs::new(); const BLOB_LEN: usize = 200; @@ -240,8 +248,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let mut large_blobs = LargeBlobs::new(); const BLOB_LEN: usize = 200; @@ -286,8 +298,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let mut large_blobs = LargeBlobs::new(); const BLOB_LEN: usize = 200; @@ -332,8 +348,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let mut large_blobs = LargeBlobs::new(); let large_blobs_params = AuthenticatorLargeBlobsParameters { @@ -355,8 +375,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let mut large_blobs = LargeBlobs::new(); const BLOB_LEN: usize = 20; @@ -383,8 +407,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + pin_uv_auth_protocol, + ); let mut large_blobs = LargeBlobs::new(); const BLOB_LEN: usize = 20; diff --git a/src/ctap/main_hid.rs b/src/ctap/main_hid.rs index e34ee555..d149fb41 100644 --- a/src/ctap/main_hid.rs +++ b/src/ctap/main_hid.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::clock::{ClockInt, CtapInstant}; +use crate::api::clock::Clock; #[cfg(feature = "with_ctap1")] use crate::ctap::ctap1; #[cfg(feature = "with_ctap1")] @@ -20,29 +20,29 @@ use crate::ctap::hid::ChannelID; use crate::ctap::hid::{ CtapHid, CtapHidCommand, CtapHidError, HidPacket, HidPacketIterator, Message, }; -use crate::ctap::{Channel, CtapState, TimedPermission}; +use crate::ctap::{Channel, CtapState}; use crate::env::Env; -use embedded_time::duration::Milliseconds; + +const WINK_TIMEOUT_DURATION_MS: usize = 5000; /// Implements the standard CTAP command processing for HID. -pub struct MainHid { - hid: CtapHid, - wink_permission: TimedPermission, +pub struct MainHid { + hid: CtapHid, + wink_permission: ::Timer, } -impl MainHid { - const WINK_TIMEOUT_DURATION: Milliseconds = Milliseconds(5000 as ClockInt); - +impl MainHid { /// Instantiates a HID handler for CTAP1, CTAP2 and Wink. pub fn new() -> Self { #[cfg(feature = "with_ctap1")] - let capabilities = CtapHid::CAPABILITY_WINK | CtapHid::CAPABILITY_CBOR; + let capabilities = CtapHid::::CAPABILITY_WINK | CtapHid::::CAPABILITY_CBOR; #[cfg(not(feature = "with_ctap1"))] - let capabilities = - CtapHid::CAPABILITY_WINK | CtapHid::CAPABILITY_CBOR | CtapHid::CAPABILITY_NMSG; + let capabilities = CtapHid::::CAPABILITY_WINK + | CtapHid::::CAPABILITY_CBOR + | CtapHid::::CAPABILITY_NMSG; let hid = CtapHid::new(capabilities); - let wink_permission = TimedPermission::waiting(); + let wink_permission = ::Timer::default(); MainHid { hid, wink_permission, @@ -52,15 +52,14 @@ impl MainHid { /// Processes an incoming USB HID packet, and returns an iterator for all outgoing packets. pub fn process_hid_packet( &mut self, - env: &mut impl Env, + env: &mut E, packet: &HidPacket, - now: CtapInstant, - ctap_state: &mut CtapState, + ctap_state: &mut CtapState, ) -> HidPacketIterator { - if let Some(message) = self.hid.parse_packet(env, packet, now) { - let processed_message = self.process_message(env, message, now, ctap_state); + if let Some(message) = self.hid.parse_packet(env, packet) { + let processed_message = self.process_message(env, message, ctap_state); debug_ctap!(env, "Sending message: {:02x?}", processed_message); - CtapHid::split_message(processed_message) + CtapHid::::split_message(processed_message) } else { HidPacketIterator::none() } @@ -69,13 +68,12 @@ impl MainHid { /// Processes a message's commands that affect the protocol outside HID. pub fn process_message( &mut self, - env: &mut impl Env, + env: &mut E, message: Message, - now: CtapInstant, - ctap_state: &mut CtapState, + ctap_state: &mut CtapState, ) -> Message { // If another command arrives, stop winking to prevent accidential button touches. - self.wink_permission = TimedPermission::waiting(); + self.wink_permission = ::Timer::default(); let cid = message.cid; match message.cmd { @@ -83,12 +81,12 @@ impl MainHid { CtapHidCommand::Msg => { // If we don't have CTAP1 backward compatibilty, this command is invalid. #[cfg(not(feature = "with_ctap1"))] - return CtapHid::error_message(cid, CtapHidError::InvalidCmd); + return CtapHid::::error_message(cid, CtapHidError::InvalidCmd); #[cfg(feature = "with_ctap1")] - match ctap1::Ctap1Command::process_command(env, &message.payload, ctap_state, now) { - Ok(payload) => MainHid::ctap1_success_message(cid, &payload), - Err(ctap1_status_code) => MainHid::ctap1_error_message(cid, ctap1_status_code), + match ctap1::Ctap1Command::process_command(env, &message.payload, ctap_state) { + Ok(payload) => Self::ctap1_success_message(cid, &payload), + Err(ctap1_status_code) => Self::ctap1_error_message(cid, ctap1_status_code), } } // CTAP 2.1 from 2021-06-15, section 11.2.9.1.2. @@ -97,7 +95,7 @@ impl MainHid { // don't handle any other packet in the meantime. // TODO: Send "Processing" type keep-alive packets in the meantime. let response = - ctap_state.process_command(env, &message.payload, Channel::MainHid(cid), now); + ctap_state.process_command(env, &message.payload, Channel::MainHid(cid)); Message { cid, cmd: CtapHidCommand::Cbor, @@ -107,12 +105,11 @@ impl MainHid { // CTAP 2.1 from 2021-06-15, section 11.2.9.2.1. CtapHidCommand::Wink => { if message.payload.is_empty() { - self.wink_permission = - TimedPermission::granted(now, Self::WINK_TIMEOUT_DURATION); + self.wink_permission = env.clock().make_timer(WINK_TIMEOUT_DURATION_MS); // The response is empty like the request. message } else { - CtapHid::error_message(cid, CtapHidError::InvalidLen) + CtapHid::::error_message(cid, CtapHidError::InvalidLen) } } // All other commands have already been processed, keep them as is. @@ -121,13 +118,8 @@ impl MainHid { } /// Returns whether a wink permission is currently granted. - pub fn should_wink(&self, now: CtapInstant) -> bool { - self.wink_permission.is_granted(now) - } - - /// Updates the timeout for the wink permission. - pub fn update_wink_timeout(&mut self, now: CtapInstant) { - self.wink_permission = self.wink_permission.check_expiration(now); + pub fn should_wink(&self, env: &mut E) -> bool { + !env.clock().is_elapsed(&self.wink_permission) } #[cfg(feature = "with_ctap1")] @@ -159,11 +151,11 @@ mod test { use crate::ctap::hid::ChannelID; use crate::env::test::TestEnv; - fn new_initialized() -> (MainHid, ChannelID) { + fn new_initialized() -> (MainHid, ChannelID) { let (hid, cid) = CtapHid::new_initialized(); - let wink_permission = TimedPermission::waiting(); + let wink_permission = <::Clock as Clock>::Timer::default(); ( - MainHid { + MainHid:: { hid, wink_permission, }, @@ -174,19 +166,14 @@ mod test { #[test] fn test_process_hid_packet() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let (mut main_hid, cid) = new_initialized(); let mut ping_packet = [0x00; 64]; ping_packet[..4].copy_from_slice(&cid); ping_packet[4..9].copy_from_slice(&[0x81, 0x00, 0x02, 0x99, 0x99]); - let mut response = main_hid.process_hid_packet( - &mut env, - &ping_packet, - CtapInstant::new(0), - &mut ctap_state, - ); + let mut response = main_hid.process_hid_packet(&mut env, &ping_packet, &mut ctap_state); assert_eq!(response.next(), Some(ping_packet)); assert_eq!(response.next(), None); } @@ -194,42 +181,33 @@ mod test { #[test] fn test_process_hid_packet_empty() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let (mut main_hid, cid) = new_initialized(); let mut cancel_packet = [0x00; 64]; cancel_packet[..4].copy_from_slice(&cid); cancel_packet[4..7].copy_from_slice(&[0x91, 0x00, 0x00]); - let mut response = main_hid.process_hid_packet( - &mut env, - &cancel_packet, - CtapInstant::new(0), - &mut ctap_state, - ); + let mut response = main_hid.process_hid_packet(&mut env, &cancel_packet, &mut ctap_state); assert_eq!(response.next(), None); } #[test] fn test_wink() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let (mut main_hid, cid) = new_initialized(); - assert!(!main_hid.should_wink(CtapInstant::new(0))); + assert!(!main_hid.should_wink(&mut env)); let mut wink_packet = [0x00; 64]; wink_packet[..4].copy_from_slice(&cid); wink_packet[4..7].copy_from_slice(&[0x88, 0x00, 0x00]); - let mut response = main_hid.process_hid_packet( - &mut env, - &wink_packet, - CtapInstant::new(0), - &mut ctap_state, - ); + let mut response = main_hid.process_hid_packet(&mut env, &wink_packet, &mut ctap_state); assert_eq!(response.next(), Some(wink_packet)); assert_eq!(response.next(), None); - assert!(main_hid.should_wink(CtapInstant::new(0))); - assert!(!main_hid.should_wink(CtapInstant::new(1) + MainHid::WINK_TIMEOUT_DURATION)); + assert!(main_hid.should_wink(&mut env)); + env.clock().advance(WINK_TIMEOUT_DURATION_MS); + assert!(!main_hid.should_wink(&mut env)); } } diff --git a/src/ctap/mod.rs b/src/ctap/mod.rs index 97e1bc45..7ec0a470 100644 --- a/src/ctap/mod.rs +++ b/src/ctap/mod.rs @@ -30,8 +30,9 @@ mod pin_protocol; pub mod response; pub mod status_code; mod storage; -mod timed_permission; mod token_state; +#[cfg(feature = "with_ctap1")] +mod u2f_up; #[cfg(feature = "vendor_hid")] pub mod vendor_hid; @@ -60,16 +61,15 @@ use self::response::{ AuthenticatorVendorUpgradeInfoResponse, ResponseData, }; use self::status_code::Ctap2StatusCode; -use self::timed_permission::TimedPermission; #[cfg(feature = "with_ctap1")] -use self::timed_permission::U2fUserPresenceState; +use self::u2f_up::U2fUserPresenceState; use crate::api::attestation_store::{self, Attestation, AttestationStore}; +use crate::api::clock::Clock; use crate::api::connection::{HidConnection, SendOrRecvStatus}; use crate::api::customization::Customization; use crate::api::firmware_protection::FirmwareProtection; use crate::api::upgrade_storage::UpgradeStorage; use crate::api::user_presence::{UserPresence, UserPresenceError}; -use crate::clock::{ClockInt, CtapInstant, KEEPALIVE_DELAY, KEEPALIVE_DELAY_MS}; use crate::env::Env; use alloc::boxed::Box; use alloc::string::{String, ToString}; @@ -80,7 +80,6 @@ use core::convert::TryFrom; use crypto::hmac::hmac_256; use crypto::sha256::Sha256; use crypto::{ecdsa, Hash256}; -use embedded_time::duration::Milliseconds; use libtock_drivers::usb_ctap_hid::UsbEndpoint; use rng256::Rng256; use sk_cbor as cbor; @@ -99,14 +98,11 @@ const ED_FLAG: u8 = 0x80; // CTAP2 specification section 6 requires that the depth of nested CBOR structures be limited to at most four levels. const MAX_CBOR_NESTING_DEPTH: i8 = 4; -pub const TOUCH_TIMEOUT_MS: ClockInt = 30000; -#[cfg(feature = "with_ctap1")] -pub const TOUCH_TIMEOUT: Milliseconds = Milliseconds(TOUCH_TIMEOUT_MS); -#[cfg(feature = "with_ctap1")] -const U2F_UP_PROMPT_TIMEOUT: Milliseconds = Milliseconds(10000 as ClockInt); +pub const KEEPALIVE_DELAY_MS: usize = 100; +pub const TOUCH_TIMEOUT_MS: usize = 30000; // TODO(kaczmarczyck) 2.1 allows Reset after Reset and 15 seconds? -const RESET_TIMEOUT_DURATION: Milliseconds = Milliseconds(10000 as ClockInt); -const STATEFUL_COMMAND_TIMEOUT_DURATION: Milliseconds = Milliseconds(30000 as ClockInt); +const RESET_TIMEOUT_DURATION_MS: usize = 10000; +const STATEFUL_COMMAND_TIMEOUT_DURATION_MS: usize = 30000; pub const FIDO2_VERSION_STRING: &str = "FIDO_2_0"; #[cfg(feature = "with_ctap1")] @@ -209,20 +205,20 @@ fn truncate_to_char_boundary(s: &str, mut max: usize) -> &str { // Sends keepalive packet during user presence checking. If user agent replies with CANCEL response, // returns Err(UserPresenceError::Canceled). -fn send_keepalive_up_needed( - env: &mut impl Env, +fn send_keepalive_up_needed( + env: &mut E, channel: Channel, - timeout: Milliseconds, + timeout_ms: usize, ) -> Result<(), UserPresenceError> { let (cid, transport) = match channel { Channel::MainHid(cid) => (cid, Transport::MainHid), #[cfg(feature = "vendor_hid")] Channel::VendorHid(cid) => (cid, Transport::VendorHid), }; - let keepalive_msg = CtapHid::keepalive(cid, KeepaliveStatus::UpNeeded); + let keepalive_msg = CtapHid::::keepalive(cid, KeepaliveStatus::UpNeeded); for mut pkt in keepalive_msg { let ctap_hid_connection = transport.hid_connection(env); - match ctap_hid_connection.send_and_maybe_recv(&mut pkt, timeout) { + match ctap_hid_connection.send_and_maybe_recv(&mut pkt, timeout_ms) { Ok(SendOrRecvStatus::Timeout) => { debug_ctap!(env, "Sending a KEEPALIVE packet timed out"); // TODO: abort user presence test? @@ -249,7 +245,7 @@ fn send_keepalive_up_needed( } // 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 { debug_ctap!( env, @@ -288,11 +284,11 @@ fn send_keepalive_up_needed( /// Blocks for user presence. /// /// Returns an error in case of timeout, user declining presence request, or keepalive error. -fn check_user_presence(env: &mut impl Env, channel: Channel) -> Result<(), Ctap2StatusCode> { +fn check_user_presence(env: &mut E, channel: Channel) -> Result<(), Ctap2StatusCode> { env.user_presence().check_init(); // The timeout is N times the keepalive delay. - const TIMEOUT_ITERATIONS: usize = TOUCH_TIMEOUT_MS as usize / KEEPALIVE_DELAY_MS as usize; + const TIMEOUT_ITERATIONS: usize = TOUCH_TIMEOUT_MS / KEEPALIVE_DELAY_MS; // All fallible functions are called without '?' operator to always reach // check_complete(...) cleanup function. @@ -301,11 +297,9 @@ fn check_user_presence(env: &mut impl Env, channel: Channel) -> Result<(), Ctap2 for i in 0..=TIMEOUT_ITERATIONS { // First presence check is made without timeout. That way Env implementation may return // user presence check result immediately to client, without sending any keepalive packets. - result = env.user_presence().wait_with_timeout(if i == 0 { - Milliseconds(0) - } else { - KEEPALIVE_DELAY - }); + result = env + .user_presence() + .wait_with_timeout(if i == 0 { 0 } else { KEEPALIVE_DELAY_MS }); if !matches!(result, Err(UserPresenceError::Timeout)) { break; } @@ -313,7 +307,7 @@ fn check_user_presence(env: &mut impl Env, channel: Channel) -> Result<(), Ctap2 // accordingly, so that all wait_with_timeout invocations are separated by // equal time intervals. That way token indicators, such as LEDs, will blink // with a consistent pattern. - let keepalive_result = send_keepalive_up_needed(env, channel, KEEPALIVE_DELAY); + let keepalive_result = send_keepalive_up_needed(env, channel, KEEPALIVE_DELAY_MS); if keepalive_result.is_err() { debug_ctap!( env, @@ -365,20 +359,20 @@ pub enum StatefulCommand { /// /// Additionally, state that is held over multiple commands is assigned to a channel. We discard /// all state when we receive data on a different channel. -pub struct StatefulPermission { - permission: TimedPermission, +pub struct StatefulPermission { + permission: ::Timer, command_type: Option, channel: Option, } -impl StatefulPermission { +impl StatefulPermission { /// Creates the command state at device startup. /// /// Resets are only possible after a power cycle. Therefore, initialization /// means allowing Reset, and Reset cannot be granted later. - pub fn new_reset(now: CtapInstant) -> StatefulPermission { + pub fn new_reset(env: &mut E) -> StatefulPermission { StatefulPermission { - permission: TimedPermission::granted(now, RESET_TIMEOUT_DURATION), + permission: env.clock().make_timer(RESET_TIMEOUT_DURATION_MS), command_type: Some(StatefulCommand::Reset), channel: None, } @@ -386,7 +380,7 @@ impl StatefulPermission { /// Clears all permissions and state. pub fn clear(&mut self) { - self.permission = TimedPermission::waiting(); + self.permission = ::Timer::default(); self.command_type = None; self.channel = None; } @@ -411,8 +405,8 @@ impl StatefulPermission { } /// Clears all state if the permission timed out. - pub fn clear_timer(&mut self, now: CtapInstant) { - if !self.permission.is_granted(now) { + pub fn clear_timer(&mut self, env: &mut E) { + if env.clock().is_elapsed(&self.permission) { self.clear(); } } @@ -427,7 +421,7 @@ impl StatefulPermission { /// Sets a new command state, and starts a new clock for timeouts. pub fn set_command( &mut self, - now: CtapInstant, + env: &mut E, new_command_type: StatefulCommand, channel: Channel, ) { @@ -435,7 +429,7 @@ impl StatefulPermission { // Reset is only allowed after a power cycle. StatefulCommand::Reset => unreachable!(), _ => { - self.permission = TimedPermission::granted(now, STATEFUL_COMMAND_TIMEOUT_DURATION); + self.permission = env.clock().make_timer(STATEFUL_COMMAND_TIMEOUT_DURATION_MS); self.command_type = Some(new_command_type); self.channel = Some(channel); } @@ -486,36 +480,36 @@ impl StatefulPermission { // This struct currently holds all state, not only the persistent memory. The persistent members are // in the persistent store field. -pub struct CtapState { - client_pin: ClientPin, +pub struct CtapState { + client_pin: ClientPin, #[cfg(feature = "with_ctap1")] - pub(crate) u2f_up_state: U2fUserPresenceState, + pub(crate) u2f_up_state: U2fUserPresenceState, // The state initializes to Reset and its timeout, and never goes back to Reset. - stateful_command_permission: StatefulPermission, + stateful_command_permission: StatefulPermission, large_blobs: LargeBlobs, } -impl CtapState { - pub fn new(env: &mut impl Env, now: CtapInstant) -> Self { +impl CtapState { + pub fn new(env: &mut E) -> Self { storage::init(env).ok().unwrap(); - let client_pin = ClientPin::new(env.rng()); + let client_pin = ClientPin::new(env); CtapState { client_pin, #[cfg(feature = "with_ctap1")] - u2f_up_state: U2fUserPresenceState::new(U2F_UP_PROMPT_TIMEOUT, TOUCH_TIMEOUT), - stateful_command_permission: StatefulPermission::new_reset(now), + u2f_up_state: U2fUserPresenceState::new(), + stateful_command_permission: StatefulPermission::new_reset(env), large_blobs: LargeBlobs::new(), } } - pub fn update_timeouts(&mut self, now: CtapInstant) { - self.stateful_command_permission.clear_timer(now); - self.client_pin.update_timeouts(now); + pub fn update_timeouts(&mut self, env: &mut E) { + self.stateful_command_permission.clear_timer(env); + self.client_pin.update_timeouts(env); } pub fn increment_global_signature_counter( &mut self, - env: &mut impl Env, + env: &mut E, ) -> Result<(), Ctap2StatusCode> { if env.customization().use_signature_counter() { let increment = env.rng().gen_uniform_u32x8()[0] % 8 + 1; @@ -528,21 +522,19 @@ impl CtapState { // If alwaysUv is enabled and the authenticator does not support internal UV, // CTAP1 needs to be disabled. #[cfg(feature = "with_ctap1")] - pub fn allows_ctap1(&self, env: &mut impl Env) -> Result { + pub fn allows_ctap1(&self, env: &mut E) -> Result { Ok(!storage::has_always_uv(env)?) } pub fn process_command( &mut self, - env: &mut impl Env, + env: &mut E, command_cbor: &[u8], channel: Channel, - now: CtapInstant, ) -> Vec { let cmd = Command::deserialize(command_cbor); debug_ctap!(env, "Received command: {:#?}", cmd); - let response = - cmd.and_then(|command| self.process_parsed_command(env, command, channel, now)); + let response = cmd.and_then(|command| self.process_parsed_command(env, command, channel)); debug_ctap!(env, "Sending response: {:#?}", response); match response { Ok(response_data) => { @@ -564,21 +556,20 @@ impl CtapState { /// It should make command parsing easier to test. pub fn process_parsed_command( &mut self, - env: &mut impl Env, + env: &mut E, command: Command, channel: Channel, - now: CtapInstant, ) -> Result { // Correct behavior between CTAP1 and CTAP2 isn't defined yet. Just a guess. #[cfg(feature = "with_ctap1")] { // We create a block statement to wrap this assignment expression, because attributes // (like #[cfg]) are not supported on expressions. - self.u2f_up_state = U2fUserPresenceState::new(U2F_UP_PROMPT_TIMEOUT, TOUCH_TIMEOUT); + self.u2f_up_state = U2fUserPresenceState::new(); } self.stateful_command_permission .clear_old_channels(&channel); - self.stateful_command_permission.clear_timer(now); + self.stateful_command_permission.clear_timer(env); match (&command, self.stateful_command_permission.get_command()) { (Command::AuthenticatorGetNextAssertion, Ok(StatefulCommand::GetAssertion(_))) | (Command::AuthenticatorReset, Ok(StatefulCommand::Reset)) @@ -600,7 +591,7 @@ impl CtapState { (_, _) => self.stateful_command_permission.clear(), } match &channel { - Channel::MainHid(_) => self.process_fido_command(env, command, channel, now), + Channel::MainHid(_) => self.process_fido_command(env, command, channel), #[cfg(feature = "vendor_hid")] Channel::VendorHid(_) => self.process_vendor_command(env, command, channel), } @@ -608,23 +599,20 @@ impl CtapState { fn process_fido_command( &mut self, - env: &mut impl Env, + env: &mut E, command: Command, channel: Channel, - now: CtapInstant, ) -> Result { match command { Command::AuthenticatorMakeCredential(params) => { self.process_make_credential(env, params, channel) } Command::AuthenticatorGetAssertion(params) => { - self.process_get_assertion(env, params, channel, now) + self.process_get_assertion(env, params, channel) } Command::AuthenticatorGetNextAssertion => self.process_get_next_assertion(env), Command::AuthenticatorGetInfo => self.process_get_info(env), - Command::AuthenticatorClientPin(params) => { - self.client_pin.process_command(env, params, now) - } + Command::AuthenticatorClientPin(params) => self.client_pin.process_command(env, params), Command::AuthenticatorReset => self.process_reset(env, channel), Command::AuthenticatorCredentialManagement(params) => process_credential_management( env, @@ -632,7 +620,6 @@ impl CtapState { &mut self.client_pin, params, channel, - now, ), Command::AuthenticatorSelection => self.process_selection(env, channel), Command::AuthenticatorLargeBlobs(params) => { @@ -651,7 +638,7 @@ impl CtapState { fn process_vendor_command( &mut self, - env: &mut impl Env, + env: &mut E, command: Command, channel: Channel, ) -> Result { @@ -668,7 +655,7 @@ impl CtapState { fn pin_uv_auth_precheck( &mut self, - env: &mut impl Env, + env: &mut E, pin_uv_auth_param: &Option>, pin_uv_auth_protocol: Option, channel: Channel, @@ -706,7 +693,7 @@ impl CtapState { fn process_make_credential( &mut self, - env: &mut impl Env, + env: &mut E, make_credential_params: AuthenticatorMakeCredentialParameters, channel: Channel, ) -> Result { @@ -966,7 +953,7 @@ impl CtapState { // The computation is deterministic, and private_key expected to be unique. fn generate_cred_random( &mut self, - env: &mut impl Env, + env: &mut E, private_key: &PrivateKey, has_uv: bool, ) -> Result<[u8; 32], Ctap2StatusCode> { @@ -979,7 +966,7 @@ impl CtapState { // and returns the correct Get(Next)Assertion response. fn assertion_response( &mut self, - env: &mut impl Env, + env: &mut E, mut credential: PublicKeyCredentialSource, assertion_input: AssertionInput, number_of_credentials: Option, @@ -997,11 +984,10 @@ impl CtapState { let encrypted_output = if let Some(hmac_secret_input) = extensions.hmac_secret { let cred_random = self.generate_cred_random(env, &credential.private_key, has_uv)?; - Some(self.client_pin.process_hmac_secret( - env.rng(), - hmac_secret_input, - &cred_random, - )?) + Some( + self.client_pin + .process_hmac_secret(env, hmac_secret_input, &cred_random)?, + ) } else { None }; @@ -1068,7 +1054,7 @@ impl CtapState { // Returns the first applicable credential from the allow list. fn get_any_credential_from_allow_list( &mut self, - env: &mut impl Env, + env: &mut E, allow_list: Vec, rp_id: &str, rp_id_hash: &[u8], @@ -1089,10 +1075,9 @@ impl CtapState { fn process_get_assertion( &mut self, - env: &mut impl Env, + env: &mut E, get_assertion_params: AuthenticatorGetAssertionParameters, channel: Channel, - now: CtapInstant, ) -> Result { let AuthenticatorGetAssertionParameters { rp_id, @@ -1213,7 +1198,7 @@ impl CtapState { next_credential_keys, })); self.stateful_command_permission - .set_command(now, assertion_state, channel); + .set_command(env, assertion_state, channel); number_of_credentials }; self.assertion_response( @@ -1225,10 +1210,7 @@ impl CtapState { ) } - fn process_get_next_assertion( - &mut self, - env: &mut impl Env, - ) -> Result { + fn process_get_next_assertion(&mut self, env: &mut E) -> Result { let (assertion_input, credential_key) = self .stateful_command_permission .next_assertion_credential()?; @@ -1236,7 +1218,7 @@ impl CtapState { self.assertion_response(env, credential, assertion_input, None, true) } - fn process_get_info(&self, env: &mut impl Env) -> Result { + fn process_get_info(&self, env: &mut E) -> Result { let has_always_uv = storage::has_always_uv(env)?; #[cfg_attr(not(feature = "with_ctap1"), allow(unused_mut))] let mut versions = vec![ @@ -1310,7 +1292,7 @@ impl CtapState { fn process_reset( &mut self, - env: &mut impl Env, + env: &mut E, channel: Channel, ) -> Result { match self.stateful_command_permission.get_command()? { @@ -1320,19 +1302,19 @@ impl CtapState { check_user_presence(env, channel)?; storage::reset(env)?; - self.client_pin.reset(env.rng()); + self.client_pin.reset(env); #[cfg(feature = "with_ctap1")] { // We create a block statement to wrap this assignment expression, because attributes // (like #[cfg]) are not supported on expressions. - self.u2f_up_state = U2fUserPresenceState::new(U2F_UP_PROMPT_TIMEOUT, TOUCH_TIMEOUT); + self.u2f_up_state = U2fUserPresenceState::new(); } Ok(ResponseData::AuthenticatorReset) } fn process_selection( &self, - env: &mut impl Env, + env: &mut E, channel: Channel, ) -> Result { check_user_presence(env, channel)?; @@ -1341,7 +1323,7 @@ impl CtapState { fn process_vendor_configure( &mut self, - env: &mut impl Env, + env: &mut E, params: AuthenticatorVendorConfigureParameters, channel: Channel, ) -> Result { @@ -1395,7 +1377,7 @@ impl CtapState { fn process_vendor_upgrade( &mut self, - env: &mut impl Env, + env: &mut E, params: AuthenticatorVendorUpgradeParameters, ) -> Result { let AuthenticatorVendorUpgradeParameters { offset, data, hash } = params; @@ -1410,10 +1392,7 @@ impl CtapState { Ok(ResponseData::AuthenticatorVendorUpgrade) } - fn process_vendor_upgrade_info( - &self, - env: &mut impl Env, - ) -> Result { + fn process_vendor_upgrade_info(&self, env: &mut E) -> Result { let upgrade_locations = env .upgrade_storage() .ok_or(Ctap2StatusCode::CTAP1_ERR_INVALID_COMMAND)?; @@ -1426,7 +1405,7 @@ impl CtapState { pub fn generate_auth_data( &self, - env: &mut impl Env, + env: &mut E, rp_id_hash: &[u8], flag_byte: u8, ) -> Result, Ctap2StatusCode> { @@ -1445,13 +1424,13 @@ impl CtapState { } #[cfg(feature = "with_ctap1")] - pub fn u2f_grant_user_presence(&mut self, now: CtapInstant) { - self.u2f_up_state.grant_up(now) + pub fn u2f_grant_user_presence(&mut self, env: &mut E) { + self.u2f_up_state.grant_up(env) } #[cfg(feature = "with_ctap1")] - pub fn u2f_needs_user_presence(&mut self, now: CtapInstant) -> bool { - self.u2f_up_state.is_up_needed(now) + pub fn u2f_needs_user_presence(&mut self, env: &mut E) -> bool { + self.u2f_up_state.is_up_needed(env) } } @@ -1529,9 +1508,8 @@ mod test { #[test] fn test_get_info() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); - let info_reponse = - ctap_state.process_command(&mut env, &[0x04], DUMMY_CHANNEL, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); + let info_reponse = ctap_state.process_command(&mut env, &[0x04], DUMMY_CHANNEL); let expected_cbor = cbor_map_options! { 0x01 => cbor_array_vec![vec![ @@ -1585,7 +1563,7 @@ mod test { fn test_get_info_no_pin_protocol_v1() { let mut env = TestEnv::new(); env.customization_mut().set_allows_pin_protocol_v1(false); - let ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let ctap_state = CtapState::new(&mut env); let info_response = ctap_state.process_get_info(&mut env).unwrap(); match info_response { ResponseData::AuthenticatorGetInfo(response) => { @@ -1675,7 +1653,7 @@ mod test { #[test] fn test_resident_process_make_credential() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let make_credential_params = create_minimal_make_credential_parameters(); let make_credential_response = @@ -1693,7 +1671,7 @@ mod test { #[test] fn test_non_resident_process_make_credential() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.options.rk = false; @@ -1712,7 +1690,7 @@ mod test { #[test] fn test_process_make_credential_unsupported_algorithm() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.pub_key_cred_params = vec![]; @@ -1729,7 +1707,7 @@ mod test { fn test_process_make_credential_credential_excluded() { let mut env = TestEnv::new(); let excluded_private_key = PrivateKey::new_ecdsa(&mut env); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let excluded_credential_id = vec![0x01, 0x23, 0x45, 0x67]; let make_credential_params = @@ -1761,7 +1739,7 @@ mod test { #[test] fn test_process_make_credential_credential_with_cred_protect() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let test_policy = CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList; let make_credential_params = @@ -1812,7 +1790,7 @@ mod test { #[test] fn test_non_resident_process_make_credential_credential_with_cred_protect() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::new(&mut env); let test_policy = CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList; let mut make_credential_params = @@ -1855,7 +1833,7 @@ mod test { #[test] fn test_process_make_credential_hmac_secret() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let extensions = MakeCredentialExtensions { hmac_secret: true, @@ -1882,7 +1860,7 @@ mod test { #[test] fn test_process_make_credential_hmac_secret_resident_key() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let extensions = MakeCredentialExtensions { hmac_secret: true, @@ -1908,7 +1886,7 @@ mod test { #[test] fn test_process_make_credential_min_pin_length() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); // First part: The extension is ignored, since the RP ID is not on the list. let extensions = MakeCredentialExtensions { @@ -1957,7 +1935,7 @@ mod test { #[test] fn test_process_make_credential_cred_blob_ok() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let extensions = MakeCredentialExtensions { cred_blob: Some(vec![0xCB]), @@ -1989,7 +1967,7 @@ mod test { #[test] fn test_process_make_credential_cred_blob_too_big() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let extensions = MakeCredentialExtensions { cred_blob: Some(vec![0xCB; env.customization().max_cred_blob_length() + 1]), @@ -2021,7 +1999,7 @@ mod test { #[test] fn test_process_make_credential_large_blob_key() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let extensions = MakeCredentialExtensions { large_blob_key: Some(true), @@ -2053,10 +2031,14 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x91; PIN_TOKEN_LENGTH]; - let client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol); + let client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + pin_uv_auth_protocol, + ); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); ctap_state.client_pin = client_pin; storage::set_pin(&mut env, &[0x88; 16], 4).unwrap(); @@ -2105,7 +2087,7 @@ mod test { #[test] fn test_non_resident_process_make_credential_with_pin() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); storage::set_pin(&mut env, &[0x88; 16], 4).unwrap(); let mut make_credential_params = create_minimal_make_credential_parameters(); @@ -2125,7 +2107,7 @@ mod test { #[test] fn test_resident_process_make_credential_with_pin() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); storage::set_pin(&mut env, &[0x88; 16], 4).unwrap(); let make_credential_params = create_minimal_make_credential_parameters(); @@ -2140,7 +2122,7 @@ mod test { #[test] fn test_process_make_credential_with_pin_always_uv() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); storage::toggle_always_uv(&mut env).unwrap(); let make_credential_params = create_minimal_make_credential_parameters(); @@ -2181,7 +2163,7 @@ mod test { Some(vec!["example.com".to_string()]), ); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); test_helpers::enable_enterprise_attestation(&mut ctap_state, &mut env).unwrap(); let mut make_credential_params = create_minimal_make_credential_parameters(); @@ -2228,7 +2210,7 @@ mod test { ); assert!(customization::is_valid(env.customization())); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); test_helpers::enable_enterprise_attestation(&mut ctap_state, &mut env).unwrap(); let mut make_credential_params = create_minimal_make_credential_parameters(); @@ -2261,7 +2243,7 @@ mod test { env.customization_mut() .setup_enterprise_attestation(Some(EnterpriseAttestationMode::PlatformManaged), None); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.enterprise_attestation = Some(2); @@ -2288,7 +2270,7 @@ mod test { fn test_process_make_credential_cancelled() { let mut env = TestEnv::new(); env.user_presence().set(|| Err(UserPresenceError::Canceled)); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let make_credential_params = create_minimal_make_credential_parameters(); let make_credential_response = @@ -2383,7 +2365,7 @@ mod test { #[test] fn test_resident_process_get_assertion() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let make_credential_params = create_minimal_make_credential_parameters(); assert!(ctap_state @@ -2402,12 +2384,8 @@ mod test { pin_uv_auth_param: None, pin_uv_auth_protocol: None, }; - let get_assertion_response = ctap_state.process_get_assertion( - &mut env, - get_assertion_params, - DUMMY_CHANNEL, - CtapInstant::new(0), - ); + let get_assertion_response = + ctap_state.process_get_assertion(&mut env, get_assertion_params, DUMMY_CHANNEL); let signature_counter = storage::global_signature_counter(&mut env).unwrap(); check_assertion_response(get_assertion_response, vec![0x1D], signature_counter, None); } @@ -2468,7 +2446,7 @@ mod test { fn test_helper_process_get_assertion_hmac_secret(pin_uv_auth_protocol: PinUvAuthProtocol) { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let make_extensions = MakeCredentialExtensions { hmac_secret: true, @@ -2495,22 +2473,17 @@ mod test { permissions: None, permissions_rp_id: None, }; - let key_agreement_response = - ctap_state - .client_pin - .process_command(&mut env, client_pin_params, CtapInstant::new(0)); + let key_agreement_response = ctap_state + .client_pin + .process_command(&mut env, client_pin_params); let get_assertion_params = get_assertion_hmac_secret_params( key_agreement_key, key_agreement_response.unwrap(), Some(credential_id), pin_uv_auth_protocol, ); - let get_assertion_response = ctap_state.process_get_assertion( - &mut env, - get_assertion_params, - DUMMY_CHANNEL, - CtapInstant::new(0), - ); + let get_assertion_response = + ctap_state.process_get_assertion(&mut env, get_assertion_params, DUMMY_CHANNEL); assert!(get_assertion_response.is_ok()); } @@ -2529,7 +2502,7 @@ mod test { ) { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let make_extensions = MakeCredentialExtensions { hmac_secret: true, @@ -2551,22 +2524,17 @@ mod test { permissions: None, permissions_rp_id: None, }; - let key_agreement_response = - ctap_state - .client_pin - .process_command(&mut env, client_pin_params, CtapInstant::new(0)); + let key_agreement_response = ctap_state + .client_pin + .process_command(&mut env, client_pin_params); let get_assertion_params = get_assertion_hmac_secret_params( key_agreement_key, key_agreement_response.unwrap(), None, pin_uv_auth_protocol, ); - let get_assertion_response = ctap_state.process_get_assertion( - &mut env, - get_assertion_params, - DUMMY_CHANNEL, - CtapInstant::new(0), - ); + let get_assertion_response = + ctap_state.process_get_assertion(&mut env, get_assertion_params, DUMMY_CHANNEL); assert!(get_assertion_response.is_ok()); } @@ -2585,7 +2553,7 @@ mod test { let mut env = TestEnv::new(); let private_key = PrivateKey::new_ecdsa(&mut env); let credential_id = env.rng().gen_uniform_u8x32().to_vec(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let cred_desc = PublicKeyCredentialDescriptor { key_type: PublicKeyCredentialType::PublicKey, @@ -2622,12 +2590,8 @@ mod test { pin_uv_auth_param: None, pin_uv_auth_protocol: None, }; - let get_assertion_response = ctap_state.process_get_assertion( - &mut env, - get_assertion_params, - DUMMY_CHANNEL, - CtapInstant::new(0), - ); + let get_assertion_response = + ctap_state.process_get_assertion(&mut env, get_assertion_params, DUMMY_CHANNEL); assert_eq!( get_assertion_response, Err(Ctap2StatusCode::CTAP2_ERR_NO_CREDENTIALS), @@ -2645,12 +2609,8 @@ mod test { pin_uv_auth_param: None, pin_uv_auth_protocol: None, }; - let get_assertion_response = ctap_state.process_get_assertion( - &mut env, - get_assertion_params, - DUMMY_CHANNEL, - CtapInstant::new(0), - ); + let get_assertion_response = + ctap_state.process_get_assertion(&mut env, get_assertion_params, DUMMY_CHANNEL); let signature_counter = storage::global_signature_counter(&mut env).unwrap(); check_assertion_response(get_assertion_response, vec![0x1D], signature_counter, None); @@ -2682,12 +2642,8 @@ mod test { pin_uv_auth_param: None, pin_uv_auth_protocol: None, }; - let get_assertion_response = ctap_state.process_get_assertion( - &mut env, - get_assertion_params, - DUMMY_CHANNEL, - CtapInstant::new(0), - ); + let get_assertion_response = + ctap_state.process_get_assertion(&mut env, get_assertion_params, DUMMY_CHANNEL); assert_eq!( get_assertion_response, Err(Ctap2StatusCode::CTAP2_ERR_NO_CREDENTIALS), @@ -2697,7 +2653,7 @@ mod test { #[test] fn test_non_resident_process_get_assertion_with_cred_protect() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::new(&mut env); let test_policy = CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList; let mut make_credential_params = @@ -2727,12 +2683,8 @@ mod test { pin_uv_auth_param: None, pin_uv_auth_protocol: None, }; - let get_assertion_response = ctap_state.process_get_assertion( - &mut env, - get_assertion_params, - DUMMY_CHANNEL, - CtapInstant::new(0), - ); + let get_assertion_response = + ctap_state.process_get_assertion(&mut env, get_assertion_params, DUMMY_CHANNEL); assert!(get_assertion_response.is_ok()); let test_policy = CredentialProtectionPolicy::UserVerificationRequired; @@ -2763,12 +2715,8 @@ mod test { pin_uv_auth_param: None, pin_uv_auth_protocol: None, }; - let get_assertion_response = ctap_state.process_get_assertion( - &mut env, - get_assertion_params, - DUMMY_CHANNEL, - CtapInstant::new(0), - ); + let get_assertion_response = + ctap_state.process_get_assertion(&mut env, get_assertion_params, DUMMY_CHANNEL); assert_eq!( get_assertion_response, Err(Ctap2StatusCode::CTAP2_ERR_NO_CREDENTIALS), @@ -2780,7 +2728,7 @@ mod test { let mut env = TestEnv::new(); let private_key = PrivateKey::new_ecdsa(&mut env); let credential_id = env.rng().gen_uniform_u8x32().to_vec(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let credential = PublicKeyCredentialSource { key_type: PublicKeyCredentialType::PublicKey, @@ -2814,12 +2762,8 @@ mod test { pin_uv_auth_param: None, pin_uv_auth_protocol: None, }; - let get_assertion_response = ctap_state.process_get_assertion( - &mut env, - get_assertion_params, - DUMMY_CHANNEL, - CtapInstant::new(0), - ); + let get_assertion_response = + ctap_state.process_get_assertion(&mut env, get_assertion_params, DUMMY_CHANNEL); let signature_counter = storage::global_signature_counter(&mut env).unwrap(); let expected_extension_cbor = [ 0xA1, 0x68, 0x63, 0x72, 0x65, 0x64, 0x42, 0x6C, 0x6F, 0x62, 0x41, 0xCB, @@ -2836,7 +2780,7 @@ mod test { #[test] fn test_non_resident_process_get_assertion_with_cred_blob() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::new(&mut env); let extensions = MakeCredentialExtensions { cred_blob: Some(vec![0xCB]), @@ -2883,12 +2827,8 @@ mod test { pin_uv_auth_param: None, pin_uv_auth_protocol: None, }; - let get_assertion_response = ctap_state.process_get_assertion( - &mut env, - get_assertion_params, - DUMMY_CHANNEL, - CtapInstant::new(0), - ); + let get_assertion_response = + ctap_state.process_get_assertion(&mut env, get_assertion_params, DUMMY_CHANNEL); let signature_counter = storage::global_signature_counter(&mut env).unwrap(); let expected_extension_cbor = [ 0xA1, 0x68, 0x63, 0x72, 0x65, 0x64, 0x42, 0x6C, 0x6F, 0x62, 0x41, 0xCB, @@ -2907,7 +2847,7 @@ mod test { let mut env = TestEnv::new(); let private_key = PrivateKey::new_ecdsa(&mut env); let credential_id = env.rng().gen_uniform_u8x32().to_vec(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let credential = PublicKeyCredentialSource { key_type: PublicKeyCredentialType::PublicKey, @@ -2941,12 +2881,8 @@ mod test { pin_uv_auth_param: None, pin_uv_auth_protocol: None, }; - let get_assertion_response = ctap_state.process_get_assertion( - &mut env, - get_assertion_params, - DUMMY_CHANNEL, - CtapInstant::new(0), - ); + let get_assertion_response = + ctap_state.process_get_assertion(&mut env, get_assertion_params, DUMMY_CHANNEL); let large_blob_key = match get_assertion_response.unwrap() { ResponseData::AuthenticatorGetAssertion(get_assertion_response) => { get_assertion_response.large_blob_key.unwrap() @@ -2962,10 +2898,14 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x88; 32]; - let client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol); + let client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + pin_uv_auth_protocol, + ); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let mut make_credential_params = create_minimal_make_credential_parameters(); let user1 = PublicKeyCredentialUserEntity { @@ -3012,12 +2952,8 @@ mod test { pin_uv_auth_param: Some(pin_uv_auth_param), pin_uv_auth_protocol: Some(pin_uv_auth_protocol), }; - let get_assertion_response = ctap_state.process_get_assertion( - &mut env, - get_assertion_params, - DUMMY_CHANNEL, - CtapInstant::new(0), - ); + let get_assertion_response = + ctap_state.process_get_assertion(&mut env, get_assertion_params, DUMMY_CHANNEL); let signature_counter = storage::global_signature_counter(&mut env).unwrap(); check_assertion_response_with_user( get_assertion_response, @@ -3058,7 +2994,7 @@ mod test { #[test] fn test_process_get_next_assertion_three_credentials_no_uv() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.user.user_id = vec![0x01]; @@ -3097,12 +3033,8 @@ mod test { pin_uv_auth_param: None, pin_uv_auth_protocol: None, }; - let get_assertion_response = ctap_state.process_get_assertion( - &mut env, - get_assertion_params, - DUMMY_CHANNEL, - CtapInstant::new(0), - ); + let get_assertion_response = + ctap_state.process_get_assertion(&mut env, get_assertion_params, DUMMY_CHANNEL); let signature_counter = storage::global_signature_counter(&mut env).unwrap(); check_assertion_response( get_assertion_response, @@ -3127,7 +3059,7 @@ mod test { #[test] fn test_process_get_next_assertion_not_allowed() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let get_assertion_response = ctap_state.process_get_next_assertion(&mut env); assert_eq!( @@ -3158,12 +3090,8 @@ mod test { pin_uv_auth_param: None, pin_uv_auth_protocol: None, }; - let get_assertion_response = ctap_state.process_get_assertion( - &mut env, - get_assertion_params, - DUMMY_CHANNEL, - CtapInstant::new(0), - ); + let get_assertion_response = + ctap_state.process_get_assertion(&mut env, get_assertion_params, DUMMY_CHANNEL); assert!(get_assertion_response.is_ok()); // This is a MakeCredential command. @@ -3179,7 +3107,7 @@ mod test { 4 => cbor_array![ES256_CRED_PARAM], }; assert!(cbor_write(cbor_value, &mut command_cbor).is_ok()); - ctap_state.process_command(&mut env, &command_cbor, DUMMY_CHANNEL, CtapInstant::new(0)); + ctap_state.process_command(&mut env, &command_cbor, DUMMY_CHANNEL); let get_assertion_response = ctap_state.process_get_next_assertion(&mut env); assert_eq!( @@ -3192,7 +3120,7 @@ mod test { fn test_process_reset() { let mut env = TestEnv::new(); let private_key = PrivateKey::new_ecdsa(&mut env); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let credential_id = vec![0x01, 0x23, 0x45, 0x67]; let credential_source = PublicKeyCredentialSource { @@ -3212,8 +3140,7 @@ mod test { assert!(storage::store_credential(&mut env, credential_source).is_ok()); assert!(storage::count_credentials(&mut env).unwrap() > 0); - let reset_reponse = - ctap_state.process_command(&mut env, &[0x07], DUMMY_CHANNEL, CtapInstant::new(0)); + let reset_reponse = ctap_state.process_command(&mut env, &[0x07], DUMMY_CHANNEL); let expected_response = vec![0x00]; assert_eq!(reset_reponse, expected_response); assert!(storage::count_credentials(&mut env).unwrap() == 0); @@ -3223,7 +3150,7 @@ mod test { fn test_process_reset_cancelled() { let mut env = TestEnv::new(); env.user_presence().set(|| Err(UserPresenceError::Canceled)); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let reset_reponse = ctap_state.process_reset(&mut env, DUMMY_CHANNEL); @@ -3236,10 +3163,10 @@ mod test { #[test] fn test_process_reset_not_first() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); // This is a GetNextAssertion command. - ctap_state.process_command(&mut env, &[0x08], DUMMY_CHANNEL, CtapInstant::new(0)); + ctap_state.process_command(&mut env, &[0x08], DUMMY_CHANNEL); let reset_reponse = ctap_state.process_reset(&mut env, DUMMY_CHANNEL); assert_eq!(reset_reponse, Err(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED)); @@ -3248,15 +3175,11 @@ mod test { #[test] fn test_process_credential_management_unknown_subcommand() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); // The subcommand 0xEE does not exist. - let reponse = ctap_state.process_command( - &mut env, - &[0x0A, 0xA1, 0x01, 0x18, 0xEE], - DUMMY_CHANNEL, - CtapInstant::new(0), - ); + let reponse = + ctap_state.process_command(&mut env, &[0x0A, 0xA1, 0x01, 0x18, 0xEE], DUMMY_CHANNEL); let expected_response = vec![Ctap2StatusCode::CTAP2_ERR_INVALID_SUBCOMMAND as u8]; assert_eq!(reponse, expected_response); } @@ -3264,11 +3187,10 @@ mod test { #[test] fn test_process_unknown_command() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); // This command does not exist. - let reponse = - ctap_state.process_command(&mut env, &[0xDF], DUMMY_CHANNEL, CtapInstant::new(0)); + let reponse = ctap_state.process_command(&mut env, &[0xDF], DUMMY_CHANNEL); let expected_response = vec![Ctap2StatusCode::CTAP1_ERR_INVALID_COMMAND as u8]; assert_eq!(reponse, expected_response); } @@ -3276,7 +3198,7 @@ mod test { #[test] fn test_signature_counter() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let mut last_counter = storage::global_signature_counter(&mut env).unwrap(); assert!(last_counter > 0); @@ -3293,7 +3215,7 @@ mod test { #[test] fn test_vendor_configure() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); // Nothing should be configured at the beginning let response = ctap_state.process_vendor_configure( @@ -3401,7 +3323,7 @@ mod test { // The test metadata storage has size 0x1000. // The test identifier matches partition B. let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); const METADATA_LEN: usize = 0x1000; let metadata = vec![0xFF; METADATA_LEN]; @@ -3480,7 +3402,7 @@ mod test { fn test_vendor_upgrade_no_second_partition() { let mut env = TestEnv::new(); env.disable_upgrade_storage(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let data = vec![0xFF; 0x1000]; let hash = Sha256::hash(&data); @@ -3498,7 +3420,7 @@ mod test { #[test] fn test_vendor_upgrade_info() { let mut env = TestEnv::new(); - let ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let ctap_state = CtapState::::new(&mut env); let bundle_identifier = env.upgrade_storage().unwrap().bundle_identifier(); let upgrade_info_reponse = ctap_state.process_vendor_upgrade_info(&mut env); @@ -3515,7 +3437,7 @@ mod test { #[test] fn test_permission_timeout() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); // Write 2 credentials for later assertions. for i in 0..3 { @@ -3542,21 +3464,21 @@ mod test { &mut env, Command::AuthenticatorGetAssertion(get_assertion_params), DUMMY_CHANNEL, - CtapInstant::new(0), ); + env.clock() + .advance(STATEFUL_COMMAND_TIMEOUT_DURATION_MS - 1); assert!(get_assertion_response.is_ok()); let get_next_assertion_response = ctap_state.process_parsed_command( &mut env, Command::AuthenticatorGetNextAssertion, DUMMY_CHANNEL, - CtapInstant::new(0) + STATEFUL_COMMAND_TIMEOUT_DURATION - Milliseconds(1 as ClockInt), ); assert!(get_next_assertion_response.is_ok()); + env.clock().advance(1); let get_next_assertion_response = ctap_state.process_parsed_command( &mut env, Command::AuthenticatorGetNextAssertion, DUMMY_CHANNEL, - CtapInstant::new(0) + STATEFUL_COMMAND_TIMEOUT_DURATION + Milliseconds(1 as ClockInt), ); assert_eq!( get_next_assertion_response, @@ -3567,14 +3489,11 @@ mod test { #[test] fn test_reset_timeout() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); - let response = ctap_state.process_parsed_command( - &mut env, - Command::AuthenticatorReset, - DUMMY_CHANNEL, - CtapInstant::new(0) + RESET_TIMEOUT_DURATION + Milliseconds(1 as ClockInt), - ); + env.clock().advance(RESET_TIMEOUT_DURATION_MS); + let response = + ctap_state.process_parsed_command(&mut env, Command::AuthenticatorReset, DUMMY_CHANNEL); assert_eq!(response, Err(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED)); } @@ -3583,8 +3502,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let private_key = PrivateKey::new_ecdsa(&mut env); let credential_source = PublicKeyCredentialSource { @@ -3602,7 +3525,7 @@ mod test { large_blob_key: None, }; - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); ctap_state.client_pin = client_pin; for i in 0..3 { @@ -3627,7 +3550,6 @@ mod test { &mut env, Command::AuthenticatorCredentialManagement(cred_management_params), DUMMY_CHANNEL, - CtapInstant::new(0), ); assert!(matches!( response, @@ -3640,11 +3562,12 @@ mod test { pin_uv_auth_protocol: None, pin_uv_auth_param: None, }; + env.clock() + .advance(STATEFUL_COMMAND_TIMEOUT_DURATION_MS - 1); let response = ctap_state.process_parsed_command( &mut env, Command::AuthenticatorCredentialManagement(cred_management_params), DUMMY_CHANNEL, - CtapInstant::new(0) + STATEFUL_COMMAND_TIMEOUT_DURATION - Milliseconds(1 as ClockInt), ); assert!(matches!( response, @@ -3657,11 +3580,11 @@ mod test { pin_uv_auth_protocol: None, pin_uv_auth_param: None, }; + env.clock().advance(1); let response = ctap_state.process_parsed_command( &mut env, Command::AuthenticatorCredentialManagement(cred_management_params), DUMMY_CHANNEL, - CtapInstant::new(0) + STATEFUL_COMMAND_TIMEOUT_DURATION + Milliseconds(1 as ClockInt), ); assert_eq!(response, Err(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED)); } @@ -3693,7 +3616,7 @@ mod test { #[test] fn test_channel_interleaving() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); const NEW_CHANNEL: Channel = Channel::MainHid([0xAA, 0xAA, 0xAA, 0xAA]); // Write 3 credentials for later assertions. @@ -3721,7 +3644,6 @@ mod test { &mut env, Command::AuthenticatorGetAssertion(get_assertion_params), DUMMY_CHANNEL, - CtapInstant::new(0), ); assert!(matches!( get_assertion_response, @@ -3731,7 +3653,6 @@ mod test { &mut env, Command::AuthenticatorGetNextAssertion, DUMMY_CHANNEL, - CtapInstant::new(0), ); assert!(get_next_assertion_response.is_ok()); assert!(matches!( @@ -3742,7 +3663,6 @@ mod test { &mut env, Command::AuthenticatorGetNextAssertion, NEW_CHANNEL, - CtapInstant::new(0), ); assert_eq!( get_next_assertion_response, @@ -3752,7 +3672,6 @@ mod test { &mut env, Command::AuthenticatorGetNextAssertion, DUMMY_CHANNEL, - CtapInstant::new(0), ); assert_eq!( get_next_assertion_response, @@ -3763,13 +3682,12 @@ mod test { #[test] fn test_get_info_command() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let response = ctap_state.process_parsed_command( &mut env, Command::AuthenticatorGetInfo, DUMMY_CHANNEL, - CtapInstant::new(0), ); assert!(matches!( response, @@ -3781,7 +3699,6 @@ mod test { &mut env, Command::AuthenticatorGetInfo, VENDOR_CHANNEL, - CtapInstant::new(0), ); assert!(matches!( response, @@ -3794,12 +3711,11 @@ mod test { #[cfg(feature = "vendor_hid")] fn test_vendor_hid_does_not_support_fido_command() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::new(&mut env); let response = ctap_state.process_parsed_command( &mut env, Command::AuthenticatorGetNextAssertion, VENDOR_CHANNEL, - CtapInstant::new(0), ); assert_eq!(response, Err(Ctap2StatusCode::CTAP1_ERR_INVALID_COMMAND)); } @@ -3808,13 +3724,12 @@ mod test { #[cfg(feature = "vendor_hid")] fn test_vendor_hid() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let response = ctap_state.process_parsed_command( &mut env, Command::AuthenticatorVendorUpgradeInfo, VENDOR_CHANNEL, - CtapInstant::new(0), ); assert!(matches!( response, @@ -3824,7 +3739,6 @@ mod test { &mut env, Command::AuthenticatorVendorUpgradeInfo, DUMMY_CHANNEL, - CtapInstant::new(0), ); assert_eq!(response, Err(Ctap2StatusCode::CTAP1_ERR_INVALID_COMMAND)); } diff --git a/src/ctap/timed_permission.rs b/src/ctap/timed_permission.rs deleted file mode 100644 index 326912a6..00000000 --- a/src/ctap/timed_permission.rs +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright 2019-2021 Google LLC -// -// Licensed under the Apache License, Version 2 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::super::clock::{ClockInt, CtapInstant}; -use embedded_time::duration::Milliseconds; - -#[derive(Debug, Copy, Clone)] -pub enum TimedPermission { - Waiting, - Granted(CtapInstant), -} - -impl TimedPermission { - pub fn waiting() -> TimedPermission { - TimedPermission::Waiting - } - - pub fn granted(now: CtapInstant, grant_duration: Milliseconds) -> TimedPermission { - // TODO: Should panic or saturate if the grant duration is too big. - TimedPermission::Granted(now.checked_add(grant_duration).unwrap()) - } - - // Checks if the timeout is not reached, false for differing ClockValue frequencies. - pub fn is_granted(&self, now: CtapInstant) -> bool { - if let TimedPermission::Granted(timeout) = self { - return timeout.checked_duration_since(&now).is_some(); - } - false - } - - // Consumes the state and returns the current new permission state at time "now". - // Returns a new state for differing ClockValue frequencies. - pub fn check_expiration(self, now: CtapInstant) -> TimedPermission { - if let TimedPermission::Granted(timeout) = self { - if timeout.checked_duration_since(&now).is_some() { - return TimedPermission::Granted(timeout); - } - } - TimedPermission::Waiting - } -} - -#[cfg(feature = "with_ctap1")] -#[derive(Debug)] -pub struct U2fUserPresenceState { - // If user presence was recently requested, its timeout is saved here. - needs_up: TimedPermission, - // Button touch timeouts, while user presence is requested, are saved here. - has_up: TimedPermission, - // This is the timeout duration of user presence requests. - request_duration: Milliseconds, - // This is the timeout duration of button touches. - presence_duration: Milliseconds, -} - -#[cfg(feature = "with_ctap1")] -impl U2fUserPresenceState { - pub fn new( - request_duration: Milliseconds, - presence_duration: Milliseconds, - ) -> U2fUserPresenceState { - U2fUserPresenceState { - needs_up: TimedPermission::Waiting, - has_up: TimedPermission::Waiting, - request_duration, - presence_duration, - } - } - - // Granting user presence is ignored if it needs activation, but waits. Also cleans up. - pub fn grant_up(&mut self, now: CtapInstant) { - self.check_expiration(now); - if self.needs_up.is_granted(now) { - self.needs_up = TimedPermission::Waiting; - self.has_up = TimedPermission::granted(now, self.presence_duration); - } - } - - // This marks user presence as needed or uses it up if already granted. Also cleans up. - pub fn consume_up(&mut self, now: CtapInstant) -> bool { - self.check_expiration(now); - if self.has_up.is_granted(now) { - self.has_up = TimedPermission::Waiting; - true - } else { - self.needs_up = TimedPermission::granted(now, self.request_duration); - false - } - } - - // Returns if user presence was requested. Also cleans up. - pub fn is_up_needed(&mut self, now: CtapInstant) -> bool { - self.check_expiration(now); - self.needs_up.is_granted(now) - } - - // If you don't regularly call any other function, not cleaning up leads to overflow problems. - pub fn check_expiration(&mut self, now: CtapInstant) { - self.needs_up = self.needs_up.check_expiration(now); - self.has_up = self.has_up.check_expiration(now); - } -} - -#[cfg(feature = "with_ctap1")] -#[cfg(test)] -mod test { - use super::*; - - fn zero() -> CtapInstant { - CtapInstant::new(0) - } - - fn big_positive() -> CtapInstant { - CtapInstant::new(u64::MAX / 1000 - 1) - } - - fn request_duration() -> Milliseconds { - Milliseconds::new(1000) - } - - fn presence_duration() -> Milliseconds { - Milliseconds::new(1000) - } - - fn epsilon() -> Milliseconds { - Milliseconds::new(1) - } - - fn grant_up_when_needed(start_time: CtapInstant) { - let mut u2f_state = U2fUserPresenceState::new(request_duration(), presence_duration()); - assert!(!u2f_state.consume_up(start_time)); - assert!(u2f_state.is_up_needed(start_time)); - u2f_state.grant_up(start_time); - assert!(u2f_state.consume_up(start_time)); - assert!(!u2f_state.consume_up(start_time)); - } - - fn need_up_timeout(start_time: CtapInstant) { - let mut u2f_state = U2fUserPresenceState::new(request_duration(), presence_duration()); - assert!(!u2f_state.consume_up(start_time)); - assert!(u2f_state.is_up_needed(start_time)); - // The timeout excludes equality, so it should be over at this instant. - assert!(!u2f_state.is_up_needed( - start_time - .checked_add(presence_duration() + epsilon()) - .unwrap() - )); - } - - fn grant_up_timeout(start_time: CtapInstant) { - let mut u2f_state = U2fUserPresenceState::new(request_duration(), presence_duration()); - assert!(!u2f_state.consume_up(start_time)); - assert!(u2f_state.is_up_needed(start_time)); - u2f_state.grant_up(start_time); - // The timeout excludes equality, so it should be over at this instant. - assert!(!u2f_state.consume_up( - start_time - .checked_add(presence_duration() + epsilon()) - .unwrap() - )); - } - - #[test] - fn test_grant_up_timeout() { - grant_up_timeout(zero()); - grant_up_timeout(big_positive()); - } - - #[test] - fn test_need_up_timeout() { - need_up_timeout(zero()); - need_up_timeout(big_positive()); - } - - #[test] - fn test_grant_up_when_needed() { - grant_up_when_needed(zero()); - grant_up_when_needed(big_positive()); - } - - #[test] - fn test_grant_up_without_need() { - let mut u2f_state = U2fUserPresenceState::new(request_duration(), presence_duration()); - u2f_state.grant_up(zero()); - assert!(!u2f_state.is_up_needed(zero())); - assert!(!u2f_state.consume_up(zero())); - } -} diff --git a/src/ctap/token_state.rs b/src/ctap/token_state.rs index c2811502..180735bb 100644 --- a/src/ctap/token_state.rs +++ b/src/ctap/token_state.rs @@ -12,22 +12,20 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::api::clock::Clock; use crate::ctap::client_pin::PinPermission; use crate::ctap::status_code::Ctap2StatusCode; -use crate::ctap::timed_permission::TimedPermission; +use crate::env::Env; use alloc::string::String; use crypto::sha256::Sha256; use crypto::Hash256; -use embedded_time::duration::Milliseconds; - -use crate::clock::{ClockInt, CtapInstant}; /// Timeout for auth tokens. /// /// This usage time limit is correct for USB, BLE, and internal. /// NFC only allows 19.8 seconds. /// TODO(#15) multiplex over transports, add NFC -const INITIAL_USAGE_TIME_LIMIT: Milliseconds = Milliseconds(30000 as ClockInt); +const INITIAL_USAGE_TIME_LIMIT_MS: usize = 30000; /// Implements pinUvAuthToken state from section 6.5.2.1. /// @@ -35,22 +33,22 @@ const INITIAL_USAGE_TIME_LIMIT: Milliseconds = Milliseconds(30000 as C /// built-in user verification. Therefore, we never cache user presence. /// /// This implementation does not use a rolling timer. -pub struct PinUvAuthTokenState { +pub struct PinUvAuthTokenState { // Relies on the fact that all permissions are represented by powers of two. permissions_set: u8, permissions_rp_id: Option, - usage_timer: TimedPermission, + usage_timer: ::Timer, user_verified: bool, in_use: bool, } -impl PinUvAuthTokenState { +impl PinUvAuthTokenState { /// Creates a pinUvAuthToken state without permissions. - pub fn new() -> PinUvAuthTokenState { + pub fn new() -> Self { PinUvAuthTokenState { permissions_set: 0, permissions_rp_id: None, - usage_timer: TimedPermission::waiting(), + usage_timer: ::Timer::default(), user_verified: false, in_use: false, } @@ -113,19 +111,18 @@ impl PinUvAuthTokenState { } /// Starts the timer for pinUvAuthToken usage. - pub fn begin_using_pin_uv_auth_token(&mut self, now: CtapInstant) { + pub fn begin_using_pin_uv_auth_token(&mut self, env: &mut E) { self.user_verified = true; - self.usage_timer = TimedPermission::granted(now, INITIAL_USAGE_TIME_LIMIT); + self.usage_timer = env.clock().make_timer(INITIAL_USAGE_TIME_LIMIT_MS); self.in_use = true; } /// Updates the usage timer, and disables the pinUvAuthToken on timeout. - pub fn pin_uv_auth_token_usage_timer_observer(&mut self, now: CtapInstant) { + pub fn pin_uv_auth_token_usage_timer_observer(&mut self, env: &mut E) { if !self.in_use { return; } - self.usage_timer = self.usage_timer.check_expiration(now); - if !self.usage_timer.is_granted(now) { + if env.clock().is_elapsed(&self.usage_timer) { self.stop_using_pin_uv_auth_token(); } } @@ -149,7 +146,7 @@ impl PinUvAuthTokenState { pub fn stop_using_pin_uv_auth_token(&mut self) { self.permissions_rp_id = None; self.permissions_set = 0; - self.usage_timer = TimedPermission::waiting(); + self.usage_timer = ::Timer::default(); self.user_verified = false; self.in_use = false; } @@ -158,27 +155,28 @@ impl PinUvAuthTokenState { #[cfg(test)] mod test { use super::*; + use crate::env::test::TestEnv; use enum_iterator::IntoEnumIterator; #[test] fn test_observer() { - let mut token_state = PinUvAuthTokenState::new(); - let mut now: CtapInstant = CtapInstant::new(0); - token_state.begin_using_pin_uv_auth_token(now); + let mut env = TestEnv::new(); + let mut token_state = PinUvAuthTokenState::::new(); + token_state.begin_using_pin_uv_auth_token(&mut env); assert!(token_state.is_in_use()); - now = now + Milliseconds(100_u32); - token_state.pin_uv_auth_token_usage_timer_observer(now); + env.clock().advance(100); + token_state.pin_uv_auth_token_usage_timer_observer(&mut env); assert!(token_state.is_in_use()); - now = now + INITIAL_USAGE_TIME_LIMIT; - token_state.pin_uv_auth_token_usage_timer_observer(now); + env.clock().advance(INITIAL_USAGE_TIME_LIMIT_MS); + token_state.pin_uv_auth_token_usage_timer_observer(&mut env); assert!(!token_state.is_in_use()); } #[test] fn test_stop() { - let mut token_state = PinUvAuthTokenState::new(); - let now: CtapInstant = CtapInstant::new(0); - token_state.begin_using_pin_uv_auth_token(now); + let mut env = TestEnv::new(); + let mut token_state = PinUvAuthTokenState::::new(); + token_state.begin_using_pin_uv_auth_token(&mut env); assert!(token_state.is_in_use()); token_state.stop_using_pin_uv_auth_token(); assert!(!token_state.is_in_use()); @@ -186,7 +184,7 @@ mod test { #[test] fn test_permissions() { - let mut token_state = PinUvAuthTokenState::new(); + let mut token_state = PinUvAuthTokenState::::new(); token_state.set_permissions(0xFF); for permission in PinPermission::into_enum_iter() { assert_eq!(token_state.has_permission(permission), Ok(())); @@ -211,7 +209,7 @@ mod test { #[test] fn test_permissions_rp_id_none() { - let mut token_state = PinUvAuthTokenState::new(); + let mut token_state = PinUvAuthTokenState::::new(); let example_hash = Sha256::hash(b"example.com"); token_state.set_permissions_rp_id(None); assert_eq!(token_state.has_no_permissions_rp_id(), Ok(())); @@ -227,7 +225,7 @@ mod test { #[test] fn test_permissions_rp_id_some() { - let mut token_state = PinUvAuthTokenState::new(); + let mut token_state = PinUvAuthTokenState::::new(); let example_hash = Sha256::hash(b"example.com"); token_state.set_permissions_rp_id(Some(String::from("example.com"))); @@ -262,14 +260,14 @@ mod test { #[test] fn test_user_verified_flag() { - let mut token_state = PinUvAuthTokenState::new(); + let mut env = TestEnv::new(); + let mut token_state = PinUvAuthTokenState::::new(); assert!(!token_state.get_user_verified_flag_value()); - let now: CtapInstant = CtapInstant::new(0); - token_state.begin_using_pin_uv_auth_token(now); + token_state.begin_using_pin_uv_auth_token(&mut env); assert!(token_state.get_user_verified_flag_value()); token_state.clear_user_verified_flag(); assert!(!token_state.get_user_verified_flag_value()); - token_state.begin_using_pin_uv_auth_token(now); + token_state.begin_using_pin_uv_auth_token(&mut env); assert!(token_state.get_user_verified_flag_value()); token_state.stop_using_pin_uv_auth_token(); assert!(!token_state.get_user_verified_flag_value()); diff --git a/src/ctap/u2f_up.rs b/src/ctap/u2f_up.rs new file mode 100644 index 00000000..bf9f0ef7 --- /dev/null +++ b/src/ctap/u2f_up.rs @@ -0,0 +1,137 @@ +// Copyright 2019-2021 Google LLC +// +// Licensed under the Apache License, Version 2 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::TOUCH_TIMEOUT_MS; +use crate::api::clock::Clock; +use crate::env::Env; + +const U2F_UP_PROMPT_TIMEOUT_MS: usize = 10000; + +pub struct U2fUserPresenceState { + /// If user presence was recently requested, its timeout is saved here. + needs_up: ::Timer, + + /// Button touch timeouts, while user presence is requested, are saved here. + has_up: ::Timer, +} + +impl U2fUserPresenceState { + pub fn new() -> U2fUserPresenceState { + U2fUserPresenceState { + needs_up: ::Timer::default(), + has_up: ::Timer::default(), + } + } + + /// Allows consuming user presence until timeout, if it was needed. + /// + /// If user presence was not requested, granting user presence does nothing. + pub fn grant_up(&mut self, env: &mut E) { + if !env.clock().is_elapsed(&self.needs_up) { + self.needs_up = ::Timer::default(); + self.has_up = env.clock().make_timer(TOUCH_TIMEOUT_MS); + } + } + + /// Returns whether user presence was granted within the timeout and not yet consumed. + pub fn consume_up(&mut self, env: &mut E) -> bool { + if !env.clock().is_elapsed(&self.has_up) { + self.has_up = ::Timer::default(); + true + } else { + self.needs_up = env.clock().make_timer(U2F_UP_PROMPT_TIMEOUT_MS); + false + } + } + + /// Returns whether user presence was requested. + /// + /// This function doesn't represent interaction with the environment, and does not change the + /// state, i.e. neither grants nor consumes user presence. + pub fn is_up_needed(&mut self, env: &mut E) -> bool { + !env.clock().is_elapsed(&self.needs_up) + } +} + +#[cfg(feature = "with_ctap1")] +#[cfg(test)] +mod test { + use super::*; + use crate::env::test::TestEnv; + + fn big_positive() -> usize { + 1000000 + } + + fn grant_up_when_needed(env: &mut TestEnv) { + let mut u2f_state = U2fUserPresenceState::new(); + assert!(!u2f_state.consume_up(env)); + assert!(u2f_state.is_up_needed(env)); + u2f_state.grant_up(env); + assert!(u2f_state.consume_up(env)); + assert!(!u2f_state.consume_up(env)); + } + + fn need_up_timeout(env: &mut TestEnv) { + let mut u2f_state = U2fUserPresenceState::new(); + assert!(!u2f_state.consume_up(env)); + assert!(u2f_state.is_up_needed(env)); + env.clock().advance(U2F_UP_PROMPT_TIMEOUT_MS); + // The timeout excludes equality, so it should be over at this instant. + assert!(!u2f_state.is_up_needed(env)); + } + + fn grant_up_timeout(env: &mut TestEnv) { + let mut u2f_state = U2fUserPresenceState::new(); + assert!(!u2f_state.consume_up(env)); + assert!(u2f_state.is_up_needed(env)); + u2f_state.grant_up(env); + env.clock().advance(TOUCH_TIMEOUT_MS); + // The timeout excludes equality, so it should be over at this instant. + assert!(!u2f_state.consume_up(env)); + } + + #[test] + fn test_grant_up_timeout() { + let mut env = TestEnv::new(); + grant_up_timeout(&mut env); + env.clock().advance(big_positive()); + grant_up_timeout(&mut env); + } + + #[test] + fn test_need_up_timeout() { + let mut env = TestEnv::new(); + need_up_timeout(&mut env); + env.clock().advance(big_positive()); + need_up_timeout(&mut env); + } + + #[test] + fn test_grant_up_when_needed() { + let mut env = TestEnv::new(); + grant_up_when_needed(&mut env); + env.clock().advance(big_positive()); + grant_up_when_needed(&mut env); + } + + #[test] + fn test_grant_up_without_need() { + let mut env = TestEnv::new(); + let mut u2f_state = U2fUserPresenceState::new(); + u2f_state.grant_up(&mut env); + assert!(!u2f_state.is_up_needed(&mut env)); + assert!(!u2f_state.consume_up(&mut env)); + } +} diff --git a/src/ctap/vendor_hid.rs b/src/ctap/vendor_hid.rs index 4aa0e295..e81f829a 100644 --- a/src/ctap/vendor_hid.rs +++ b/src/ctap/vendor_hid.rs @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::clock::CtapInstant; use crate::ctap::hid::{ CtapHid, CtapHidCommand, CtapHidError, HidPacket, HidPacketIterator, Message, }; @@ -22,33 +21,32 @@ use crate::env::Env; /// Implements the non-standard command processing for HID. /// /// Outside of the pure HID commands like INIT, only PING and CBOR commands are allowed. -pub struct VendorHid { - hid: CtapHid, +pub struct VendorHid { + hid: CtapHid, } -impl VendorHid { +impl VendorHid { /// Instantiates a HID handler for CTAP1, CTAP2 and Wink. pub fn new() -> Self { - let hid = CtapHid::new(CtapHid::CAPABILITY_CBOR | CtapHid::CAPABILITY_NMSG); + let hid = CtapHid::::new(CtapHid::::CAPABILITY_CBOR | CtapHid::::CAPABILITY_NMSG); VendorHid { hid } } /// Processes an incoming USB HID packet, and returns an iterator for all outgoing packets. pub fn process_hid_packet( &mut self, - env: &mut impl Env, + env: &mut E, packet: &HidPacket, - now: CtapInstant, - ctap_state: &mut CtapState, + ctap_state: &mut CtapState, ) -> HidPacketIterator { - if let Some(message) = self.hid.parse_packet(env, packet, now) { - let processed_message = self.process_message(env, message, now, ctap_state); + if let Some(message) = self.hid.parse_packet(env, packet) { + let processed_message = self.process_message(env, message, ctap_state); debug_ctap!( env, "Sending message through the second usage page: {:02x?}", processed_message ); - CtapHid::split_message(processed_message) + CtapHid::::split_message(processed_message) } else { HidPacketIterator::none() } @@ -57,19 +55,18 @@ impl VendorHid { /// Processes a message's commands that affect the protocol outside HID. pub fn process_message( &mut self, - env: &mut impl Env, + env: &mut E, message: Message, - now: CtapInstant, - ctap_state: &mut CtapState, + ctap_state: &mut CtapState, ) -> Message { let cid = message.cid; match message.cmd { // There are no custom CTAP1 commands. - CtapHidCommand::Msg => CtapHid::error_message(cid, CtapHidError::InvalidCmd), + CtapHidCommand::Msg => CtapHid::::error_message(cid, CtapHidError::InvalidCmd), // The CTAP2 processing function multiplexes internally. CtapHidCommand::Cbor => { let response = - ctap_state.process_command(env, &message.payload, Channel::VendorHid(cid), now); + ctap_state.process_command(env, &message.payload, Channel::VendorHid(cid)); Message { cid, cmd: CtapHidCommand::Cbor, @@ -77,7 +74,7 @@ impl VendorHid { } } // Call Wink over the main HID. - CtapHidCommand::Wink => CtapHid::error_message(cid, CtapHidError::InvalidCmd), + CtapHidCommand::Wink => CtapHid::::error_message(cid, CtapHidError::InvalidCmd), // All other commands have already been processed, keep them as is. _ => message, } @@ -90,27 +87,22 @@ mod test { use crate::ctap::hid::ChannelID; use crate::env::test::TestEnv; - fn new_initialized() -> (VendorHid, ChannelID) { + fn new_initialized() -> (VendorHid, ChannelID) { let (hid, cid) = CtapHid::new_initialized(); - (VendorHid { hid }, cid) + (VendorHid:: { hid }, cid) } #[test] fn test_process_hid_packet() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let (mut vendor_hid, cid) = new_initialized(); let mut ping_packet = [0x00; 64]; ping_packet[..4].copy_from_slice(&cid); ping_packet[4..9].copy_from_slice(&[0x81, 0x00, 0x02, 0x99, 0x99]); - let mut response = vendor_hid.process_hid_packet( - &mut env, - &ping_packet, - CtapInstant::new(0), - &mut ctap_state, - ); + let mut response = vendor_hid.process_hid_packet(&mut env, &ping_packet, &mut ctap_state); assert_eq!(response.next(), Some(ping_packet)); assert_eq!(response.next(), None); } @@ -118,26 +110,21 @@ mod test { #[test] fn test_process_hid_packet_empty() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let (mut vendor_hid, cid) = new_initialized(); let mut cancel_packet = [0x00; 64]; cancel_packet[..4].copy_from_slice(&cid); cancel_packet[4..7].copy_from_slice(&[0x91, 0x00, 0x00]); - let mut response = vendor_hid.process_hid_packet( - &mut env, - &cancel_packet, - CtapInstant::new(0), - &mut ctap_state, - ); + let mut response = vendor_hid.process_hid_packet(&mut env, &cancel_packet, &mut ctap_state); assert_eq!(response.next(), None); } #[test] fn test_blocked_commands() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env); let (mut vendor_hid, cid) = new_initialized(); // Usually longer, but we don't parse them anyway. @@ -153,21 +140,11 @@ mod test { error_packet[..4].copy_from_slice(&cid); error_packet[4..8].copy_from_slice(&[0xBF, 0x00, 0x01, 0x01]); - let mut response = vendor_hid.process_hid_packet( - &mut env, - &msg_packet, - CtapInstant::new(0), - &mut ctap_state, - ); + let mut response = vendor_hid.process_hid_packet(&mut env, &msg_packet, &mut ctap_state); assert_eq!(response.next(), Some(error_packet)); assert_eq!(response.next(), None); - let mut response = vendor_hid.process_hid_packet( - &mut env, - &wink_packet, - CtapInstant::new(0), - &mut ctap_state, - ); + let mut response = vendor_hid.process_hid_packet(&mut env, &wink_packet, &mut ctap_state); assert_eq!(response.next(), Some(error_packet)); assert_eq!(response.next(), None); } diff --git a/src/env/mod.rs b/src/env/mod.rs index eb1ad5c7..ba3e8145 100644 --- a/src/env/mod.rs +++ b/src/env/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2022-2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ // limitations under the License. use crate::api::attestation_store::AttestationStore; +use crate::api::clock::Clock; use crate::api::connection::HidConnection; use crate::api::customization::Customization; use crate::api::firmware_protection::FirmwareProtection; @@ -38,12 +39,14 @@ pub trait Env { type Customization: Customization; type HidConnection: HidConnection; type AttestationStore: AttestationStore; + type Clock: Clock; fn rng(&mut self) -> &mut Self::Rng; fn user_presence(&mut self) -> &mut Self::UserPresence; fn store(&mut self) -> &mut Store; fn key_store(&mut self) -> &mut Self::KeyStore; fn attestation_store(&mut self) -> &mut Self::AttestationStore; + fn clock(&mut self) -> &mut Self::Clock; /// Returns the upgrade storage instance. /// diff --git a/src/env/test/mod.rs b/src/env/test/mod.rs index 163b5e69..24e3fa67 100644 --- a/src/env/test/mod.rs +++ b/src/env/test/mod.rs @@ -14,15 +14,14 @@ use self::upgrade_storage::BufferUpgradeStorage; use crate::api::attestation_store::AttestationStore; +use crate::api::clock::Clock; use crate::api::connection::{HidConnection, SendOrRecvResult, SendOrRecvStatus}; use crate::api::customization::DEFAULT_CUSTOMIZATION; use crate::api::firmware_protection::FirmwareProtection; use crate::api::user_presence::{UserPresence, UserPresenceResult}; use crate::api::{attestation_store, key_store}; -use crate::clock::ClockInt; use crate::env::Env; use customization::TestCustomization; -use embedded_time::duration::Milliseconds; use persistent_store::{BufferOptions, BufferStorage, Store}; use rand::rngs::StdRng; use rand::{Rng, SeedableRng}; @@ -37,6 +36,7 @@ pub struct TestEnv { store: Store, upgrade_storage: Option, customization: TestCustomization, + clock: TestClock, } pub struct TestRng256 { @@ -57,6 +57,43 @@ impl Rng256 for TestRng256 { } } +#[derive(Debug, Default, PartialEq)] +pub struct TestTimer { + end_ms: usize, +} + +#[derive(Debug, Default)] +pub struct TestClock { + /// The current time, as advanced, in milliseconds. + now_ms: usize, +} + +impl TestClock { + pub fn advance(&mut self, milliseconds: usize) { + self.now_ms += milliseconds; + } +} + +impl Clock for TestClock { + type Timer = TestTimer; + + fn make_timer(&mut self, milliseconds: usize) -> Self::Timer { + TestTimer { + end_ms: self.now_ms + milliseconds, + } + } + + fn is_elapsed(&mut self, timer: &Self::Timer) -> bool { + self.now_ms >= timer.end_ms + } + + #[cfg(feature = "debug_ctap")] + fn timestamp_us(&mut self) -> usize { + // Unused, but let's implement something because it's easy. + self.now_ms * 1000 + } +} + pub struct TestUserPresence { check: Box UserPresenceResult>, } @@ -85,11 +122,7 @@ fn new_storage() -> BufferStorage { } impl HidConnection for TestEnv { - fn send_and_maybe_recv( - &mut self, - _buf: &mut [u8; 64], - _timeout: Milliseconds, - ) -> SendOrRecvResult { + fn send_and_maybe_recv(&mut self, _buf: &mut [u8; 64], _timeout_ms: usize) -> SendOrRecvResult { // TODO: Implement I/O from canned requests/responses for integration testing. Ok(SendOrRecvStatus::Sent) } @@ -107,12 +140,14 @@ impl TestEnv { let store = Store::new(storage).ok().unwrap(); let upgrade_storage = Some(BufferUpgradeStorage::new().unwrap()); let customization = DEFAULT_CUSTOMIZATION.into(); + let clock = TestClock::default(); TestEnv { rng, user_presence, store, upgrade_storage, customization, + clock, } } @@ -137,7 +172,7 @@ impl TestUserPresence { impl UserPresence for TestUserPresence { fn check_init(&mut self) {} - fn wait_with_timeout(&mut self, _timeout: Milliseconds) -> UserPresenceResult { + fn wait_with_timeout(&mut self, _timeout_ms: usize) -> UserPresenceResult { (self.check)() } fn check_complete(&mut self) {} @@ -174,6 +209,7 @@ impl Env for TestEnv { type Storage = BufferStorage; type KeyStore = Self; type AttestationStore = Self; + type Clock = TestClock; type UpgradeStorage = BufferUpgradeStorage; type FirmwareProtection = Self; type Write = TestWrite; @@ -200,6 +236,10 @@ impl Env for TestEnv { self } + fn clock(&mut self) -> &mut Self::Clock { + &mut self.clock + } + fn upgrade_storage(&mut self) -> Option<&mut Self::UpgradeStorage> { self.upgrade_storage.as_mut() } @@ -225,3 +265,20 @@ impl Env for TestEnv { self } } + +#[cfg(test)] +#[allow(clippy::module_inception)] +mod test { + use super::*; + + #[test] + fn test_clock() { + let mut clock = TestClock::default(); + let timer = clock.make_timer(3); + assert!(!clock.is_elapsed(&timer)); + clock.advance(2); + assert!(!clock.is_elapsed(&timer)); + clock.advance(1); + assert!(clock.is_elapsed(&timer)); + } +} diff --git a/src/env/tock/clock.rs b/src/env/tock/clock.rs new file mode 100644 index 00000000..8ca01e71 --- /dev/null +++ b/src/env/tock/clock.rs @@ -0,0 +1,118 @@ +// Copyright 2022-2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::api::clock::Clock; +use libtock_drivers::timer::{get_clock_frequency, get_ticks}; + +/// 56-bits timestamp (valid for 70k+ years) +#[derive(Clone, Copy, Debug, Default, PartialOrd, Ord, PartialEq, Eq)] +struct Timestamp { + epoch: usize, // 32-bits + tick: usize, // 24-bits (32kHz) +} + +impl Timestamp { + /// Adds (potentially more than 24 bit of) ticks to this timestamp. + pub fn add_ticks(&mut self, ticks: usize) { + // Saturating should never happen, but it fails gracefully. + let sum = self.tick.saturating_add(ticks); + self.epoch += sum >> 24; + self.tick = sum & 0xff_ffff; + } +} + +#[derive(Default)] +pub struct TockTimer { + deadline: Timestamp, +} + +/// Clock that produces timers through Tock syscalls. +/// +/// To guarantee correctness, you have to call any of its functions at least once per full tick +/// counter wrap. In our case, 24 bit ticks with a 32 kHz frequency wrap after 512 seconds. If you +/// can't guarantee to regularly create or check timers, call tickle at least every 8 minutes. +#[derive(Default)] +pub struct TockClock { + now: Timestamp, +} + +impl TockClock { + /// Elapses timers before the clock wraps. + /// + /// Call this regularly to timeout reliably despite wrapping clock ticks. + pub fn tickle(&mut self) { + let cur_tick = get_ticks().ok().unwrap(); + if cur_tick < self.now.tick { + self.now.epoch += 1; + } + self.now.tick = cur_tick; + } +} + +impl Clock for TockClock { + type Timer = TockTimer; + + fn make_timer(&mut self, milliseconds: usize) -> Self::Timer { + self.tickle(); + let clock_frequency = get_clock_frequency().ok().unwrap(); + let delta_tick = match milliseconds.checked_mul(clock_frequency) { + Some(x) => x / 1000, + // All CTAP timeouts are multiples of 100 so far. Worst case we timeout too early. + None => (milliseconds / 100).saturating_mul(clock_frequency / 10), + }; + let mut deadline = self.now; + deadline.add_ticks(delta_tick); + Self::Timer { deadline } + } + + fn is_elapsed(&mut self, timer: &Self::Timer) -> bool { + self.tickle(); + self.now >= timer.deadline + } + + #[cfg(feature = "debug_ctap")] + fn timestamp_us(&mut self) -> usize { + let clock_frequency = get_clock_frequency().ok().unwrap(); + let total_ticks = 0x100_0000u64 * self.now.epoch as u64 + self.now.tick as u64; + (total_ticks.wrapping_mul(1_000_000u64) / clock_frequency as u64) as usize + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_timestamp_add_ticks() { + let mut timestamp = Timestamp::default(); + timestamp.add_ticks(1); + let expected = Timestamp { epoch: 0, tick: 1 }; + assert_eq!(timestamp, expected); + timestamp.add_ticks(0xff_ffff); + let expected = Timestamp { epoch: 1, tick: 0 }; + assert_eq!(timestamp, expected); + timestamp.add_ticks(0x100_0000); + let expected = Timestamp { epoch: 2, tick: 0 }; + assert_eq!(timestamp, expected); + timestamp.add_ticks(0x1ff_ffff); + let expected = Timestamp { + epoch: 3, + tick: 0xff_ffff, + }; + assert_eq!(timestamp, expected); + timestamp.add_ticks(1); + let expected = Timestamp { epoch: 4, tick: 0 }; + assert_eq!(timestamp, expected); + } +} diff --git a/src/env/tock/mod.rs b/src/env/tock/mod.rs index ca050274..006f708f 100644 --- a/src/env/tock/mod.rs +++ b/src/env/tock/mod.rs @@ -19,12 +19,10 @@ use crate::api::customization::{CustomizationImpl, DEFAULT_CUSTOMIZATION}; use crate::api::firmware_protection::FirmwareProtection; use crate::api::user_presence::{UserPresence, UserPresenceError, UserPresenceResult}; use crate::api::{attestation_store, key_store}; -use crate::clock::{ClockInt, KEEPALIVE_DELAY_MS}; use crate::env::Env; +use clock::TockClock; use core::cell::Cell; use core::sync::atomic::{AtomicBool, Ordering}; -use embedded_time::duration::Milliseconds; -use embedded_time::fixed_point::FixedPoint; use libtock_core::result::{CommandError, EALREADY}; use libtock_drivers::buttons::{self, ButtonState}; use libtock_drivers::console::Console; @@ -35,6 +33,7 @@ use libtock_drivers::{crp, led, timer}; use persistent_store::{StorageResult, Store}; use rng256::TockRng256; +mod clock; mod storage; pub struct TockHidConnection { @@ -42,14 +41,10 @@ pub struct TockHidConnection { } impl HidConnection for TockHidConnection { - fn send_and_maybe_recv( - &mut self, - buf: &mut [u8; 64], - timeout: Milliseconds, - ) -> SendOrRecvResult { + fn send_and_maybe_recv(&mut self, buf: &mut [u8; 64], timeout_ms: usize) -> SendOrRecvResult { match usb_ctap_hid::send_or_recv_with_timeout( buf, - timer::Duration::from_ms(timeout.integer() as isize), + Duration::from_ms(timeout_ms as isize), self.endpoint, ) { Ok(usb_ctap_hid::SendOrRecvStatus::Timeout) => Ok(SendOrRecvStatus::Timeout), @@ -70,6 +65,7 @@ pub struct TockEnv { #[cfg(feature = "vendor_hid")] vendor_connection: TockHidConnection, blink_pattern: usize, + clock: TockClock, } impl TockEnv { @@ -95,6 +91,7 @@ impl TockEnv { endpoint: UsbEndpoint::VendorHid, }, blink_pattern: 0, + clock: TockClock::default(), } } } @@ -115,8 +112,9 @@ impl UserPresence for TockEnv { fn check_init(&mut self) { self.blink_pattern = 0; } - fn wait_with_timeout(&mut self, timeout: Milliseconds) -> UserPresenceResult { - if timeout.integer() == 0 { + + fn wait_with_timeout(&mut self, timeout_ms: usize) -> UserPresenceResult { + if timeout_ms == 0 { return Err(UserPresenceError::Timeout); } blink_leds(self.blink_pattern); @@ -141,7 +139,7 @@ impl UserPresence for TockEnv { }); let mut keepalive = keepalive_callback.init().flex_unwrap(); let keepalive_alarm = keepalive - .set_alarm(timer::Duration::from_ms(timeout.integer() as isize)) + .set_alarm(Duration::from_ms(timeout_ms as isize)) .flex_unwrap(); // Wait for a button touch or an alarm. @@ -224,6 +222,7 @@ impl Env for TockEnv { type Storage = TockStorage; type KeyStore = Self; type AttestationStore = Self; + type Clock = TockClock; type UpgradeStorage = TockUpgradeStorage; type FirmwareProtection = Self; type Write = Console; @@ -250,6 +249,10 @@ impl Env for TockEnv { self } + fn clock(&mut self) -> &mut Self::Clock { + &mut self.clock + } + fn upgrade_storage(&mut self) -> Option<&mut Self::UpgradeStorage> { self.upgrade_storage.as_mut() } @@ -324,5 +327,3 @@ pub fn switch_off_leds() { led::get(l).flex_unwrap().off().flex_unwrap(); } } - -pub const KEEPALIVE_DELAY_TOCK: Duration = Duration::from_ms(KEEPALIVE_DELAY_MS as isize); diff --git a/src/lib.rs b/src/lib.rs index 3690b57f..a04183e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,7 +25,6 @@ use crate::ctap::vendor_hid::VendorHid; use crate::ctap::CtapState; pub use crate::ctap::Transport; use crate::env::Env; -use clock::CtapInstant; // 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. @@ -45,7 +44,6 @@ macro_rules! debug_ctap { } pub mod api; -pub mod clock; // TODO(kaczmarczyck): Refactor this so that ctap module isn't public. pub mod ctap; pub mod env; @@ -55,18 +53,18 @@ pub mod test_helpers; /// CTAP implementation parameterized by its environment. pub struct Ctap { env: E, - state: CtapState, - hid: MainHid, + state: CtapState, + hid: MainHid, #[cfg(feature = "vendor_hid")] - vendor_hid: VendorHid, + vendor_hid: VendorHid, } impl Ctap { /// Instantiates a CTAP implementation given its environment. // This should only take the environment, but it temporarily takes the boot time until the // clock is part of the environment. - pub fn new(mut env: E, now: CtapInstant) -> Self { - let state = CtapState::new(&mut env, now); + pub fn new(mut env: E) -> Self { + let state = CtapState::::new(&mut env); let hid = MainHid::new(); #[cfg(feature = "vendor_hid")] let vendor_hid = VendorHid::new(); @@ -79,11 +77,11 @@ impl Ctap { } } - pub fn state(&mut self) -> &mut CtapState { + pub fn state(&mut self) -> &mut CtapState { &mut self.state } - pub fn hid(&mut self) -> &mut MainHid { + pub fn hid(&mut self) -> &mut MainHid { &mut self.hid } @@ -95,23 +93,35 @@ impl Ctap { &mut self, packet: &HidPacket, transport: Transport, - now: CtapInstant, ) -> HidPacketIterator { match transport { Transport::MainHid => { self.hid - .process_hid_packet(&mut self.env, packet, now, &mut self.state) + .process_hid_packet(&mut self.env, packet, &mut self.state) } #[cfg(feature = "vendor_hid")] Transport::VendorHid => { self.vendor_hid - .process_hid_packet(&mut self.env, packet, now, &mut self.state) + .process_hid_packet(&mut self.env, packet, &mut self.state) } } } - pub fn update_timeouts(&mut self, now: CtapInstant) { - self.state.update_timeouts(now); - self.hid.update_wink_timeout(now); + pub fn update_timeouts(&mut self) { + self.state.update_timeouts(&mut self.env); + } + + pub fn should_wink(&mut self) -> bool { + self.hid.should_wink(&mut self.env) + } + + #[cfg(feature = "with_ctap1")] + pub fn u2f_grant_user_presence(&mut self) { + self.state.u2f_grant_user_presence(&mut self.env) + } + + #[cfg(feature = "with_ctap1")] + pub fn u2f_needs_user_presence(&mut self) -> bool { + self.state.u2f_needs_user_presence(&mut self.env) } } diff --git a/src/main.rs b/src/main.rs index 6688627f..8862ccbd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,22 +24,16 @@ extern crate lang_items; #[cfg(feature = "with_ctap1")] use core::cell::Cell; #[cfg(feature = "debug_ctap")] -use core::convert::TryFrom; -use core::convert::TryInto; -#[cfg(feature = "debug_ctap")] use core::fmt::Write; +use ctap2::api::clock::Clock; use ctap2::api::connection::{HidConnection, SendOrRecvStatus}; -#[cfg(feature = "debug_ctap")] -use ctap2::clock::CtapClock; -use ctap2::clock::{new_clock, Clock, ClockInt, KEEPALIVE_DELAY, KEEPALIVE_DELAY_MS}; use ctap2::ctap::hid::HidPacketIterator; +use ctap2::ctap::KEEPALIVE_DELAY_MS; #[cfg(feature = "with_ctap1")] use ctap2::env::tock::blink_leds; use ctap2::env::tock::{switch_off_leds, wink_leds, TockEnv}; +use ctap2::env::Env; use ctap2::Transport; -#[cfg(feature = "debug_ctap")] -use embedded_time::duration::Microseconds; -use embedded_time::duration::Milliseconds; #[cfg(feature = "with_ctap1")] use libtock_drivers::buttons::{self, ButtonState}; #[cfg(feature = "debug_ctap")] @@ -51,8 +45,8 @@ use usb_ctap_hid::UsbEndpoint; libtock_core::stack_size! {0x4000} -const SEND_TIMEOUT: Milliseconds = Milliseconds(1000); -const KEEPALIVE_DELAY_TOCK: Duration = Duration::from_ms(KEEPALIVE_DELAY_MS as isize); +const SEND_TIMEOUT_MS: usize = 1000; +const KEEPALIVE_DELAY_MS_TOCK: Duration = Duration::from_ms(KEEPALIVE_DELAY_MS as isize); #[cfg(not(feature = "vendor_hid"))] const NUM_ENDPOINTS: usize = 1; @@ -113,20 +107,18 @@ impl EndpointReplies { None } } -fn main() { - let clock = new_clock(); +fn main() { // Setup USB driver. if !usb_ctap_hid::setup() { panic!("Cannot setup USB driver"); } - let boot_time = clock.try_now().unwrap(); let env = TockEnv::new(); - let mut ctap = ctap2::Ctap::new(env, boot_time); + let mut ctap = ctap2::Ctap::new(env); let mut led_counter = 0; - let mut last_led_increment = boot_time; + let mut led_blink_timer = <::Clock as Clock>::Timer::default(); let mut replies = EndpointReplies::new(); @@ -159,21 +151,27 @@ fn main() { if let Some(mut packet) = replies.next_packet() { // send and receive. let hid_connection = packet.transport.hid_connection(ctap.env()); - match hid_connection.send_and_maybe_recv(&mut packet.packet, SEND_TIMEOUT) { + match hid_connection.send_and_maybe_recv(&mut packet.packet, SEND_TIMEOUT_MS) { Ok(SendOrRecvStatus::Timeout) => { #[cfg(feature = "debug_ctap")] - print_packet_notice("Sending packet timed out", &clock); + print_packet_notice( + "Sending packet timed out", + ctap.env().clock().timestamp_us(), + ); // TODO: reset the ctap_hid state. // Since sending the packet timed out, we cancel this reply. break; } Ok(SendOrRecvStatus::Sent) => { #[cfg(feature = "debug_ctap")] - print_packet_notice("Sent packet", &clock); + print_packet_notice("Sent packet", ctap.env().clock().timestamp_us()); } Ok(SendOrRecvStatus::Received(ep)) => { #[cfg(feature = "debug_ctap")] - print_packet_notice("Received another packet", &clock); + print_packet_notice( + "Received another packet", + ctap.env().clock().timestamp_us(), + ); usb_endpoint = Some(ep); // Copy to incoming packet to local buffer to be consistent @@ -185,12 +183,12 @@ fn main() { } else { // receive usb_endpoint = - 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_MS_TOCK) .flex_unwrap() { usb_ctap_hid::SendOrRecvStatus::Received(endpoint) => { #[cfg(feature = "debug_ctap")] - print_packet_notice("Received packet", &clock); + print_packet_notice("Received packet", ctap.env().clock().timestamp_us()); Some(endpoint) } usb_ctap_hid::SendOrRecvStatus::Sent => { @@ -200,11 +198,10 @@ fn main() { }; } - let now = clock.try_now().unwrap(); #[cfg(feature = "with_ctap1")] { if button_touched.get() { - ctap.state().u2f_grant_user_presence(now); + ctap.u2f_grant_user_presence(); } // Cleanup button callbacks. We miss button presses while processing though. // Heavy computation mostly follows a registered touch luckily. Unregistering @@ -218,7 +215,8 @@ fn main() { // These calls are making sure that even for long inactivity, wrapping clock values // don't cause problems with timers. - ctap.update_timeouts(now); + ctap.update_timeouts(); + ctap.env().clock().tickle(); if let Some(endpoint) = usb_endpoint { let transport = match endpoint { @@ -226,7 +224,7 @@ fn main() { #[cfg(feature = "vendor_hid")] UsbEndpoint::VendorHid => Transport::VendorHid, }; - let reply = ctap.process_hid_packet(&pkt_request, transport, now); + let reply = ctap.process_hid_packet(&pkt_request, transport); if reply.has_data() { // Update endpoint with the reply. for ep in replies.replies.iter_mut() { @@ -247,28 +245,20 @@ fn main() { } } - let now = clock.try_now().unwrap(); - if let Some(wait_duration) = now.checked_duration_since(&last_led_increment) { - let wait_duration: Milliseconds = wait_duration.try_into().unwrap(); - if wait_duration > KEEPALIVE_DELAY { - // Loops quickly when waiting for U2F user presence, so the next LED blink - // state is only set if enough time has elapsed. - led_counter += 1; - last_led_increment = now; - } - } else { - // This branch means the clock frequency changed. This should never happen. + if ctap.env().clock().is_elapsed(&led_blink_timer) { + // Loops quickly when waiting for U2F user presence, so the next LED blink + // state is only set if enough time has elapsed. led_counter += 1; - last_led_increment = now; + led_blink_timer = ctap.env().clock().make_timer(KEEPALIVE_DELAY_MS) } - if ctap.hid().should_wink(now) { + if ctap.should_wink() { wink_leds(led_counter); } else { #[cfg(not(feature = "with_ctap1"))] switch_off_leds(); #[cfg(feature = "with_ctap1")] - if ctap.state().u2f_needs_user_presence(now) { + if ctap.u2f_needs_user_presence() { // Flash the LEDs with an almost regular pattern. The inaccuracy comes from // delay caused by processing and sending of packets. blink_leds(led_counter); @@ -280,11 +270,7 @@ fn main() { } #[cfg(feature = "debug_ctap")] -fn print_packet_notice(notice_text: &str, clock: &CtapClock) { - let now = clock.try_now().unwrap(); - let now_us = Microseconds::::try_from(now.duration_since_epoch()) - .unwrap() - .0; +fn print_packet_notice(notice_text: &str, now_us: usize) { writeln!( Console::new(), "{} at {}.{:06} s", diff --git a/src/test_helpers/mod.rs b/src/test_helpers/mod.rs index 968e25a2..6420a106 100644 --- a/src/test_helpers/mod.rs +++ b/src/test_helpers/mod.rs @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::clock::CtapInstant; use crate::ctap::command::{ AuthenticatorAttestationMaterial, AuthenticatorConfigParameters, AuthenticatorVendorConfigureParameters, Command, @@ -28,9 +27,9 @@ const DUMMY_CHANNEL: Channel = Channel::MainHid([0x12, 0x34, 0x56, 0x78]); #[cfg(feature = "vendor_hid")] const VENDOR_CHANNEL: Channel = Channel::VendorHid([0x12, 0x34, 0x56, 0x78]); -pub fn enable_enterprise_attestation( - state: &mut CtapState, - env: &mut impl Env, +pub fn enable_enterprise_attestation( + state: &mut CtapState, + env: &mut E, ) -> Result { let dummy_key = [0x41; key_material::ATTESTATION_PRIVATE_KEY_LENGTH]; let dummy_cert = vec![0xdd; 20]; @@ -47,7 +46,7 @@ pub fn enable_enterprise_attestation( #[cfg(not(feature = "vendor_hid"))] let vendor_channel = DUMMY_CHANNEL; let vendor_command = Command::AuthenticatorVendorConfigure(configure_params); - state.process_parsed_command(env, vendor_command, vendor_channel, CtapInstant::new(0))?; + state.process_parsed_command(env, vendor_command, vendor_channel)?; let config_params = AuthenticatorConfigParameters { sub_command: ConfigSubCommand::EnableEnterpriseAttestation, @@ -56,7 +55,7 @@ pub fn enable_enterprise_attestation( pin_uv_auth_protocol: None, }; let config_command = Command::AuthenticatorConfig(config_params); - state.process_parsed_command(env, config_command, DUMMY_CHANNEL, CtapInstant::new(0))?; + state.process_parsed_command(env, config_command, DUMMY_CHANNEL)?; Ok(attestation_material) } diff --git a/third_party/libtock-drivers/src/timer.rs b/third_party/libtock-drivers/src/timer.rs index f3faadd5..9ab53360 100644 --- a/third_party/libtock-drivers/src/timer.rs +++ b/third_party/libtock-drivers/src/timer.rs @@ -41,6 +41,24 @@ pub fn sleep(duration: Duration) -> TockResult<()> { } } +pub fn get_ticks() -> TockResult { + Ok(syscalls::command( + DRIVER_NUMBER, + command_nr::GET_CLOCK_VALUE, + 0, + 0, + )?) +} + +pub fn get_clock_frequency() -> TockResult { + Ok(syscalls::command( + DRIVER_NUMBER, + command_nr::GET_CLOCK_FREQUENCY, + 0, + 0, + )?) +} + pub fn with_callback(callback: CB) -> WithCallback<'static, CB> { WithCallback { callback,