From da9814b56d1749e65c32c6eb8785fad45178cb9c Mon Sep 17 00:00:00 2001 From: Fabian Kaczmarczyck Date: Fri, 24 Feb 2023 10:45:28 +0100 Subject: [PATCH] use new Clock --- Cargo.lock | 73 ------ Cargo.toml | 1 - fuzz/Cargo.lock | 1 - fuzz/fuzz_helper/src/lib.rs | 31 +-- src/api/connection.rs | 8 +- src/api/user_presence.rs | 5 +- src/clock.rs | 170 -------------- src/ctap/client_pin.rs | 65 +++--- src/ctap/credential_management.rs | 38 +-- src/ctap/ctap1.rs | 140 +++++------ src/ctap/hid/mod.rs | 32 +-- src/ctap/hid/receive.rs | 153 +++--------- src/ctap/main_hid.rs | 77 +++--- src/ctap/mod.rs | 373 +++++++++++------------------- src/ctap/timed_permission.rs | 199 ---------------- src/ctap/token_state.rs | 48 ++-- src/ctap/u2f_up.rs | 141 +++++++++++ src/ctap/vendor_hid.rs | 43 +--- src/env/test/mod.rs | 10 +- src/env/tock/mod.rs | 19 +- src/lib.rs | 32 ++- src/main.rs | 72 +++--- src/test_helpers/mod.rs | 5 +- 23 files changed, 541 insertions(+), 1195 deletions(-) delete mode 100644 src/clock.rs delete mode 100644 src/ctap/timed_permission.rs create mode 100644 src/ctap/u2f_up.rs 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 35a3aaaa..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/connection.rs b/src/api/connection.rs index 3970b040..d0bbcb6a 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: usize) -> SendOrRecvResult; } diff --git a/src/api/user_presence.rs b/src/api/user_presence.rs index 989945c6..42296d78 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: 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 47c142db..4e580112 100644 --- a/src/ctap/client_pin.rs +++ b/src/ctap/client_pin.rs @@ -1,5 +1,4 @@ // Copyright 2020-2021 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 @@ -12,7 +11,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, @@ -291,7 +289,6 @@ impl ClientPin { &mut self, env: &mut E, client_pin_params: AuthenticatorClientPinParameters, - now: CtapInstant, ) -> Result { let AuthenticatorClientPinParameters { pin_uv_auth_protocol, @@ -324,7 +321,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(), @@ -359,7 +356,6 @@ impl ClientPin { &mut self, 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. @@ -375,7 +371,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); @@ -388,7 +384,6 @@ impl ClientPin { &mut self, 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 @@ -409,7 +404,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)?, @@ -419,7 +414,6 @@ impl ClientPin { self.process_get_pin_uv_auth_token_using_pin_with_permissions( env, client_pin_params, - now, )?, ), }; @@ -500,9 +494,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. @@ -573,7 +567,7 @@ impl ClientPin { }; 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)); + 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), @@ -589,7 +583,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) { @@ -826,7 +819,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)) ); @@ -838,7 +831,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)) ); } @@ -866,7 +859,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)) ); } @@ -890,7 +883,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) ); } @@ -900,7 +893,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)) ); } @@ -933,14 +926,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) ); @@ -948,7 +941,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) ); } @@ -979,7 +972,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)) => { @@ -1015,7 +1008,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) ); } @@ -1040,7 +1033,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), ); } @@ -1073,7 +1066,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)) => { @@ -1109,21 +1102,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) ); } @@ -1150,7 +1143,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) ); } @@ -1503,7 +1496,7 @@ mod test { 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) @@ -1634,9 +1627,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 @@ -1652,8 +1643,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 @@ -1680,9 +1671,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/credential_management.rs b/src/ctap/credential_management.rs index 07f906ec..8c192a75 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::{ @@ -139,13 +138,12 @@ 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 @@ -177,7 +175,6 @@ fn process_enumerate_credentials_begin( 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, ); @@ -259,7 +256,6 @@ pub fn process_credential_management( 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 => { @@ -403,7 +397,7 @@ mod test { ); 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(); @@ -426,7 +420,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)) => { @@ -452,7 +445,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)) => { @@ -491,7 +483,7 @@ mod test { 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(); @@ -515,7 +507,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)) => { @@ -540,7 +531,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)) => { @@ -566,7 +556,6 @@ mod test { &mut ctap_state.client_pin, cred_management_params, DUMMY_CHANNEL, - CtapInstant::new(0), ); assert_eq!( cred_management_response, @@ -587,7 +576,7 @@ mod test { ); 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; @@ -620,7 +609,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)) => { @@ -651,7 +639,6 @@ mod test { &mut ctap_state.client_pin, cred_management_params, DUMMY_CHANNEL, - CtapInstant::new(0), ); assert_eq!( cred_management_response, @@ -677,7 +664,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(); @@ -708,7 +695,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)) => { @@ -732,7 +718,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)) => { @@ -757,7 +742,6 @@ mod test { &mut ctap_state.client_pin, cred_management_params, DUMMY_CHANNEL, - CtapInstant::new(0), ); assert_eq!( cred_management_response, @@ -779,7 +763,7 @@ mod test { 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(); @@ -812,7 +796,6 @@ mod test { &mut ctap_state.client_pin, cred_management_params, DUMMY_CHANNEL, - CtapInstant::new(0), ); assert_eq!( cred_management_response, @@ -831,7 +814,6 @@ mod test { &mut ctap_state.client_pin, cred_management_params, DUMMY_CHANNEL, - CtapInstant::new(0), ); assert_eq!( cred_management_response, @@ -853,7 +835,7 @@ mod test { 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(); @@ -892,7 +874,6 @@ mod test { &mut ctap_state.client_pin, cred_management_params, DUMMY_CHANNEL, - CtapInstant::new(0), ); assert_eq!( cred_management_response, @@ -914,7 +895,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(); @@ -930,7 +911,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 63d925c9..fcdc9361 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; @@ -178,7 +177,6 @@ impl Ctap1Command { env: &mut E, message: &[u8], ctap_state: &mut CtapState, - clock_value: CtapInstant, ) -> 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); } @@ -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; 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); + 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); + 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); + 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 4dedb35b..92b69179 100644 --- a/src/ctap/hid/mod.rs +++ b/src/ctap/hid/mod.rs @@ -21,7 +21,6 @@ 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; @@ -29,7 +28,6 @@ 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; @@ -197,9 +195,6 @@ impl CtapHid { #[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. @@ -230,13 +225,8 @@ impl CtapHid { /// Ignoring the others is incorrect behavior. You have to at least replace them with an error /// message: /// `Self::error_message(message.cid, CtapHidError::InvalidCmd)` - pub fn parse_packet( - &mut self, - env: &mut E, - packet: &HidPacket, - clock_value: CtapInstant, - ) -> Option { - match self.assembler.parse_packet(env, packet, clock_value) { + 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) @@ -432,7 +422,7 @@ mod test { let mut messages = Vec::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]), @@ -450,10 +440,7 @@ mod test { 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] @@ -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, @@ -525,7 +509,7 @@ mod test { 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, @@ -543,7 +527,7 @@ mod test { 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); } diff --git a/src/ctap/hid/receive.rs b/src/ctap/hid/receive.rs index 93c2d19b..24d88a1b 100644 --- a/src/ctap/hid/receive.rs +++ b/src/ctap/hid/receive.rs @@ -15,22 +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::marker::PhantomData; use core::mem::swap; +// TODO: Is this timeout duration specified? +const TIMEOUT_DURATION: usize = 100; + /// A structure to assemble CTAPHID commands from a series of incoming USB HID packets. pub struct MessageAssembler { - _phantom: PhantomData, // 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: Option<<::Clock as Clock>::Timer>, // Current command. cmd: u8, // Sequence number expected for the next packet. @@ -44,10 +45,9 @@ pub struct MessageAssembler { impl MessageAssembler { pub fn new() -> MessageAssembler { MessageAssembler { - _phantom: PhantomData, idle: true, cid: [0, 0, 0, 0], - last_timestamp: CtapInstant::new(0), + timer: None, cmd: 0, seq: 0, remaining_payload_len: 0, @@ -60,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 = None; self.cmd = 0; self.seq = 0; self.remaining_payload_len = 0; @@ -72,19 +72,17 @@ 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 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); - if !self.idle && timestamp >= self.last_timestamp + CtapHid::::TIMEOUT_DURATION { + env.clock().update_timer(&mut self.timer); + if !self.idle && self.timer.is_none() { // The current channel timed out. // Save the channel ID and reset the state. let current_cid = self.cid; @@ -101,7 +99,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 @@ -123,7 +121,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)) } @@ -135,7 +133,7 @@ impl MessageAssembler { Err((cid, CtapHidError::InvalidSeq)) } else { // Update the last timestamp. - self.last_timestamp = timestamp; + self.timer = Some(env.clock().make_timer(TIMEOUT_DURATION)); // Increment the sequence number for the next packet. self.seq += 1; Ok(self.append_payload(data)) @@ -147,12 +145,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? @@ -160,7 +157,7 @@ impl MessageAssembler { return Err((cid, CtapHidError::InvalidLen)); } self.cid = cid; - self.last_timestamp = timestamp; + self.timer = Some(env.clock().make_timer(TIMEOUT_DURATION)); self.cmd = cmd; self.seq = 0; self.remaining_payload_len = len; @@ -190,9 +187,7 @@ impl MessageAssembler { #[cfg(test)] mod test { - use crate::ctap::hid::CtapHid; use crate::env::test::TestEnv; - use embedded_time::duration::Milliseconds; use super::*; @@ -215,14 +210,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, @@ -239,7 +228,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], @@ -260,7 +248,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], @@ -278,16 +265,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, @@ -304,24 +286,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, @@ -338,26 +311,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, @@ -383,7 +347,6 @@ mod test { &[0x12, 0x34, 0x56, 0x78, 0x80 | cmd as u8, 0x00, 0x80], byte ), - CtapInstant::new(0) ), Ok(None) ); @@ -391,7 +354,6 @@ mod test { assembler.parse_packet( &mut env, &byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x00], byte), - CtapInstant::new(0) ), Ok(None) ); @@ -399,7 +361,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], @@ -425,24 +386,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, @@ -460,7 +414,6 @@ mod test { assembler.parse_packet( &mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]), - CtapInstant::new(0) ), Ok(None) ); @@ -473,7 +426,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)) ); @@ -481,11 +433,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, @@ -508,7 +456,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], @@ -520,11 +467,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 @@ -541,16 +484,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)) ); } @@ -563,16 +501,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)) ); } @@ -585,16 +518,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); 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)) ); } @@ -602,37 +531,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 - 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, @@ -650,7 +569,6 @@ mod test { assembler.parse_packet( &mut env, &byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x02, 0x00], 0x51), - CtapInstant::new(0) ), Ok(None) ); @@ -662,7 +580,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/main_hid.rs b/src/ctap/main_hid.rs index 0abc8523..f99c05da 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,19 +20,18 @@ 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: usize = 5000; /// Implements the standard CTAP command processing for HID. pub struct MainHid { hid: CtapHid, - wink_permission: TimedPermission, + wink_permission: Option<<::Clock as Clock>::Timer>, } impl MainHid { - const WINK_TIMEOUT_DURATION: Milliseconds = Milliseconds(5000 as ClockInt); - /// Instantiates a HID handler for CTAP1, CTAP2 and Wink. pub fn new() -> Self { #[cfg(feature = "with_ctap1")] @@ -43,7 +42,7 @@ impl MainHid { | CtapHid::::CAPABILITY_NMSG; let hid = CtapHid::::new(capabilities); - let wink_permission = TimedPermission::waiting(); + let wink_permission = None; MainHid { hid, wink_permission, @@ -55,11 +54,10 @@ impl MainHid { &mut self, env: &mut E, packet: &HidPacket, - now: CtapInstant, 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) } else { @@ -72,11 +70,10 @@ impl MainHid { &mut self, env: &mut E, message: Message, - now: CtapInstant, ctap_state: &mut CtapState, ) -> Message { // If another command arrives, stop winking to prevent accidential button touches. - self.wink_permission = TimedPermission::waiting(); + self.wink_permission = None; let cid = message.cid; match message.cmd { @@ -87,7 +84,7 @@ impl MainHid { return CtapHid::::error_message(cid, CtapHidError::InvalidCmd); #[cfg(feature = "with_ctap1")] - match ctap1::Ctap1Command::process_command(env, &message.payload, ctap_state, now) { + 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), } @@ -98,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, @@ -108,8 +105,7 @@ 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 = Some(env.clock().make_timer(WINK_TIMEOUT_DURATION)); // The response is empty like the request. message } else { @@ -122,13 +118,9 @@ 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(&mut self, env: &mut E) -> bool { + env.clock().update_timer(&mut self.wink_permission); + self.wink_permission.is_some() } #[cfg(feature = "with_ctap1")] @@ -162,11 +154,10 @@ mod test { fn new_initialized() -> (MainHid, ChannelID) { let (hid, cid) = CtapHid::new_initialized(); - let wink_permission = TimedPermission::waiting(); ( MainHid:: { hid, - wink_permission, + wink_permission: None, }, cid, ) @@ -175,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); } @@ -195,44 +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); + assert!(!main_hid.should_wink(&mut env)); } } diff --git a/src/ctap/mod.rs b/src/ctap/mod.rs index 7d00e753..4c26a1ae 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}; @@ -77,11 +77,9 @@ use alloc::vec; use alloc::vec::Vec; use byteorder::{BigEndian, ByteOrder}; use core::convert::TryFrom; -use core::marker::PhantomData; 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; @@ -100,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: usize = 100; +pub const TOUCH_TIMEOUT: 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: usize = 10000; +const STATEFUL_COMMAND_TIMEOUT_DURATION: usize = 30000; pub const FIDO2_VERSION_STRING: &str = "FIDO_2_0"; #[cfg(feature = "with_ctap1")] @@ -213,7 +208,7 @@ fn truncate_to_char_boundary(s: &str, mut max: usize) -> &str { fn send_keepalive_up_needed( env: &mut E, channel: Channel, - timeout: Milliseconds, + timeout: usize, ) -> Result<(), UserPresenceError> { let (cid, transport) = match channel { Channel::MainHid(cid) => (cid, Transport::MainHid), @@ -293,7 +288,7 @@ fn check_user_presence(env: &mut E, channel: Channel) -> Result<(), Ctap 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 / KEEPALIVE_DELAY; // All fallible functions are called without '?' operator to always reach // check_complete(...) cleanup function. @@ -302,11 +297,9 @@ fn check_user_presence(env: &mut E, channel: Channel) -> Result<(), Ctap 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 }); if !matches!(result, Err(UserPresenceError::Timeout)) { break; } @@ -367,8 +360,7 @@ 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 { - _phantom: PhantomData, - permission: TimedPermission, + permission: Option<<::Clock as Clock>::Timer>, command_type: Option, channel: Option, } @@ -378,10 +370,9 @@ impl StatefulPermission { /// /// 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 { - _phantom: PhantomData, - permission: TimedPermission::granted(now, RESET_TIMEOUT_DURATION), + permission: Some(env.clock().make_timer(RESET_TIMEOUT_DURATION)), command_type: Some(StatefulCommand::Reset), channel: None, } @@ -389,7 +380,7 @@ impl StatefulPermission { /// Clears all permissions and state. pub fn clear(&mut self) { - self.permission = TimedPermission::waiting(); + self.permission = None; self.command_type = None; self.channel = None; } @@ -414,8 +405,9 @@ 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) { + env.clock().update_timer(&mut self.permission); + if self.permission.is_none() { self.clear(); } } @@ -430,7 +422,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, ) { @@ -438,7 +430,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 = Some(env.clock().make_timer(STATEFUL_COMMAND_TIMEOUT_DURATION)); self.command_type = Some(new_command_type); self.channel = Some(channel); } @@ -492,28 +484,28 @@ impl StatefulPermission { 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, large_blobs: LargeBlobs, } impl CtapState { - pub fn new(env: &mut E, now: CtapInstant) -> Self { + pub fn new(env: &mut E) -> Self { storage::init(env).ok().unwrap(); 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( @@ -540,12 +532,10 @@ impl CtapState { 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) => { @@ -570,18 +560,17 @@ impl CtapState { 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)) @@ -603,7 +592,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), } @@ -614,20 +603,17 @@ impl CtapState { 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, @@ -635,7 +621,6 @@ impl CtapState { &mut self.client_pin, params, channel, - now, ), Command::AuthenticatorSelection => self.process_selection(env, channel), Command::AuthenticatorLargeBlobs(params) => { @@ -1094,7 +1079,6 @@ impl CtapState { env: &mut E, get_assertion_params: AuthenticatorGetAssertionParameters, channel: Channel, - now: CtapInstant, ) -> Result { let AuthenticatorGetAssertionParameters { rp_id, @@ -1215,7 +1199,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( @@ -1324,7 +1308,7 @@ impl CtapState { { // 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) } @@ -1441,13 +1425,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) } } @@ -1525,9 +1509,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![ @@ -1581,7 +1564,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) => { @@ -1671,7 +1654,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 = @@ -1689,7 +1672,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; @@ -1708,7 +1691,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![]; @@ -1725,7 +1708,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 = @@ -1757,7 +1740,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 = @@ -1808,7 +1791,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 = @@ -1851,7 +1834,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, @@ -1878,7 +1861,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, @@ -1904,7 +1887,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 { @@ -1953,7 +1936,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]), @@ -1985,7 +1968,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]), @@ -2017,7 +2000,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), @@ -2056,7 +2039,7 @@ mod test { 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 +2088,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 +2108,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 +2123,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 +2164,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 +2211,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 +2244,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 +2271,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 +2366,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 +2385,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 +2447,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 +2474,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 +2503,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 +2525,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 +2554,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 +2591,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 +2610,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 +2643,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 +2654,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 +2684,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 +2716,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 +2729,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 +2763,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 +2781,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 +2828,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 +2848,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 +2882,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() @@ -2969,7 +2906,7 @@ mod test { 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 { @@ -3016,12 +2953,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, @@ -3062,7 +2995,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]; @@ -3101,12 +3034,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, @@ -3131,7 +3060,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!( @@ -3162,12 +3091,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. @@ -3183,7 +3108,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!( @@ -3196,7 +3121,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 { @@ -3216,8 +3141,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); @@ -3227,7 +3151,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); @@ -3240,10 +3164,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)); @@ -3252,15 +3176,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); } @@ -3268,11 +3188,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); } @@ -3280,7 +3199,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); @@ -3297,7 +3216,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( @@ -3405,7 +3324,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]; @@ -3484,7 +3403,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); @@ -3502,7 +3421,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); @@ -3519,7 +3438,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 { @@ -3546,21 +3465,20 @@ mod test { &mut env, Command::AuthenticatorGetAssertion(get_assertion_params), DUMMY_CHANNEL, - CtapInstant::new(0), ); + env.clock().advance(STATEFUL_COMMAND_TIMEOUT_DURATION - 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, @@ -3571,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); + let response = + ctap_state.process_parsed_command(&mut env, Command::AuthenticatorReset, DUMMY_CHANNEL); assert_eq!(response, Err(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED)); } @@ -3610,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 { @@ -3635,7 +3550,6 @@ mod test { &mut env, Command::AuthenticatorCredentialManagement(cred_management_params), DUMMY_CHANNEL, - CtapInstant::new(0), ); assert!(matches!( response, @@ -3648,11 +3562,11 @@ mod test { pin_uv_auth_protocol: None, pin_uv_auth_param: None, }; + env.clock().advance(STATEFUL_COMMAND_TIMEOUT_DURATION - 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, @@ -3665,11 +3579,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)); } @@ -3701,7 +3615,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. @@ -3729,7 +3643,6 @@ mod test { &mut env, Command::AuthenticatorGetAssertion(get_assertion_params), DUMMY_CHANNEL, - CtapInstant::new(0), ); assert!(matches!( get_assertion_response, @@ -3739,7 +3652,6 @@ mod test { &mut env, Command::AuthenticatorGetNextAssertion, DUMMY_CHANNEL, - CtapInstant::new(0), ); assert!(get_next_assertion_response.is_ok()); assert!(matches!( @@ -3750,7 +3662,6 @@ mod test { &mut env, Command::AuthenticatorGetNextAssertion, NEW_CHANNEL, - CtapInstant::new(0), ); assert_eq!( get_next_assertion_response, @@ -3760,7 +3671,6 @@ mod test { &mut env, Command::AuthenticatorGetNextAssertion, DUMMY_CHANNEL, - CtapInstant::new(0), ); assert_eq!( get_next_assertion_response, @@ -3771,13 +3681,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, @@ -3789,7 +3698,6 @@ mod test { &mut env, Command::AuthenticatorGetInfo, VENDOR_CHANNEL, - CtapInstant::new(0), ); assert!(matches!( response, @@ -3802,12 +3710,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)); } @@ -3816,13 +3723,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, @@ -3832,7 +3738,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 c3f6837f..2cade02c 100644 --- a/src/ctap/token_state.rs +++ b/src/ctap/token_state.rs @@ -12,24 +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 core::marker::PhantomData; 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: usize = 30000; /// Implements pinUvAuthToken state from section 6.5.2.1. /// @@ -38,11 +34,10 @@ const INITIAL_USAGE_TIME_LIMIT: Milliseconds = Milliseconds(30000 as C /// /// This implementation does not use a rolling timer. pub struct PinUvAuthTokenState { - _phantom: PhantomData, // 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: Option<<::Clock as Clock>::Timer>, user_verified: bool, in_use: bool, } @@ -51,10 +46,9 @@ impl PinUvAuthTokenState { /// Creates a pinUvAuthToken state without permissions. pub fn new() -> Self { PinUvAuthTokenState { - _phantom: PhantomData, permissions_set: 0, permissions_rp_id: None, - usage_timer: TimedPermission::waiting(), + usage_timer: None, user_verified: false, in_use: false, } @@ -117,19 +111,19 @@ 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 = Some(env.clock().make_timer(INITIAL_USAGE_TIME_LIMIT)); 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) { + env.clock().update_timer(&mut self.usage_timer); + if self.usage_timer.is_none() { self.stop_using_pin_uv_auth_token(); } } @@ -153,7 +147,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 = None; self.user_verified = false; self.in_use = false; } @@ -167,23 +161,23 @@ mod test { #[test] fn test_observer() { + let mut env = TestEnv::new(); let mut token_state = PinUvAuthTokenState::::new(); - let mut 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.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); + token_state.pin_uv_auth_token_usage_timer_observer(&mut env); assert!(!token_state.is_in_use()); } #[test] fn test_stop() { + let mut env = TestEnv::new(); let mut token_state = PinUvAuthTokenState::::new(); - 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.is_in_use()); token_state.stop_using_pin_uv_auth_token(); assert!(!token_state.is_in_use()); @@ -267,14 +261,14 @@ mod test { #[test] fn test_user_verified_flag() { + 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..0dde2b14 --- /dev/null +++ b/src/ctap/u2f_up.rs @@ -0,0 +1,141 @@ +// 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; +use crate::api::clock::Clock; +use crate::env::Env; + +const U2F_UP_PROMPT_TIMEOUT: usize = 10000; + +pub struct U2fUserPresenceState { + /// If user presence was recently requested, its timeout is saved here. + needs_up: Option<<::Clock as Clock>::Timer>, + + /// Button touch timeouts, while user presence is requested, are saved here. + has_up: Option<<::Clock as Clock>::Timer>, +} + +impl U2fUserPresenceState { + pub fn new() -> U2fUserPresenceState { + U2fUserPresenceState { + needs_up: None, + has_up: None, + } + } + + // Granting user presence is ignored if it needs activation, but waits. Also cleans up. + pub fn grant_up(&mut self, env: &mut E) { + self.check_expiration(env); + if self.needs_up.is_some() { + self.needs_up = None; + self.has_up = Some(env.clock().make_timer(TOUCH_TIMEOUT)); + } + } + + // This marks user presence as needed or uses it up if already granted. Also cleans up. + pub fn consume_up(&mut self, env: &mut E) -> bool { + self.check_expiration(env); + if self.has_up.is_some() { + self.has_up = None; + true + } else { + self.needs_up = Some(env.clock().make_timer(U2F_UP_PROMPT_TIMEOUT)); + false + } + } + + // Returns if user presence was requested. Also cleans up. + pub fn is_up_needed(&mut self, env: &mut E) -> bool { + self.check_expiration(env); + self.needs_up.is_some() + } + + /// Checks and updates all timers. + pub fn check_expiration(&mut self, env: &mut E) { + env.clock().update_timer(&mut self.needs_up); + env.clock().update_timer(&mut self.has_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); + // 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); + // 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 316e7133..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, }; @@ -38,11 +37,10 @@ impl VendorHid { &mut self, env: &mut E, packet: &HidPacket, - now: CtapInstant, 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?}", @@ -59,7 +57,6 @@ impl VendorHid { &mut self, env: &mut E, message: Message, - now: CtapInstant, ctap_state: &mut CtapState, ) -> Message { let cid = message.cid; @@ -69,7 +66,7 @@ impl VendorHid { // 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, @@ -98,19 +95,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 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/test/mod.rs b/src/env/test/mod.rs index f4dd3c45..575e357e 100644 --- a/src/env/test/mod.rs +++ b/src/env/test/mod.rs @@ -20,10 +20,8 @@ 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}; @@ -133,11 +131,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: usize) -> SendOrRecvResult { // TODO: Implement I/O from canned requests/responses for integration testing. Ok(SendOrRecvStatus::Sent) } @@ -187,7 +181,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: usize) -> UserPresenceResult { (self.check)() } fn check_complete(&mut self) {} diff --git a/src/env/tock/mod.rs b/src/env/tock/mod.rs index ad5e53a5..30a99bf8 100644 --- a/src/env/tock/mod.rs +++ b/src/env/tock/mod.rs @@ -19,13 +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; @@ -44,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: usize) -> SendOrRecvResult { match usb_ctap_hid::send_or_recv_with_timeout( buf, - timer::Duration::from_ms(timeout.integer() as isize), + Duration::from_ms(timeout as isize), self.endpoint, ) { Ok(usb_ctap_hid::SendOrRecvStatus::Timeout) => Ok(SendOrRecvStatus::Timeout), @@ -120,8 +113,8 @@ impl UserPresence for TockEnv { self.blink_pattern = 0; } - fn wait_with_timeout(&mut self, timeout: Milliseconds) -> UserPresenceResult { - if timeout.integer() == 0 { + fn wait_with_timeout(&mut self, timeout: usize) -> UserPresenceResult { + if timeout == 0 { return Err(UserPresenceError::Timeout); } blink_leds(self.blink_pattern); @@ -146,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 as isize)) .flex_unwrap(); // Wait for a button touch or an alarm. @@ -334,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 5380c733..a75cebd2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,6 +18,7 @@ extern crate alloc; #[macro_use] extern crate arrayref; +use crate::api::clock::Clock; use crate::ctap::hid::{HidPacket, HidPacketIterator}; use crate::ctap::main_hid::MainHid; #[cfg(feature = "vendor_hid")] @@ -25,7 +26,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 +45,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; @@ -65,8 +64,8 @@ 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(); @@ -95,23 +94,36 @@ 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.env.clock().tickle(); + 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..2d2b6fa3 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; #[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: usize = 1000; +const KEEPALIVE_DELAY_TOCK: Duration = Duration::from_ms(KEEPALIVE_DELAY 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 = None; let mut replies = EndpointReplies::new(); @@ -162,18 +154,24 @@ fn main() { match hid_connection.send_and_maybe_recv(&mut packet.packet, SEND_TIMEOUT) { 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 @@ -190,7 +188,7 @@ fn main() { { 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,7 @@ 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(); if let Some(endpoint) = usb_endpoint { let transport = match endpoint { @@ -226,7 +223,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 +244,21 @@ 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. + ctap.env().clock().update_timer(&mut led_blink_timer); + if led_blink_timer.is_none() { + // 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 = Some(ctap.env().clock().make_timer(KEEPALIVE_DELAY)) } - 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 a9e85be4..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, @@ -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) }