From 4cd759e69f6184d725fade54df6e89f16bf934c7 Mon Sep 17 00:00:00 2001 From: Fabian Kaczmarczyck Date: Fri, 24 Jun 2022 10:10:07 +0200 Subject: [PATCH 1/8] adds generic Env parameters --- fuzz/fuzz_helper/src/lib.rs | 2 +- src/ctap/client_pin.rs | 89 ++++++------- src/ctap/config_command.rs | 70 +++++++--- src/ctap/credential_management.rs | 123 +++++++++++------- src/ctap/ctap1.rs | 12 +- src/ctap/hid/mod.rs | 108 ++++++++-------- src/ctap/hid/receive.rs | 19 +-- src/ctap/hid/send.rs | 6 +- src/ctap/large_blobs.rs | 62 ++++++--- src/ctap/main_hid.rs | 47 +++---- src/ctap/mod.rs | 204 ++++++++++++++++-------------- src/ctap/token_state.rs | 23 ++-- src/ctap/vendor_hid.rs | 32 ++--- src/lib.rs | 12 +- src/test_helpers/mod.rs | 6 +- 15 files changed, 463 insertions(+), 352 deletions(-) diff --git a/fuzz/fuzz_helper/src/lib.rs b/fuzz/fuzz_helper/src/lib.rs index 2d389f1d..35a3aaaa 100644 --- a/fuzz/fuzz_helper/src/lib.rs +++ b/fuzz/fuzz_helper/src/lib.rs @@ -179,7 +179,7 @@ fn setup_customization( fn setup_state( unstructured: &mut Unstructured, - state: &mut CtapState, + state: &mut CtapState::, env: &mut TestEnv, ) -> FuzzResult<()> { if bool::arbitrary(unstructured)? { diff --git a/src/ctap/client_pin.rs b/src/ctap/client_pin.rs index 12d5b642..47c142db 100644 --- a/src/ctap/client_pin.rs +++ b/src/ctap/client_pin.rs @@ -33,7 +33,6 @@ use crypto::sha256::Sha256; use crypto::Hash256; #[cfg(test)] use enum_iterator::IntoEnumIterator; -use rng256::Rng256; use subtle::ConstantTimeEq; /// The prefix length of the PIN hash that is stored and compared. @@ -106,20 +105,20 @@ pub enum PinPermission { AuthenticatorConfiguration = 0x20, } -pub struct ClientPin { +pub struct ClientPin { pin_protocol_v1: PinProtocol, pin_protocol_v2: PinProtocol, consecutive_pin_mismatches: u8, - pin_uv_auth_token_state: PinUvAuthTokenState, + pin_uv_auth_token_state: PinUvAuthTokenState, } -impl ClientPin { - pub fn new(rng: &mut impl Rng256) -> ClientPin { +impl ClientPin { + pub fn new(env: &mut E) -> Self { ClientPin { - pin_protocol_v1: PinProtocol::new(rng), - pin_protocol_v2: PinProtocol::new(rng), + pin_protocol_v1: PinProtocol::new(env.rng()), + pin_protocol_v2: PinProtocol::new(env.rng()), consecutive_pin_mismatches: 0, - pin_uv_auth_token_state: PinUvAuthTokenState::new(), + pin_uv_auth_token_state: PinUvAuthTokenState::::new(), } } @@ -159,7 +158,7 @@ impl ClientPin { /// Also, in case of failure, the key agreement key is randomly reset. fn verify_pin_hash_enc( &mut self, - env: &mut impl Env, + env: &mut E, pin_uv_auth_protocol: PinUvAuthProtocol, shared_secret: &dyn SharedSecret, pin_hash_enc: Vec, @@ -197,7 +196,7 @@ impl ClientPin { fn process_get_pin_retries( &self, - env: &mut impl Env, + env: &mut E, ) -> Result { Ok(AuthenticatorClientPinResponse { key_agreement: None, @@ -225,7 +224,7 @@ impl ClientPin { fn process_set_pin( &mut self, - env: &mut impl Env, + env: &mut E, client_pin_params: AuthenticatorClientPinParameters, ) -> Result<(), Ctap2StatusCode> { let AuthenticatorClientPinParameters { @@ -252,7 +251,7 @@ impl ClientPin { fn process_change_pin( &mut self, - env: &mut impl Env, + env: &mut E, client_pin_params: AuthenticatorClientPinParameters, ) -> Result<(), Ctap2StatusCode> { let AuthenticatorClientPinParameters { @@ -290,7 +289,7 @@ impl ClientPin { fn process_get_pin_token( &mut self, - env: &mut impl Env, + env: &mut E, client_pin_params: AuthenticatorClientPinParameters, now: CtapInstant, ) -> Result { @@ -358,7 +357,7 @@ impl ClientPin { fn process_get_pin_uv_auth_token_using_pin_with_permissions( &mut self, - env: &mut impl Env, + env: &mut E, mut client_pin_params: AuthenticatorClientPinParameters, now: CtapInstant, ) -> Result { @@ -387,7 +386,7 @@ impl ClientPin { /// Processes the authenticatorClientPin command. pub fn process_command( &mut self, - env: &mut impl Env, + env: &mut E, client_pin_params: AuthenticatorClientPinParameters, now: CtapInstant, ) -> Result { @@ -447,11 +446,11 @@ impl ClientPin { } /// Resets all held state. - pub fn reset(&mut self, rng: &mut impl Rng256) { - self.pin_protocol_v1.regenerate(rng); - self.pin_protocol_v1.reset_pin_uv_auth_token(rng); - self.pin_protocol_v2.regenerate(rng); - self.pin_protocol_v2.reset_pin_uv_auth_token(rng); + pub fn reset(&mut self, env: &mut E) { + self.pin_protocol_v1.regenerate(env.rng()); + self.pin_protocol_v1.reset_pin_uv_auth_token(env.rng()); + self.pin_protocol_v2.regenerate(env.rng()); + self.pin_protocol_v2.reset_pin_uv_auth_token(env.rng()); self.consecutive_pin_mismatches = 0; self.pin_uv_auth_token_state.stop_using_pin_uv_auth_token(); } @@ -466,7 +465,7 @@ impl ClientPin { /// 32 byte. pub fn process_hmac_secret( &self, - rng: &mut impl Rng256, + env: &mut E, hmac_secret_input: GetAssertionHmacSecretInput, cred_random: &[u8; 32], ) -> Result, Ctap2StatusCode> { @@ -490,7 +489,7 @@ impl ClientPin { let mut output2 = hmac_256::(cred_random, &decrypted_salts[32..]).to_vec(); output.append(&mut output2); } - shared_secret.encrypt(rng, &output) + shared_secret.encrypt(env.rng(), &output) } /// Consumes flags and permissions related to the pinUvAuthToken. @@ -563,19 +562,19 @@ impl ClientPin { #[cfg(test)] pub fn new_test( + env: &mut E, key_agreement_key: crypto::ecdh::SecKey, pin_uv_auth_token: [u8; PIN_TOKEN_LENGTH], pin_uv_auth_protocol: PinUvAuthProtocol, - ) -> ClientPin { - let mut env = crate::env::test::TestEnv::new(); + ) -> Self { let (key_agreement_key_v1, key_agreement_key_v2) = match pin_uv_auth_protocol { PinUvAuthProtocol::V1 => (key_agreement_key, crypto::ecdh::SecKey::gensk(env.rng())), PinUvAuthProtocol::V2 => (crypto::ecdh::SecKey::gensk(env.rng()), key_agreement_key), }; - let mut pin_uv_auth_token_state = PinUvAuthTokenState::new(); + let mut pin_uv_auth_token_state = PinUvAuthTokenState::::new(); pin_uv_auth_token_state.set_permissions(0xFF); pin_uv_auth_token_state.begin_using_pin_uv_auth_token(CtapInstant::new(0)); - ClientPin { + Self { pin_protocol_v1: PinProtocol::new_test(key_agreement_key_v1, pin_uv_auth_token), pin_protocol_v2: PinProtocol::new_test(key_agreement_key_v2, pin_uv_auth_token), consecutive_pin_mismatches: 0, @@ -618,14 +617,18 @@ mod test { /// should fail. fn create_client_pin_and_shared_secret( pin_uv_auth_protocol: PinUvAuthProtocol, - ) -> (ClientPin, Box) { + ) -> (ClientPin, Box) { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pk = key_agreement_key.genpk(); let key_agreement = CoseKey::from(pk); let pin_uv_auth_token = [0x91; PIN_TOKEN_LENGTH]; - let client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol); + let client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + pin_uv_auth_protocol, + ); let shared_secret = client_pin .get_pin_protocol(pin_uv_auth_protocol) .decapsulate(key_agreement, pin_uv_auth_protocol) @@ -639,7 +642,7 @@ mod test { fn create_client_pin_and_parameters( pin_uv_auth_protocol: PinUvAuthProtocol, sub_command: ClientPinSubCommand, - ) -> (ClientPin, AuthenticatorClientPinParameters) { + ) -> (ClientPin, AuthenticatorClientPinParameters) { let mut env = TestEnv::new(); let (client_pin, shared_secret) = create_client_pin_and_shared_secret(pin_uv_auth_protocol); @@ -683,7 +686,7 @@ mod test { #[test] fn test_mix_pin_protocols() { let mut env = TestEnv::new(); - let client_pin = ClientPin::new(env.rng()); + let client_pin = ClientPin::::new(&mut env); let pin_protocol_v1 = client_pin.get_pin_protocol(PinUvAuthProtocol::V1); let pin_protocol_v2 = client_pin.get_pin_protocol(PinUvAuthProtocol::V2); let message = vec![0xAA; 16]; @@ -724,7 +727,7 @@ mod test { fn test_helper_verify_pin_hash_enc(pin_uv_auth_protocol: PinUvAuthProtocol) { let mut env = TestEnv::new(); - let mut client_pin = ClientPin::new(env.rng()); + let mut client_pin = ClientPin::::new(&mut env); let pin_protocol = client_pin.get_pin_protocol(pin_uv_auth_protocol); let shared_secret = pin_protocol .decapsulate(pin_protocol.get_public_key(), pin_uv_auth_protocol) @@ -1281,7 +1284,7 @@ mod test { salt_auth, pin_uv_auth_protocol, }; - let output = client_pin.process_hmac_secret(env.rng(), hmac_secret_input, cred_random); + let output = client_pin.process_hmac_secret(&mut env, hmac_secret_input, cred_random); output.map(|v| shared_secret.as_ref().decrypt(&v).unwrap()) } @@ -1301,7 +1304,7 @@ mod test { salt_auth, pin_uv_auth_protocol, }; - let output = client_pin.process_hmac_secret(env.rng(), hmac_secret_input, &cred_random); + let output = client_pin.process_hmac_secret(&mut env, hmac_secret_input, &cred_random); assert_eq!(output, Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)); } @@ -1403,7 +1406,7 @@ mod test { #[test] fn test_has_permission() { let mut env = TestEnv::new(); - let mut client_pin = ClientPin::new(env.rng()); + let mut client_pin = ClientPin::::new(&mut env); client_pin.pin_uv_auth_token_state.set_permissions(0x7F); for permission in PinPermission::into_enum_iter() { assert_eq!( @@ -1427,7 +1430,7 @@ mod test { #[test] fn test_has_no_rp_id_permission() { let mut env = TestEnv::new(); - let mut client_pin = ClientPin::new(env.rng()); + let mut client_pin = ClientPin::::new(&mut env); assert_eq!(client_pin.has_no_rp_id_permission(), Ok(())); client_pin .pin_uv_auth_token_state @@ -1441,7 +1444,7 @@ mod test { #[test] fn test_has_no_or_rp_id_permission() { let mut env = TestEnv::new(); - let mut client_pin = ClientPin::new(env.rng()); + let mut client_pin = ClientPin::::new(&mut env); assert_eq!(client_pin.has_no_or_rp_id_permission("example.com"), Ok(())); client_pin .pin_uv_auth_token_state @@ -1456,7 +1459,7 @@ mod test { #[test] fn test_has_no_or_rp_id_hash_permission() { let mut env = TestEnv::new(); - let mut client_pin = ClientPin::new(env.rng()); + let mut client_pin = ClientPin::::new(&mut env); let rp_id_hash = Sha256::hash(b"example.com"); assert_eq!( client_pin.has_no_or_rp_id_hash_permission(&rp_id_hash), @@ -1478,7 +1481,7 @@ mod test { #[test] fn test_ensure_rp_id_permission() { let mut env = TestEnv::new(); - let mut client_pin = ClientPin::new(env.rng()); + let mut client_pin = ClientPin::::new(&mut env); assert_eq!(client_pin.ensure_rp_id_permission("example.com"), Ok(())); assert_eq!( client_pin @@ -1496,7 +1499,7 @@ mod test { #[test] fn test_verify_pin_uv_auth_token() { let mut env = TestEnv::new(); - let mut client_pin = ClientPin::new(env.rng()); + let mut client_pin = ClientPin::::new(&mut env); let message = [0xAA]; client_pin .pin_uv_auth_token_state @@ -1570,7 +1573,7 @@ mod test { #[test] fn test_verify_pin_uv_auth_token_not_in_use() { let mut env = TestEnv::new(); - let client_pin = ClientPin::new(env.rng()); + let client_pin = ClientPin::::new(&mut env); let message = [0xAA]; let pin_uv_auth_token_v1 = client_pin @@ -1592,7 +1595,7 @@ mod test { #[test] fn test_reset() { let mut env = TestEnv::new(); - let mut client_pin = ClientPin::new(env.rng()); + let mut client_pin = ClientPin::::new(&mut env); let public_key_v1 = client_pin.pin_protocol_v1.get_public_key(); let public_key_v2 = client_pin.pin_protocol_v2.get_public_key(); let token_v1 = *client_pin.pin_protocol_v1.get_pin_uv_auth_token(); @@ -1601,7 +1604,7 @@ mod test { client_pin .pin_uv_auth_token_state .set_permissions_rp_id(Some(String::from("example.com"))); - client_pin.reset(env.rng()); + client_pin.reset(&mut env); assert_ne!(public_key_v1, client_pin.pin_protocol_v1.get_public_key()); assert_ne!(public_key_v2, client_pin.pin_protocol_v2.get_public_key()); assert_ne!( diff --git a/src/ctap/config_command.rs b/src/ctap/config_command.rs index 2fad2719..8deba2b3 100644 --- a/src/ctap/config_command.rs +++ b/src/ctap/config_command.rs @@ -73,9 +73,9 @@ fn process_set_min_pin_length( } /// Processes the AuthenticatorConfig command. -pub fn process_config( - env: &mut impl Env, - client_pin: &mut ClientPin, +pub fn process_config( + env: &mut E, + client_pin: &mut ClientPin, params: AuthenticatorConfigParameters, ) -> Result { let AuthenticatorConfigParameters { @@ -133,8 +133,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let config_params = AuthenticatorConfigParameters { sub_command: ConfigSubCommand::EnableEnterpriseAttestation, @@ -160,8 +164,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let config_params = AuthenticatorConfigParameters { sub_command: ConfigSubCommand::ToggleAlwaysUv, @@ -195,8 +203,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + pin_uv_auth_protocol, + ); storage::set_pin(&mut env, &[0x88; 16], 4).unwrap(); let mut config_data = vec![0xFF; 32]; @@ -265,8 +277,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); // First, increase minimum PIN length from 4 to 6 without PIN auth. let min_pin_length = 6; @@ -309,8 +325,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); // First, set RP IDs without PIN auth. let min_pin_length = 6; @@ -385,8 +405,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); storage::set_pin(&mut env, &[0x88; 16], 4).unwrap(); // Increase min PIN, force PIN change. @@ -408,8 +432,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); storage::set_pin(&mut env, &[0x88; 16], 4).unwrap(); let pin_uv_auth_param = Some(vec![ @@ -439,8 +467,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let config_params = AuthenticatorConfigParameters { sub_command: ConfigSubCommand::VendorPrototype, diff --git a/src/ctap/credential_management.rs b/src/ctap/credential_management.rs index aee9e3af..07f906ec 100644 --- a/src/ctap/credential_management.rs +++ b/src/ctap/credential_management.rs @@ -108,9 +108,9 @@ fn enumerate_credentials_response( /// Check if the token permissions have the correct associated RP ID. /// /// Either no RP ID is associated, or the RP ID matches the stored credential. -fn check_rp_id_permissions( - env: &mut impl Env, - client_pin: &mut ClientPin, +fn check_rp_id_permissions( + env: &mut E, + client_pin: &mut ClientPin, credential_id: &[u8], ) -> Result<(), Ctap2StatusCode> { // Pre-check a sufficient condition before calling the store. @@ -135,9 +135,9 @@ fn process_get_creds_metadata( } /// Processes the subcommand enumerateRPsBegin for CredentialManagement. -fn process_enumerate_rps_begin( - env: &mut impl Env, - stateful_command_permission: &mut StatefulPermission, +fn process_enumerate_rps_begin( + env: &mut E, + stateful_command_permission: &mut StatefulPermission, channel: Channel, now: CtapInstant, ) -> Result { @@ -156,9 +156,9 @@ fn process_enumerate_rps_begin( } /// Processes the subcommand enumerateRPsGetNextRP for CredentialManagement. -fn process_enumerate_rps_get_next_rp( - env: &mut impl Env, - stateful_command_permission: &mut StatefulPermission, +fn process_enumerate_rps_get_next_rp( + env: &mut E, + stateful_command_permission: &mut StatefulPermission, ) -> Result { let rp_id_index = stateful_command_permission.next_enumerate_rp()?; let rp_set = get_stored_rp_ids(env)?; @@ -171,10 +171,10 @@ fn process_enumerate_rps_get_next_rp( } /// Processes the subcommand enumerateCredentialsBegin for CredentialManagement. -fn process_enumerate_credentials_begin( - env: &mut impl Env, - stateful_command_permission: &mut StatefulPermission, - client_pin: &mut ClientPin, +fn process_enumerate_credentials_begin( + env: &mut E, + stateful_command_permission: &mut StatefulPermission, + client_pin: &mut ClientPin, sub_command_params: CredentialManagementSubCommandParameters, channel: Channel, now: CtapInstant, @@ -212,9 +212,9 @@ fn process_enumerate_credentials_begin( } /// Processes the subcommand enumerateCredentialsGetNextCredential for CredentialManagement. -fn process_enumerate_credentials_get_next_credential( - env: &mut impl Env, - stateful_command_permission: &mut StatefulPermission, +fn process_enumerate_credentials_get_next_credential( + env: &mut E, + stateful_command_permission: &mut StatefulPermission, ) -> Result { let credential_key = stateful_command_permission.next_enumerate_credential()?; let credential = storage::get_credential(env, credential_key)?; @@ -222,23 +222,23 @@ fn process_enumerate_credentials_get_next_credential( } /// Processes the subcommand deleteCredential for CredentialManagement. -fn process_delete_credential( - env: &mut impl Env, - client_pin: &mut ClientPin, +fn process_delete_credential( + env: &mut E, + client_pin: &mut ClientPin, sub_command_params: CredentialManagementSubCommandParameters, ) -> Result<(), Ctap2StatusCode> { let credential_id = sub_command_params .credential_id .ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)? .key_id; - check_rp_id_permissions(env, client_pin, &credential_id)?; + check_rp_id_permissions::(env, client_pin, &credential_id)?; storage::delete_credential(env, &credential_id) } /// Processes the subcommand updateUserInformation for CredentialManagement. -fn process_update_user_information( - env: &mut impl Env, - client_pin: &mut ClientPin, +fn process_update_user_information( + env: &mut E, + client_pin: &mut ClientPin, sub_command_params: CredentialManagementSubCommandParameters, ) -> Result<(), Ctap2StatusCode> { let credential_id = sub_command_params @@ -248,15 +248,15 @@ fn process_update_user_information( let user = sub_command_params .user .ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?; - check_rp_id_permissions(env, client_pin, &credential_id)?; + check_rp_id_permissions::(env, client_pin, &credential_id)?; storage::update_credential(env, &credential_id, user) } /// Processes the CredentialManagement command and all its subcommands. -pub fn process_credential_management( - env: &mut impl Env, - stateful_command_permission: &mut StatefulPermission, - client_pin: &mut ClientPin, +pub fn process_credential_management( + env: &mut E, + stateful_command_permission: &mut StatefulPermission, + client_pin: &mut ClientPin, cred_management_params: AuthenticatorCredentialManagementParameters, channel: Channel, now: CtapInstant, @@ -315,7 +315,7 @@ pub fn process_credential_management( } CredentialManagementSubCommand::EnumerateRpsBegin => { client_pin.has_no_rp_id_permission()?; - Some(process_enumerate_rps_begin( + Some(process_enumerate_rps_begin::( env, stateful_command_permission, channel, @@ -323,10 +323,10 @@ pub fn process_credential_management( )?) } CredentialManagementSubCommand::EnumerateRpsGetNextRp => Some( - process_enumerate_rps_get_next_rp(env, stateful_command_permission)?, + process_enumerate_rps_get_next_rp::(env, stateful_command_permission)?, ), CredentialManagementSubCommand::EnumerateCredentialsBegin => { - Some(process_enumerate_credentials_begin( + Some(process_enumerate_credentials_begin::( env, stateful_command_permission, client_pin, @@ -335,11 +335,14 @@ pub fn process_credential_management( now, )?) } - CredentialManagementSubCommand::EnumerateCredentialsGetNextCredential => Some( - process_enumerate_credentials_get_next_credential(env, stateful_command_permission)?, - ), + CredentialManagementSubCommand::EnumerateCredentialsGetNextCredential => { + Some(process_enumerate_credentials_get_next_credential::( + env, + stateful_command_permission, + )?) + } CredentialManagementSubCommand::DeleteCredential => { - process_delete_credential( + process_delete_credential::( env, client_pin, sub_command_params.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?, @@ -347,7 +350,7 @@ pub fn process_credential_management( None } CredentialManagementSubCommand::UpdateUserInformation => { - process_update_user_information( + process_update_user_information::( env, client_pin, sub_command_params.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?, @@ -392,8 +395,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol); + let client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + pin_uv_auth_protocol, + ); let credential_source = create_credential_source(&mut env); let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); @@ -474,8 +481,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let credential_source1 = create_credential_source(&mut env); let mut credential_source2 = create_credential_source(&mut env); credential_source2.rp_id = "another.example.com".to_string(); @@ -568,8 +579,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let credential_source = create_credential_source(&mut env); let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); @@ -649,8 +664,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let credential_source1 = create_credential_source(&mut env); let mut credential_source2 = create_credential_source(&mut env); credential_source2.user_handle = vec![0x02]; @@ -751,8 +770,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let mut credential_source = create_credential_source(&mut env); credential_source.credential_id = vec![0x1D; 32]; @@ -821,8 +844,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let mut credential_source = create_credential_source(&mut env); credential_source.credential_id = vec![0x1D; 32]; diff --git a/src/ctap/ctap1.rs b/src/ctap/ctap1.rs index 5752d05b..63d925c9 100644 --- a/src/ctap/ctap1.rs +++ b/src/ctap/ctap1.rs @@ -174,10 +174,10 @@ impl Ctap1Command { const VENDOR_SPECIFIC_FIRST: u8 = 0x40; const VENDOR_SPECIFIC_LAST: u8 = 0xBF; - pub fn process_command( - env: &mut impl Env, + pub fn process_command( + env: &mut E, message: &[u8], - ctap_state: &mut CtapState, + ctap_state: &mut CtapState, clock_value: CtapInstant, ) -> Result, Ctap1StatusCode> { if !ctap_state @@ -301,13 +301,13 @@ impl Ctap1Command { // +-------------------+---------+--------------+-----------------+ // + application (32B) | UP (1B) | Counter (4B) | challenge (32B) | // +-------------------+---------+--------------+-----------------+ - fn process_authenticate( - env: &mut impl Env, + fn process_authenticate( + env: &mut E, challenge: [u8; 32], application: [u8; 32], key_handle: Vec, flags: Ctap1Flags, - ctap_state: &mut CtapState, + ctap_state: &mut CtapState, ) -> Result, Ctap1StatusCode> { let credential_source = decrypt_credential_id(env, key_handle, &application) .map_err(|_| Ctap1StatusCode::SW_WRONG_DATA)?; diff --git a/src/ctap/hid/mod.rs b/src/ctap/hid/mod.rs index 81dab017..4dedb35b 100644 --- a/src/ctap/hid/mod.rs +++ b/src/ctap/hid/mod.rs @@ -23,6 +23,8 @@ use self::receive::MessageAssembler; pub use self::send::HidPacketIterator; use super::super::clock::{ClockInt, CtapInstant}; use super::status_code::Ctap2StatusCode; +#[cfg(test)] +use crate::env::test::TestEnv; use crate::env::Env; use alloc::vec; use alloc::vec::Vec; @@ -31,6 +33,19 @@ use embedded_time::duration::Milliseconds; #[cfg(test)] use enum_iterator::IntoEnumIterator; +// We implement CTAP 2.1 from 2021-06-15. Please see section +// 11.2. USB Human Interface Device (USB HID) +const CHANNEL_RESERVED: ChannelID = [0, 0, 0, 0]; +const CHANNEL_BROADCAST: ChannelID = [0xFF, 0xFF, 0xFF, 0xFF]; +const PACKET_TYPE_MASK: u8 = 0x80; + +// See section 11.2.9.1.3. CTAPHID_INIT (0x06). +const PROTOCOL_VERSION: u8 = 2; +// The device version number is vendor-defined. +const DEVICE_VERSION_MAJOR: u8 = 1; +const DEVICE_VERSION_MINOR: u8 = 0; +const DEVICE_VERSION_BUILD: u8 = 0; + pub type HidPacket = [u8; 64]; pub type ChannelID = [u8; 4]; @@ -164,8 +179,8 @@ pub enum KeepaliveStatus { /// 2. If you didn't receive any message or preprocessing discarded it, stop. /// 3. Handles all CTAP protocol interactions. /// 4. `split_message` creates packets out of the response message. -pub struct CtapHid { - assembler: MessageAssembler, +pub struct CtapHid { + assembler: MessageAssembler, // The specification only requires unique CIDs, the allocation algorithm is vendor specific. // We allocate them incrementally, that is all `cid` such that 1 <= cid <= allocated_cids are // allocated. @@ -176,21 +191,7 @@ pub struct CtapHid { capabilities: u8, } -impl CtapHid { - // We implement CTAP 2.1 from 2021-06-15. Please see section - // 11.2. USB Human Interface Device (USB HID) - const CHANNEL_RESERVED: ChannelID = [0, 0, 0, 0]; - const CHANNEL_BROADCAST: ChannelID = [0xFF, 0xFF, 0xFF, 0xFF]; - const TYPE_INIT_BIT: u8 = 0x80; - const PACKET_TYPE_MASK: u8 = 0x80; - - // See section 11.2.9.1.3. CTAPHID_INIT (0x06). - const PROTOCOL_VERSION: u8 = 2; - // The device version number is vendor-defined. - const DEVICE_VERSION_MAJOR: u8 = 1; - const DEVICE_VERSION_MINOR: u8 = 0; - const DEVICE_VERSION_BUILD: u8 = 0; - +impl CtapHid { pub const CAPABILITY_WINK: u8 = 0x01; pub const CAPABILITY_CBOR: u8 = 0x04; #[cfg(any(not(feature = "with_ctap1"), feature = "vendor_hid"))] @@ -202,9 +203,9 @@ impl CtapHid { /// Creates a new CTAP HID packet parser. /// /// The capabilities passed in are reported to the client in Init. - pub fn new(capabilities: u8) -> CtapHid { - CtapHid { - assembler: MessageAssembler::new(), + pub fn new(capabilities: u8) -> CtapHid { + Self { + assembler: MessageAssembler::::new(), allocated_cids: 0, capabilities, } @@ -228,10 +229,10 @@ impl CtapHid { /// You may ignore PING, it's behaving correctly by default (input == output). /// Ignoring the others is incorrect behavior. You have to at least replace them with an error /// message: - /// `CtapHid::error_message(message.cid, CtapHid::ERR_INVALID_CMD)` + /// `Self::error_message(message.cid, CtapHidError::InvalidCmd)` pub fn parse_packet( &mut self, - env: &mut impl Env, + env: &mut E, packet: &HidPacket, clock_value: CtapInstant, ) -> Option { @@ -248,9 +249,9 @@ impl CtapHid { if matches!(error, CtapHidError::UnexpectedContinuation) { None } else if !self.is_allocated_channel(cid) { - Some(CtapHid::error_message(cid, CtapHidError::InvalidChannel)) + Some(Self::error_message(cid, CtapHidError::InvalidChannel)) } else { - Some(CtapHid::error_message(cid, error)) + Some(Self::error_message(cid, error)) } } } @@ -267,7 +268,7 @@ impl CtapHid { fn preprocess_message(&mut self, message: Message) -> Option { let cid = message.cid; if !self.has_valid_channel(&message) { - return Some(CtapHid::error_message(cid, CtapHidError::InvalidChannel)); + return Some(Self::error_message(cid, CtapHidError::InvalidChannel)); } match message.cmd { @@ -276,10 +277,10 @@ impl CtapHid { // CTAP 2.1 from 2021-06-15, section 11.2.9.1.3. CtapHidCommand::Init => { if message.payload.len() != 8 { - return Some(CtapHid::error_message(cid, CtapHidError::InvalidLen)); + return Some(Self::error_message(cid, CtapHidError::InvalidLen)); } - let new_cid = if cid == CtapHid::CHANNEL_BROADCAST { + let new_cid = if cid == CHANNEL_BROADCAST { // TODO: Prevent allocating 2^32 channels. self.allocated_cids += 1; (self.allocated_cids as u32).to_be_bytes() @@ -291,10 +292,10 @@ impl CtapHid { let mut payload = vec![0; 17]; payload[..8].copy_from_slice(&message.payload); payload[8..12].copy_from_slice(&new_cid); - payload[12] = CtapHid::PROTOCOL_VERSION; - payload[13] = CtapHid::DEVICE_VERSION_MAJOR; - payload[14] = CtapHid::DEVICE_VERSION_MINOR; - payload[15] = CtapHid::DEVICE_VERSION_BUILD; + payload[12] = PROTOCOL_VERSION; + payload[13] = DEVICE_VERSION_MAJOR; + payload[14] = DEVICE_VERSION_MINOR; + payload[15] = DEVICE_VERSION_BUILD; payload[16] = self.capabilities; Some(Message { @@ -317,7 +318,7 @@ impl CtapHid { CtapHidCommand::Wink => Some(message), _ => { // Unknown or unsupported command. - Some(CtapHid::error_message(cid, CtapHidError::InvalidCmd)) + Some(Self::error_message(cid, CtapHidError::InvalidCmd)) } } } @@ -325,14 +326,14 @@ impl CtapHid { fn has_valid_channel(&self, message: &Message) -> bool { match message.cid { // Only INIT commands use the broadcast channel. - CtapHid::CHANNEL_BROADCAST => message.cmd == CtapHidCommand::Init, + CHANNEL_BROADCAST => message.cmd == CtapHidCommand::Init, // Check that the channel is allocated. _ => self.is_allocated_channel(message.cid), } } fn is_allocated_channel(&self, cid: ChannelID) -> bool { - cid != CtapHid::CHANNEL_RESERVED && u32::from_be_bytes(cid) as usize <= self.allocated_cids + cid != CHANNEL_RESERVED && u32::from_be_bytes(cid) as usize <= self.allocated_cids } pub fn error_message(cid: ChannelID, error_code: CtapHidError) -> Message { @@ -346,8 +347,8 @@ impl CtapHid { /// Helper function to parse a raw packet. pub fn process_single_packet(packet: &HidPacket) -> (ChannelID, ProcessedPacket) { let (&cid, rest) = array_refs![packet, 4, 60]; - if rest[0] & CtapHid::PACKET_TYPE_MASK != 0 { - let cmd = rest[0] & !CtapHid::PACKET_TYPE_MASK; + if rest[0] & PACKET_TYPE_MASK != 0 { + let cmd = rest[0] & !PACKET_TYPE_MASK; let len = (rest[1] as usize) << 8 | (rest[2] as usize); ( cid, @@ -394,7 +395,7 @@ impl CtapHid { /// Generates the HID response packets for a keepalive status. pub fn keepalive(cid: ChannelID, status: KeepaliveStatus) -> HidPacketIterator { - CtapHid::split_message(Message { + Self::split_message(Message { cid, cmd: CtapHidCommand::Keepalive, payload: vec![status as u8], @@ -402,10 +403,10 @@ impl CtapHid { } #[cfg(test)] - pub fn new_initialized() -> (CtapHid, ChannelID) { + pub fn new_initialized() -> (Self, ChannelID) { ( - CtapHid { - assembler: MessageAssembler::new(), + Self { + assembler: MessageAssembler::::new(), allocated_cids: 1, capabilities: 0x0D, }, @@ -417,7 +418,6 @@ impl CtapHid { #[cfg(test)] mod test { use super::*; - use crate::env::test::TestEnv; #[test] fn test_split_assemble() { @@ -430,7 +430,7 @@ mod test { }; let mut messages = Vec::new(); - let mut assembler = MessageAssembler::new(); + let mut assembler = MessageAssembler::::new(); for packet in HidPacketIterator::new(message.clone()).unwrap() { match assembler.parse_packet(&mut env, &packet, CtapInstant::new(0)) { Ok(Some(msg)) => messages.push(msg), @@ -446,7 +446,7 @@ mod test { #[test] fn test_spurious_continuation_packet() { let mut env = TestEnv::new(); - let mut ctap_hid = CtapHid::new(0x0D); + let mut ctap_hid = CtapHid::::new(0x0D); let mut packet = [0x00; 64]; packet[0..7].copy_from_slice(&[0xC1, 0xC1, 0xC1, 0xC1, 0x00, 0x51, 0x51]); // Continuation packets are silently ignored. @@ -458,9 +458,9 @@ mod test { #[test] fn test_command_init() { - let mut ctap_hid = CtapHid::new(0x0D); + let mut ctap_hid = CtapHid::::new(0x0D); let init_message = Message { - cid: CtapHid::CHANNEL_BROADCAST, + cid: CHANNEL_BROADCAST, cmd: CtapHidCommand::Init, payload: vec![0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0], }; @@ -468,7 +468,7 @@ mod test { assert_eq!( reply, Some(Message { - cid: CtapHid::CHANNEL_BROADCAST, + cid: CHANNEL_BROADCAST, cmd: CtapHidCommand::Init, payload: vec![ 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, // Nonce @@ -484,7 +484,7 @@ mod test { #[test] fn test_command_init_for_sync() { let mut env = TestEnv::new(); - let (mut ctap_hid, cid) = CtapHid::new_initialized(); + let (mut ctap_hid, cid) = CtapHid::::new_initialized(); // Ping packet with a length longer than one packet. let mut packet1 = [0x51; 64]; @@ -519,7 +519,7 @@ mod test { #[test] fn test_command_ping() { let mut env = TestEnv::new(); - let (mut ctap_hid, cid) = CtapHid::new_initialized(); + let (mut ctap_hid, cid) = CtapHid::::new_initialized(); let mut ping_packet = [0x00; 64]; ping_packet[..4].copy_from_slice(&cid); @@ -537,7 +537,7 @@ mod test { #[test] fn test_command_cancel() { let mut env = TestEnv::new(); - let (mut ctap_hid, cid) = CtapHid::new_initialized(); + let (mut ctap_hid, cid) = CtapHid::::new_initialized(); let mut cancel_packet = [0x00; 64]; cancel_packet[..4].copy_from_slice(&cid); @@ -554,7 +554,7 @@ mod test { cmd: CtapHidCommand::Ping, payload: vec![0x99, 0x99], }; - let mut response = CtapHid::split_message(message); + let mut response = CtapHid::::split_message(message); let mut expected_packet = [0x00; 64]; expected_packet[..9] .copy_from_slice(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x02, 0x99, 0x99]); @@ -570,7 +570,7 @@ mod test { cmd: CtapHidCommand::Cbor, payload, }; - let mut response = CtapHid::split_message(message); + let mut response = CtapHid::::split_message(message); let mut expected_packet = [0x00; 64]; expected_packet[..8].copy_from_slice(&[0x12, 0x34, 0x56, 0x78, 0x90, 0x00, 0x01, 0xF2]); assert_eq!(response.next(), Some(expected_packet)); @@ -581,7 +581,7 @@ mod test { fn test_keepalive() { for &status in [KeepaliveStatus::Processing, KeepaliveStatus::UpNeeded].iter() { let cid = [0x12, 0x34, 0x56, 0x78]; - let mut response = CtapHid::keepalive(cid, status); + let mut response = CtapHid::::keepalive(cid, status); let mut expected_packet = [0x00; 64]; expected_packet[..8].copy_from_slice(&[ 0x12, @@ -604,7 +604,7 @@ mod test { let mut packet = [0x00; 64]; packet[..4].copy_from_slice(&cid); packet[4..9].copy_from_slice(&[0x81, 0x00, 0x02, 0x99, 0x99]); - let (processed_cid, processed_packet) = CtapHid::process_single_packet(&packet); + let (processed_cid, processed_packet) = CtapHid::::process_single_packet(&packet); assert_eq!(processed_cid, cid); let expected_packet = ProcessedPacket::InitPacket { cmd: CtapHidCommand::Ping as u8, @@ -627,7 +627,7 @@ mod test { fn test_error_message() { let cid = [0x12, 0x34, 0x56, 0x78]; assert_eq!( - CtapHid::error_message(cid, CtapHidError::InvalidCmd), + CtapHid::::error_message(cid, CtapHidError::InvalidCmd), Message { cid, cmd: CtapHidCommand::Error, diff --git a/src/ctap/hid/receive.rs b/src/ctap/hid/receive.rs index f026b2bd..93c2d19b 100644 --- a/src/ctap/hid/receive.rs +++ b/src/ctap/hid/receive.rs @@ -19,10 +19,12 @@ 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; /// A structure to assemble CTAPHID commands from a series of incoming USB HID packets. -pub struct MessageAssembler { +pub struct MessageAssembler { + _phantom: PhantomData, // Whether this is waiting to receive an initialization packet. idle: bool, // Current channel ID. @@ -39,9 +41,10 @@ pub struct MessageAssembler { payload: Vec, } -impl MessageAssembler { - pub fn new() -> MessageAssembler { +impl MessageAssembler { + pub fn new() -> MessageAssembler { MessageAssembler { + _phantom: PhantomData, idle: true, cid: [0, 0, 0, 0], last_timestamp: CtapInstant::new(0), @@ -73,15 +76,15 @@ impl MessageAssembler { // packet was received. pub fn parse_packet( &mut self, - env: &mut impl Env, + env: &mut E, packet: &HidPacket, timestamp: CtapInstant, ) -> Result, (ChannelID, CtapHidError)> { // TODO: Support non-full-speed devices (i.e. packet len != 64)? This isn't recommended by // section 8.8.1 - let (cid, processed_packet) = CtapHid::process_single_packet(packet); + let (cid, processed_packet) = CtapHid::::process_single_packet(packet); - if !self.idle && timestamp >= self.last_timestamp + CtapHid::TIMEOUT_DURATION { + if !self.idle && timestamp >= self.last_timestamp + CtapHid::::TIMEOUT_DURATION { // The current channel timed out. // Save the channel ID and reset the state. let current_cid = self.cid; @@ -590,7 +593,7 @@ mod test { assembler.parse_packet( &mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]), - CtapInstant::new(0) + CtapHid::TIMEOUT_DURATION + CtapInstant::new(0) + CtapHid::::TIMEOUT_DURATION ), Err(([0x12, 0x34, 0x56, 0x78], CtapHidError::MsgTimeout)) ); @@ -601,7 +604,7 @@ mod test { 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 = CtapHid::::TIMEOUT_DURATION - Milliseconds(1_u32); let mut assembler = MessageAssembler::new(); assert_eq!( diff --git a/src/ctap/hid/send.rs b/src/ctap/hid/send.rs index afe49ecb..4869355a 100644 --- a/src/ctap/hid/send.rs +++ b/src/ctap/hid/send.rs @@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::{CtapHid, HidPacket, Message}; +use super::{HidPacket, Message}; + +const TYPE_INIT_BIT: u8 = 0x80; /// Iterator for HID packets. /// @@ -121,7 +123,7 @@ impl Iterator for MessageSplitter { match self.seq { None => { // First, send an initialization packet. - self.packet[4] = self.message.cmd as u8 | CtapHid::TYPE_INIT_BIT; + self.packet[4] = self.message.cmd as u8 | TYPE_INIT_BIT; self.packet[5] = (payload_len >> 8) as u8; self.packet[6] = payload_len as u8; diff --git a/src/ctap/large_blobs.rs b/src/ctap/large_blobs.rs index d8588dea..2ef68c78 100644 --- a/src/ctap/large_blobs.rs +++ b/src/ctap/large_blobs.rs @@ -45,10 +45,10 @@ impl LargeBlobs { } /// Process the large blob command. - pub fn process_command( + pub fn process_command( &mut self, - env: &mut impl Env, - client_pin: &mut ClientPin, + env: &mut E, + client_pin: &mut ClientPin, large_blobs_params: AuthenticatorLargeBlobsParameters, ) -> Result { let AuthenticatorLargeBlobsParameters { @@ -147,8 +147,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let mut large_blobs = LargeBlobs::new(); let large_blob = vec![ @@ -178,8 +182,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let mut large_blobs = LargeBlobs::new(); const BLOB_LEN: usize = 200; @@ -240,8 +248,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let mut large_blobs = LargeBlobs::new(); const BLOB_LEN: usize = 200; @@ -286,8 +298,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let mut large_blobs = LargeBlobs::new(); const BLOB_LEN: usize = 200; @@ -332,8 +348,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let mut large_blobs = LargeBlobs::new(); let large_blobs_params = AuthenticatorLargeBlobsParameters { @@ -355,8 +375,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let mut large_blobs = LargeBlobs::new(); const BLOB_LEN: usize = 20; @@ -383,8 +407,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let mut client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol); + let mut client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + pin_uv_auth_protocol, + ); let mut large_blobs = LargeBlobs::new(); const BLOB_LEN: usize = 20; diff --git a/src/ctap/main_hid.rs b/src/ctap/main_hid.rs index e34ee555..0abc8523 100644 --- a/src/ctap/main_hid.rs +++ b/src/ctap/main_hid.rs @@ -25,23 +25,24 @@ use crate::env::Env; use embedded_time::duration::Milliseconds; /// Implements the standard CTAP command processing for HID. -pub struct MainHid { - hid: CtapHid, +pub struct MainHid { + hid: CtapHid, wink_permission: TimedPermission, } -impl MainHid { +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")] - let capabilities = CtapHid::CAPABILITY_WINK | CtapHid::CAPABILITY_CBOR; + let capabilities = CtapHid::::CAPABILITY_WINK | CtapHid::::CAPABILITY_CBOR; #[cfg(not(feature = "with_ctap1"))] - let capabilities = - CtapHid::CAPABILITY_WINK | CtapHid::CAPABILITY_CBOR | CtapHid::CAPABILITY_NMSG; + let capabilities = CtapHid::::CAPABILITY_WINK + | CtapHid::::CAPABILITY_CBOR + | CtapHid::::CAPABILITY_NMSG; - let hid = CtapHid::new(capabilities); + let hid = CtapHid::::new(capabilities); let wink_permission = TimedPermission::waiting(); MainHid { hid, @@ -52,15 +53,15 @@ impl MainHid { /// Processes an incoming USB HID packet, and returns an iterator for all outgoing packets. pub fn process_hid_packet( &mut self, - env: &mut impl Env, + env: &mut E, packet: &HidPacket, now: CtapInstant, - ctap_state: &mut CtapState, + ctap_state: &mut CtapState, ) -> HidPacketIterator { if let Some(message) = self.hid.parse_packet(env, packet, now) { let processed_message = self.process_message(env, message, now, ctap_state); debug_ctap!(env, "Sending message: {:02x?}", processed_message); - CtapHid::split_message(processed_message) + CtapHid::::split_message(processed_message) } else { HidPacketIterator::none() } @@ -69,10 +70,10 @@ impl MainHid { /// Processes a message's commands that affect the protocol outside HID. pub fn process_message( &mut self, - env: &mut impl Env, + env: &mut E, message: Message, now: CtapInstant, - ctap_state: &mut CtapState, + ctap_state: &mut CtapState, ) -> Message { // If another command arrives, stop winking to prevent accidential button touches. self.wink_permission = TimedPermission::waiting(); @@ -83,12 +84,12 @@ impl MainHid { CtapHidCommand::Msg => { // If we don't have CTAP1 backward compatibilty, this command is invalid. #[cfg(not(feature = "with_ctap1"))] - return CtapHid::error_message(cid, CtapHidError::InvalidCmd); + return CtapHid::::error_message(cid, CtapHidError::InvalidCmd); #[cfg(feature = "with_ctap1")] match ctap1::Ctap1Command::process_command(env, &message.payload, ctap_state, now) { - Ok(payload) => MainHid::ctap1_success_message(cid, &payload), - Err(ctap1_status_code) => MainHid::ctap1_error_message(cid, ctap1_status_code), + Ok(payload) => Self::ctap1_success_message(cid, &payload), + Err(ctap1_status_code) => Self::ctap1_error_message(cid, ctap1_status_code), } } // CTAP 2.1 from 2021-06-15, section 11.2.9.1.2. @@ -112,7 +113,7 @@ impl MainHid { // The response is empty like the request. message } else { - CtapHid::error_message(cid, CtapHidError::InvalidLen) + CtapHid::::error_message(cid, CtapHidError::InvalidLen) } } // All other commands have already been processed, keep them as is. @@ -159,11 +160,11 @@ mod test { use crate::ctap::hid::ChannelID; use crate::env::test::TestEnv; - fn new_initialized() -> (MainHid, ChannelID) { + fn new_initialized() -> (MainHid, ChannelID) { let (hid, cid) = CtapHid::new_initialized(); let wink_permission = TimedPermission::waiting(); ( - MainHid { + MainHid:: { hid, wink_permission, }, @@ -174,7 +175,7 @@ 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, CtapInstant::new(0)); let (mut main_hid, cid) = new_initialized(); let mut ping_packet = [0x00; 64]; @@ -194,7 +195,7 @@ 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, CtapInstant::new(0)); let (mut main_hid, cid) = new_initialized(); let mut cancel_packet = [0x00; 64]; @@ -213,7 +214,7 @@ mod test { #[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, CtapInstant::new(0)); let (mut main_hid, cid) = new_initialized(); assert!(!main_hid.should_wink(CtapInstant::new(0))); @@ -230,6 +231,8 @@ mod test { 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(CtapInstant::new(1) + MainHid::::WINK_TIMEOUT_DURATION) + ); } } diff --git a/src/ctap/mod.rs b/src/ctap/mod.rs index 97e1bc45..7d00e753 100644 --- a/src/ctap/mod.rs +++ b/src/ctap/mod.rs @@ -77,6 +77,7 @@ 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}; @@ -209,8 +210,8 @@ fn truncate_to_char_boundary(s: &str, mut max: usize) -> &str { // Sends keepalive packet during user presence checking. If user agent replies with CANCEL response, // returns Err(UserPresenceError::Canceled). -fn send_keepalive_up_needed( - env: &mut impl Env, +fn send_keepalive_up_needed( + env: &mut E, channel: Channel, timeout: Milliseconds, ) -> Result<(), UserPresenceError> { @@ -219,7 +220,7 @@ fn send_keepalive_up_needed( #[cfg(feature = "vendor_hid")] Channel::VendorHid(cid) => (cid, Transport::VendorHid), }; - let keepalive_msg = CtapHid::keepalive(cid, KeepaliveStatus::UpNeeded); + let keepalive_msg = CtapHid::::keepalive(cid, KeepaliveStatus::UpNeeded); for mut pkt in keepalive_msg { let ctap_hid_connection = transport.hid_connection(env); match ctap_hid_connection.send_and_maybe_recv(&mut pkt, timeout) { @@ -249,7 +250,7 @@ fn send_keepalive_up_needed( } // We only parse one packet, because we only care about CANCEL. - let (received_cid, processed_packet) = CtapHid::process_single_packet(&pkt); + let (received_cid, processed_packet) = CtapHid::::process_single_packet(&pkt); if received_cid != cid { debug_ctap!( env, @@ -288,7 +289,7 @@ fn send_keepalive_up_needed( /// Blocks for user presence. /// /// Returns an error in case of timeout, user declining presence request, or keepalive error. -fn check_user_presence(env: &mut impl Env, channel: Channel) -> Result<(), Ctap2StatusCode> { +fn check_user_presence(env: &mut E, channel: Channel) -> Result<(), Ctap2StatusCode> { env.user_presence().check_init(); // The timeout is N times the keepalive delay. @@ -365,19 +366,21 @@ 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 { +pub struct StatefulPermission { + _phantom: PhantomData, permission: TimedPermission, command_type: Option, channel: Option, } -impl StatefulPermission { +impl StatefulPermission { /// Creates the command state at device startup. /// /// Resets are only possible after a power cycle. Therefore, initialization /// means allowing Reset, and Reset cannot be granted later. - pub fn new_reset(now: CtapInstant) -> StatefulPermission { + pub fn new_reset(now: CtapInstant) -> StatefulPermission { StatefulPermission { + _phantom: PhantomData, permission: TimedPermission::granted(now, RESET_TIMEOUT_DURATION), command_type: Some(StatefulCommand::Reset), channel: None, @@ -486,24 +489,24 @@ impl StatefulPermission { // This struct currently holds all state, not only the persistent memory. The persistent members are // in the persistent store field. -pub struct CtapState { - client_pin: ClientPin, +pub struct CtapState { + client_pin: ClientPin, #[cfg(feature = "with_ctap1")] pub(crate) u2f_up_state: U2fUserPresenceState, // The state initializes to Reset and its timeout, and never goes back to Reset. - stateful_command_permission: StatefulPermission, + stateful_command_permission: StatefulPermission, large_blobs: LargeBlobs, } -impl CtapState { - pub fn new(env: &mut impl Env, now: CtapInstant) -> Self { +impl CtapState { + pub fn new(env: &mut E, now: CtapInstant) -> Self { storage::init(env).ok().unwrap(); - let client_pin = ClientPin::new(env.rng()); + let client_pin = ClientPin::::new(env); CtapState { client_pin, #[cfg(feature = "with_ctap1")] u2f_up_state: U2fUserPresenceState::new(U2F_UP_PROMPT_TIMEOUT, TOUCH_TIMEOUT), - stateful_command_permission: StatefulPermission::new_reset(now), + stateful_command_permission: StatefulPermission::::new_reset(now), large_blobs: LargeBlobs::new(), } } @@ -515,7 +518,7 @@ impl CtapState { pub fn increment_global_signature_counter( &mut self, - env: &mut impl Env, + env: &mut E, ) -> Result<(), Ctap2StatusCode> { if env.customization().use_signature_counter() { let increment = env.rng().gen_uniform_u32x8()[0] % 8 + 1; @@ -528,13 +531,13 @@ impl CtapState { // If alwaysUv is enabled and the authenticator does not support internal UV, // CTAP1 needs to be disabled. #[cfg(feature = "with_ctap1")] - pub fn allows_ctap1(&self, env: &mut impl Env) -> Result { + pub fn allows_ctap1(&self, env: &mut E) -> Result { Ok(!storage::has_always_uv(env)?) } pub fn process_command( &mut self, - env: &mut impl Env, + env: &mut E, command_cbor: &[u8], channel: Channel, now: CtapInstant, @@ -564,7 +567,7 @@ impl CtapState { /// It should make command parsing easier to test. pub fn process_parsed_command( &mut self, - env: &mut impl Env, + env: &mut E, command: Command, channel: Channel, now: CtapInstant, @@ -608,7 +611,7 @@ impl CtapState { fn process_fido_command( &mut self, - env: &mut impl Env, + env: &mut E, command: Command, channel: Channel, now: CtapInstant, @@ -651,7 +654,7 @@ impl CtapState { fn process_vendor_command( &mut self, - env: &mut impl Env, + env: &mut E, command: Command, channel: Channel, ) -> Result { @@ -668,7 +671,7 @@ impl CtapState { fn pin_uv_auth_precheck( &mut self, - env: &mut impl Env, + env: &mut E, pin_uv_auth_param: &Option>, pin_uv_auth_protocol: Option, channel: Channel, @@ -706,7 +709,7 @@ impl CtapState { fn process_make_credential( &mut self, - env: &mut impl Env, + env: &mut E, make_credential_params: AuthenticatorMakeCredentialParameters, channel: Channel, ) -> Result { @@ -966,7 +969,7 @@ impl CtapState { // The computation is deterministic, and private_key expected to be unique. fn generate_cred_random( &mut self, - env: &mut impl Env, + env: &mut E, private_key: &PrivateKey, has_uv: bool, ) -> Result<[u8; 32], Ctap2StatusCode> { @@ -979,7 +982,7 @@ impl CtapState { // and returns the correct Get(Next)Assertion response. fn assertion_response( &mut self, - env: &mut impl Env, + env: &mut E, mut credential: PublicKeyCredentialSource, assertion_input: AssertionInput, number_of_credentials: Option, @@ -997,11 +1000,10 @@ impl CtapState { let encrypted_output = if let Some(hmac_secret_input) = extensions.hmac_secret { let cred_random = self.generate_cred_random(env, &credential.private_key, has_uv)?; - Some(self.client_pin.process_hmac_secret( - env.rng(), - hmac_secret_input, - &cred_random, - )?) + Some( + self.client_pin + .process_hmac_secret(env, hmac_secret_input, &cred_random)?, + ) } else { None }; @@ -1068,7 +1070,7 @@ impl CtapState { // Returns the first applicable credential from the allow list. fn get_any_credential_from_allow_list( &mut self, - env: &mut impl Env, + env: &mut E, allow_list: Vec, rp_id: &str, rp_id_hash: &[u8], @@ -1089,7 +1091,7 @@ impl CtapState { fn process_get_assertion( &mut self, - env: &mut impl Env, + env: &mut E, get_assertion_params: AuthenticatorGetAssertionParameters, channel: Channel, now: CtapInstant, @@ -1225,10 +1227,7 @@ impl CtapState { ) } - fn process_get_next_assertion( - &mut self, - env: &mut impl Env, - ) -> Result { + fn process_get_next_assertion(&mut self, env: &mut E) -> Result { let (assertion_input, credential_key) = self .stateful_command_permission .next_assertion_credential()?; @@ -1236,7 +1235,7 @@ impl CtapState { self.assertion_response(env, credential, assertion_input, None, true) } - fn process_get_info(&self, env: &mut impl Env) -> Result { + fn process_get_info(&self, env: &mut E) -> Result { let has_always_uv = storage::has_always_uv(env)?; #[cfg_attr(not(feature = "with_ctap1"), allow(unused_mut))] let mut versions = vec![ @@ -1310,7 +1309,7 @@ impl CtapState { fn process_reset( &mut self, - env: &mut impl Env, + env: &mut E, channel: Channel, ) -> Result { match self.stateful_command_permission.get_command()? { @@ -1320,7 +1319,7 @@ impl CtapState { check_user_presence(env, channel)?; storage::reset(env)?; - self.client_pin.reset(env.rng()); + self.client_pin.reset(env); #[cfg(feature = "with_ctap1")] { // We create a block statement to wrap this assignment expression, because attributes @@ -1332,7 +1331,7 @@ impl CtapState { fn process_selection( &self, - env: &mut impl Env, + env: &mut E, channel: Channel, ) -> Result { check_user_presence(env, channel)?; @@ -1341,7 +1340,7 @@ impl CtapState { fn process_vendor_configure( &mut self, - env: &mut impl Env, + env: &mut E, params: AuthenticatorVendorConfigureParameters, channel: Channel, ) -> Result { @@ -1395,7 +1394,7 @@ impl CtapState { fn process_vendor_upgrade( &mut self, - env: &mut impl Env, + env: &mut E, params: AuthenticatorVendorUpgradeParameters, ) -> Result { let AuthenticatorVendorUpgradeParameters { offset, data, hash } = params; @@ -1410,10 +1409,7 @@ impl CtapState { Ok(ResponseData::AuthenticatorVendorUpgrade) } - fn process_vendor_upgrade_info( - &self, - env: &mut impl Env, - ) -> Result { + fn process_vendor_upgrade_info(&self, env: &mut E) -> Result { let upgrade_locations = env .upgrade_storage() .ok_or(Ctap2StatusCode::CTAP1_ERR_INVALID_COMMAND)?; @@ -1426,7 +1422,7 @@ impl CtapState { pub fn generate_auth_data( &self, - env: &mut impl Env, + env: &mut E, rp_id_hash: &[u8], flag_byte: u8, ) -> Result, Ctap2StatusCode> { @@ -1529,7 +1525,7 @@ mod test { #[test] fn test_get_info() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + 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)); @@ -1675,7 +1671,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, CtapInstant::new(0)); let make_credential_params = create_minimal_make_credential_parameters(); let make_credential_response = @@ -1693,7 +1689,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, CtapInstant::new(0)); let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.options.rk = false; @@ -1712,7 +1708,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, CtapInstant::new(0)); let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.pub_key_cred_params = vec![]; @@ -1729,7 +1725,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, CtapInstant::new(0)); let excluded_credential_id = vec![0x01, 0x23, 0x45, 0x67]; let make_credential_params = @@ -1761,7 +1757,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, CtapInstant::new(0)); let test_policy = CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList; let make_credential_params = @@ -1855,7 +1851,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, CtapInstant::new(0)); let extensions = MakeCredentialExtensions { hmac_secret: true, @@ -1882,7 +1878,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, CtapInstant::new(0)); let extensions = MakeCredentialExtensions { hmac_secret: true, @@ -1908,7 +1904,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, CtapInstant::new(0)); // First part: The extension is ignored, since the RP ID is not on the list. let extensions = MakeCredentialExtensions { @@ -1957,7 +1953,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, CtapInstant::new(0)); let extensions = MakeCredentialExtensions { cred_blob: Some(vec![0xCB]), @@ -1989,7 +1985,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, CtapInstant::new(0)); let extensions = MakeCredentialExtensions { cred_blob: Some(vec![0xCB; env.customization().max_cred_blob_length() + 1]), @@ -2021,7 +2017,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, CtapInstant::new(0)); let extensions = MakeCredentialExtensions { large_blob_key: Some(true), @@ -2053,10 +2049,14 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x91; PIN_TOKEN_LENGTH]; - let client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol); + let client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + pin_uv_auth_protocol, + ); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env, CtapInstant::new(0)); ctap_state.client_pin = client_pin; storage::set_pin(&mut env, &[0x88; 16], 4).unwrap(); @@ -2105,7 +2105,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, CtapInstant::new(0)); storage::set_pin(&mut env, &[0x88; 16], 4).unwrap(); let mut make_credential_params = create_minimal_make_credential_parameters(); @@ -2125,7 +2125,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, CtapInstant::new(0)); storage::set_pin(&mut env, &[0x88; 16], 4).unwrap(); let make_credential_params = create_minimal_make_credential_parameters(); @@ -2140,7 +2140,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, CtapInstant::new(0)); storage::toggle_always_uv(&mut env).unwrap(); let make_credential_params = create_minimal_make_credential_parameters(); @@ -2181,7 +2181,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, CtapInstant::new(0)); test_helpers::enable_enterprise_attestation(&mut ctap_state, &mut env).unwrap(); let mut make_credential_params = create_minimal_make_credential_parameters(); @@ -2228,7 +2228,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, CtapInstant::new(0)); test_helpers::enable_enterprise_attestation(&mut ctap_state, &mut env).unwrap(); let mut make_credential_params = create_minimal_make_credential_parameters(); @@ -2261,7 +2261,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, CtapInstant::new(0)); let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.enterprise_attestation = Some(2); @@ -2288,7 +2288,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, CtapInstant::new(0)); let make_credential_params = create_minimal_make_credential_parameters(); let make_credential_response = @@ -2383,7 +2383,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, CtapInstant::new(0)); let make_credential_params = create_minimal_make_credential_parameters(); assert!(ctap_state @@ -2468,7 +2468,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, CtapInstant::new(0)); let make_extensions = MakeCredentialExtensions { hmac_secret: true, @@ -2529,7 +2529,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, CtapInstant::new(0)); let make_extensions = MakeCredentialExtensions { hmac_secret: true, @@ -2585,7 +2585,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, CtapInstant::new(0)); let cred_desc = PublicKeyCredentialDescriptor { key_type: PublicKeyCredentialType::PublicKey, @@ -2780,7 +2780,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, CtapInstant::new(0)); let credential = PublicKeyCredentialSource { key_type: PublicKeyCredentialType::PublicKey, @@ -2907,7 +2907,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, CtapInstant::new(0)); let credential = PublicKeyCredentialSource { key_type: PublicKeyCredentialType::PublicKey, @@ -2962,10 +2962,14 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x88; 32]; - let client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol); + let client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + pin_uv_auth_protocol, + ); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env, CtapInstant::new(0)); let mut make_credential_params = create_minimal_make_credential_parameters(); let user1 = PublicKeyCredentialUserEntity { @@ -3058,7 +3062,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, CtapInstant::new(0)); let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.user.user_id = vec![0x01]; @@ -3127,7 +3131,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, CtapInstant::new(0)); let get_assertion_response = ctap_state.process_get_next_assertion(&mut env); assert_eq!( @@ -3192,7 +3196,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, CtapInstant::new(0)); let credential_id = vec![0x01, 0x23, 0x45, 0x67]; let credential_source = PublicKeyCredentialSource { @@ -3223,7 +3227,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, CtapInstant::new(0)); let reset_reponse = ctap_state.process_reset(&mut env, DUMMY_CHANNEL); @@ -3236,7 +3240,7 @@ 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, CtapInstant::new(0)); // This is a GetNextAssertion command. ctap_state.process_command(&mut env, &[0x08], DUMMY_CHANNEL, CtapInstant::new(0)); @@ -3248,7 +3252,7 @@ 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, CtapInstant::new(0)); // The subcommand 0xEE does not exist. let reponse = ctap_state.process_command( @@ -3264,7 +3268,7 @@ 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, CtapInstant::new(0)); // This command does not exist. let reponse = @@ -3276,7 +3280,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, CtapInstant::new(0)); let mut last_counter = storage::global_signature_counter(&mut env).unwrap(); assert!(last_counter > 0); @@ -3293,7 +3297,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, CtapInstant::new(0)); // Nothing should be configured at the beginning let response = ctap_state.process_vendor_configure( @@ -3401,7 +3405,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, CtapInstant::new(0)); const METADATA_LEN: usize = 0x1000; let metadata = vec![0xFF; METADATA_LEN]; @@ -3480,7 +3484,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, CtapInstant::new(0)); let data = vec![0xFF; 0x1000]; let hash = Sha256::hash(&data); @@ -3498,7 +3502,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, CtapInstant::new(0)); let bundle_identifier = env.upgrade_storage().unwrap().bundle_identifier(); let upgrade_info_reponse = ctap_state.process_vendor_upgrade_info(&mut env); @@ -3515,7 +3519,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, CtapInstant::new(0)); // Write 2 credentials for later assertions. for i in 0..3 { @@ -3567,7 +3571,7 @@ 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, CtapInstant::new(0)); let response = ctap_state.process_parsed_command( &mut env, @@ -3583,8 +3587,12 @@ mod test { let mut env = TestEnv::new(); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; - let client_pin = - ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); + let client_pin = ClientPin::::new_test( + &mut env, + key_agreement_key, + pin_uv_auth_token, + PinUvAuthProtocol::V1, + ); let private_key = PrivateKey::new_ecdsa(&mut env); let credential_source = PublicKeyCredentialSource { @@ -3602,7 +3610,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, CtapInstant::new(0)); ctap_state.client_pin = client_pin; for i in 0..3 { @@ -3693,7 +3701,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, CtapInstant::new(0)); const NEW_CHANNEL: Channel = Channel::MainHid([0xAA, 0xAA, 0xAA, 0xAA]); // Write 3 credentials for later assertions. @@ -3763,7 +3771,7 @@ 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, CtapInstant::new(0)); let response = ctap_state.process_parsed_command( &mut env, @@ -3808,7 +3816,7 @@ 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, CtapInstant::new(0)); let response = ctap_state.process_parsed_command( &mut env, diff --git a/src/ctap/token_state.rs b/src/ctap/token_state.rs index c2811502..c3f6837f 100644 --- a/src/ctap/token_state.rs +++ b/src/ctap/token_state.rs @@ -15,7 +15,9 @@ 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; @@ -35,7 +37,8 @@ const INITIAL_USAGE_TIME_LIMIT: Milliseconds = Milliseconds(30000 as C /// built-in user verification. Therefore, we never cache user presence. /// /// This implementation does not use a rolling timer. -pub struct PinUvAuthTokenState { +pub struct PinUvAuthTokenState { + _phantom: PhantomData, // Relies on the fact that all permissions are represented by powers of two. permissions_set: u8, permissions_rp_id: Option, @@ -44,10 +47,11 @@ pub struct PinUvAuthTokenState { in_use: bool, } -impl PinUvAuthTokenState { +impl PinUvAuthTokenState { /// Creates a pinUvAuthToken state without permissions. - pub fn new() -> PinUvAuthTokenState { + pub fn new() -> Self { PinUvAuthTokenState { + _phantom: PhantomData, permissions_set: 0, permissions_rp_id: None, usage_timer: TimedPermission::waiting(), @@ -158,11 +162,12 @@ impl PinUvAuthTokenState { #[cfg(test)] mod test { use super::*; + use crate::env::test::TestEnv; use enum_iterator::IntoEnumIterator; #[test] fn test_observer() { - let mut token_state = PinUvAuthTokenState::new(); + let mut token_state = PinUvAuthTokenState::::new(); let mut now: CtapInstant = CtapInstant::new(0); token_state.begin_using_pin_uv_auth_token(now); assert!(token_state.is_in_use()); @@ -176,7 +181,7 @@ mod test { #[test] fn test_stop() { - let mut token_state = PinUvAuthTokenState::new(); + let mut token_state = PinUvAuthTokenState::::new(); let now: CtapInstant = CtapInstant::new(0); token_state.begin_using_pin_uv_auth_token(now); assert!(token_state.is_in_use()); @@ -186,7 +191,7 @@ mod test { #[test] fn test_permissions() { - let mut token_state = PinUvAuthTokenState::new(); + let mut token_state = PinUvAuthTokenState::::new(); token_state.set_permissions(0xFF); for permission in PinPermission::into_enum_iter() { assert_eq!(token_state.has_permission(permission), Ok(())); @@ -211,7 +216,7 @@ mod test { #[test] fn test_permissions_rp_id_none() { - let mut token_state = PinUvAuthTokenState::new(); + let mut token_state = PinUvAuthTokenState::::new(); let example_hash = Sha256::hash(b"example.com"); token_state.set_permissions_rp_id(None); assert_eq!(token_state.has_no_permissions_rp_id(), Ok(())); @@ -227,7 +232,7 @@ mod test { #[test] fn test_permissions_rp_id_some() { - let mut token_state = PinUvAuthTokenState::new(); + let mut token_state = PinUvAuthTokenState::::new(); let example_hash = Sha256::hash(b"example.com"); token_state.set_permissions_rp_id(Some(String::from("example.com"))); @@ -262,7 +267,7 @@ mod test { #[test] fn test_user_verified_flag() { - let mut token_state = PinUvAuthTokenState::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); diff --git a/src/ctap/vendor_hid.rs b/src/ctap/vendor_hid.rs index 4aa0e295..316e7133 100644 --- a/src/ctap/vendor_hid.rs +++ b/src/ctap/vendor_hid.rs @@ -22,24 +22,24 @@ use crate::env::Env; /// Implements the non-standard command processing for HID. /// /// Outside of the pure HID commands like INIT, only PING and CBOR commands are allowed. -pub struct VendorHid { - hid: CtapHid, +pub struct VendorHid { + hid: CtapHid, } -impl VendorHid { +impl VendorHid { /// Instantiates a HID handler for CTAP1, CTAP2 and Wink. pub fn new() -> Self { - let hid = CtapHid::new(CtapHid::CAPABILITY_CBOR | CtapHid::CAPABILITY_NMSG); + let hid = CtapHid::::new(CtapHid::::CAPABILITY_CBOR | CtapHid::::CAPABILITY_NMSG); VendorHid { hid } } /// Processes an incoming USB HID packet, and returns an iterator for all outgoing packets. pub fn process_hid_packet( &mut self, - env: &mut impl Env, + env: &mut E, packet: &HidPacket, now: CtapInstant, - ctap_state: &mut CtapState, + ctap_state: &mut CtapState, ) -> HidPacketIterator { if let Some(message) = self.hid.parse_packet(env, packet, now) { let processed_message = self.process_message(env, message, now, ctap_state); @@ -48,7 +48,7 @@ impl VendorHid { "Sending message through the second usage page: {:02x?}", processed_message ); - CtapHid::split_message(processed_message) + CtapHid::::split_message(processed_message) } else { HidPacketIterator::none() } @@ -57,15 +57,15 @@ impl VendorHid { /// Processes a message's commands that affect the protocol outside HID. pub fn process_message( &mut self, - env: &mut impl Env, + env: &mut E, message: Message, now: CtapInstant, - ctap_state: &mut CtapState, + ctap_state: &mut CtapState, ) -> Message { let cid = message.cid; match message.cmd { // There are no custom CTAP1 commands. - CtapHidCommand::Msg => CtapHid::error_message(cid, CtapHidError::InvalidCmd), + CtapHidCommand::Msg => CtapHid::::error_message(cid, CtapHidError::InvalidCmd), // The CTAP2 processing function multiplexes internally. CtapHidCommand::Cbor => { let response = @@ -77,7 +77,7 @@ impl VendorHid { } } // Call Wink over the main HID. - CtapHidCommand::Wink => CtapHid::error_message(cid, CtapHidError::InvalidCmd), + CtapHidCommand::Wink => CtapHid::::error_message(cid, CtapHidError::InvalidCmd), // All other commands have already been processed, keep them as is. _ => message, } @@ -90,15 +90,15 @@ mod test { use crate::ctap::hid::ChannelID; use crate::env::test::TestEnv; - fn new_initialized() -> (VendorHid, ChannelID) { + fn new_initialized() -> (VendorHid, ChannelID) { let (hid, cid) = CtapHid::new_initialized(); - (VendorHid { hid }, cid) + (VendorHid:: { hid }, cid) } #[test] fn test_process_hid_packet() { let mut env = TestEnv::new(); - let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let mut ctap_state = CtapState::::new(&mut env, CtapInstant::new(0)); let (mut vendor_hid, cid) = new_initialized(); let mut ping_packet = [0x00; 64]; @@ -118,7 +118,7 @@ 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, CtapInstant::new(0)); let (mut vendor_hid, cid) = new_initialized(); let mut cancel_packet = [0x00; 64]; @@ -137,7 +137,7 @@ mod test { #[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, CtapInstant::new(0)); let (mut vendor_hid, cid) = new_initialized(); // Usually longer, but we don't parse them anyway. diff --git a/src/lib.rs b/src/lib.rs index 3690b57f..5380c733 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,10 +55,10 @@ pub mod test_helpers; /// CTAP implementation parameterized by its environment. pub struct Ctap { env: E, - state: CtapState, - hid: MainHid, + state: CtapState, + hid: MainHid, #[cfg(feature = "vendor_hid")] - vendor_hid: VendorHid, + vendor_hid: VendorHid, } impl Ctap { @@ -66,7 +66,7 @@ impl Ctap { // 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); + let state = CtapState::::new(&mut env, now); let hid = MainHid::new(); #[cfg(feature = "vendor_hid")] let vendor_hid = VendorHid::new(); @@ -79,11 +79,11 @@ impl Ctap { } } - pub fn state(&mut self) -> &mut CtapState { + pub fn state(&mut self) -> &mut CtapState { &mut self.state } - pub fn hid(&mut self) -> &mut MainHid { + pub fn hid(&mut self) -> &mut MainHid { &mut self.hid } diff --git a/src/test_helpers/mod.rs b/src/test_helpers/mod.rs index 968e25a2..a9e85be4 100644 --- a/src/test_helpers/mod.rs +++ b/src/test_helpers/mod.rs @@ -28,9 +28,9 @@ const DUMMY_CHANNEL: Channel = Channel::MainHid([0x12, 0x34, 0x56, 0x78]); #[cfg(feature = "vendor_hid")] const VENDOR_CHANNEL: Channel = Channel::VendorHid([0x12, 0x34, 0x56, 0x78]); -pub fn enable_enterprise_attestation( - state: &mut CtapState, - env: &mut impl Env, +pub fn enable_enterprise_attestation( + state: &mut CtapState, + env: &mut E, ) -> Result { let dummy_key = [0x41; key_material::ATTESTATION_PRIVATE_KEY_LENGTH]; let dummy_cert = vec![0xdd; 20]; From 7ef2e22b4a98bb56bb39f7b3897aa7f8007ca2f8 Mon Sep 17 00:00:00 2001 From: Fabian Kaczmarczyck Date: Thu, 23 Feb 2023 14:44:51 +0100 Subject: [PATCH 2/8] adds Clock type to Env --- src/api/clock.rs | 37 +++++++++++ src/api/mod.rs | 1 + src/env/mod.rs | 5 +- src/env/test/mod.rs | 72 +++++++++++++++++++++ src/env/tock/clock.rs | 146 ++++++++++++++++++++++++++++++++++++++++++ src/env/tock/mod.rs | 10 +++ 6 files changed, 270 insertions(+), 1 deletion(-) create mode 100644 src/api/clock.rs create mode 100644 src/env/tock/clock.rs diff --git a/src/api/clock.rs b/src/api/clock.rs new file mode 100644 index 00000000..24c65184 --- /dev/null +++ b/src/api/clock.rs @@ -0,0 +1,37 @@ +// Copyright 2022-2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub trait Clock: Sized { + type Timer; + + /// Creates a new timer that expires after the given time in ms. + fn make_timer(&mut self, milliseconds: usize) -> Self::Timer; + + /// Consumes a timer, and returns it if not expired. + fn check_timer(&mut self, timer: Self::Timer) -> Option; + + /// Checks the timer and takes it, if expired. + fn update_timer(&mut self, timer: &mut Option) { + *timer = timer.take().and_then(|t| Self::check_timer(self, t)); + } + + /// Tickles the clock and makes it check its timers. Often a NOOP. + fn tickle(&mut self); + + /// Timestamp in microseconds. + /// + /// Normal operation only needs relative time, absolute timestamps are useful for debugging. + #[cfg(feature = "debug_ctap")] + fn timestamp_us(&mut self) -> usize; +} diff --git a/src/api/mod.rs b/src/api/mod.rs index 1239c112..faaa5e8f 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -18,6 +18,7 @@ //! by a trait. This module gathers the API of those components. pub mod attestation_store; +pub mod clock; pub mod connection; pub mod customization; pub mod firmware_protection; diff --git a/src/env/mod.rs b/src/env/mod.rs index eb1ad5c7..ba3e8145 100644 --- a/src/env/mod.rs +++ b/src/env/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2022-2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ // limitations under the License. use crate::api::attestation_store::AttestationStore; +use crate::api::clock::Clock; use crate::api::connection::HidConnection; use crate::api::customization::Customization; use crate::api::firmware_protection::FirmwareProtection; @@ -38,12 +39,14 @@ pub trait Env { type Customization: Customization; type HidConnection: HidConnection; type AttestationStore: AttestationStore; + type Clock: Clock; fn rng(&mut self) -> &mut Self::Rng; fn user_presence(&mut self) -> &mut Self::UserPresence; fn store(&mut self) -> &mut Store; fn key_store(&mut self) -> &mut Self::KeyStore; fn attestation_store(&mut self) -> &mut Self::AttestationStore; + fn clock(&mut self) -> &mut Self::Clock; /// Returns the upgrade storage instance. /// diff --git a/src/env/test/mod.rs b/src/env/test/mod.rs index 163b5e69..f4dd3c45 100644 --- a/src/env/test/mod.rs +++ b/src/env/test/mod.rs @@ -14,6 +14,7 @@ use self::upgrade_storage::BufferUpgradeStorage; use crate::api::attestation_store::AttestationStore; +use crate::api::clock::Clock; use crate::api::connection::{HidConnection, SendOrRecvResult, SendOrRecvStatus}; use crate::api::customization::DEFAULT_CUSTOMIZATION; use crate::api::firmware_protection::FirmwareProtection; @@ -37,6 +38,7 @@ pub struct TestEnv { store: Store, upgrade_storage: Option, customization: TestCustomization, + clock: TestClock, } pub struct TestRng256 { @@ -57,6 +59,52 @@ impl Rng256 for TestRng256 { } } +#[derive(Debug, PartialEq)] +pub struct TestTimer { + end: usize, +} + +#[derive(Debug)] +pub struct TestClock { + cur_time: usize, +} + +impl TestClock { + pub fn new() -> Self { + TestClock { cur_time: 0 } + } + + pub fn advance(&mut self, milliseconds: usize) { + self.cur_time += milliseconds; + } +} + +impl Clock for TestClock { + type Timer = TestTimer; + + fn make_timer(&mut self, milliseconds: usize) -> Self::Timer { + TestTimer { + end: self.cur_time + milliseconds, + } + } + + fn check_timer(&mut self, timer: Self::Timer) -> Option { + if self.cur_time < timer.end { + Some(timer) + } else { + None + } + } + + fn tickle(&mut self) {} + + #[cfg(feature = "debug_ctap")] + fn timestamp_us(&mut self) -> usize { + // Unused, but let's implement something because it's easy. + self.cur_time * 1000 + } +} + pub struct TestUserPresence { check: Box UserPresenceResult>, } @@ -107,12 +155,14 @@ impl TestEnv { let store = Store::new(storage).ok().unwrap(); let upgrade_storage = Some(BufferUpgradeStorage::new().unwrap()); let customization = DEFAULT_CUSTOMIZATION.into(); + let clock = TestClock::new(); TestEnv { rng, user_presence, store, upgrade_storage, customization, + clock, } } @@ -174,6 +224,7 @@ impl Env for TestEnv { type Storage = BufferStorage; type KeyStore = Self; type AttestationStore = Self; + type Clock = TestClock; type UpgradeStorage = BufferUpgradeStorage; type FirmwareProtection = Self; type Write = TestWrite; @@ -200,6 +251,10 @@ impl Env for TestEnv { self } + fn clock(&mut self) -> &mut Self::Clock { + &mut self.clock + } + fn upgrade_storage(&mut self) -> Option<&mut Self::UpgradeStorage> { self.upgrade_storage.as_mut() } @@ -225,3 +280,20 @@ impl Env for TestEnv { self } } + +#[cfg(test)] +#[allow(clippy::module_inception)] +mod test { + use super::*; + + #[test] + fn test_clock() { + let mut clock = TestClock::new(); + let timer = clock.make_timer(3); + let timer = clock.check_timer(timer).unwrap(); + clock.advance(2); + let timer = clock.check_timer(timer).unwrap(); + clock.advance(1); + assert_eq!(clock.check_timer(timer), None); + } +} diff --git a/src/env/tock/clock.rs b/src/env/tock/clock.rs new file mode 100644 index 00000000..303be641 --- /dev/null +++ b/src/env/tock/clock.rs @@ -0,0 +1,146 @@ +// Copyright 2022-2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::api::clock::Clock; +use libtock_core::syscalls; + +mod command_nr { + pub const GET_CLOCK_FREQUENCY: usize = 1; + pub const GET_CLOCK_VALUE: usize = 2; +} + +const DRIVER_NUMBER: usize = 0x00000; + +pub struct TockTimer { + end_epoch: usize, + end_tick: usize, +} + +/// Returns a tupel of u24 sum and how many times the sum wraps. +fn add_to_u24_with_wraps(lhs: usize, rhs: usize) -> (usize, usize) { + // Saturating should never happen, but it fails gracefully. + let sum = lhs.saturating_add(rhs); + (sum & 0xffffff, sum >> 24) +} + +fn wrapping_sub_u24(lhs: usize, rhs: usize) -> usize { + lhs.wrapping_sub(rhs) & 0xffffff +} + +pub struct TockClock { + epoch: usize, + tick: usize, +} + +impl TockClock { + pub fn new() -> Self { + TockClock { epoch: 0, tick: 0 } + } +} + +impl Clock for TockClock { + type Timer = TockTimer; + + fn make_timer(&mut self, milliseconds: usize) -> Self::Timer { + let clock_frequency = + syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_FREQUENCY, 0, 0) + .ok() + .unwrap(); + let cur_tick = syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_VALUE, 0, 0) + .ok() + .unwrap(); + // Update self first + if wrapping_sub_u24(self.tick, cur_tick) < 0x80_0000 { + self.epoch += 1; + } + self.tick = cur_tick; + + // Now create the Timer + let delta_tick = match milliseconds.checked_mul(clock_frequency) { + Some(x) => x / 1000, + // All CTAP timeouts are multiples of 100 so far. Worst case we timeout too early. + None => (milliseconds / 100).saturating_mul(clock_frequency / 10), + }; + let (end_tick, passed_epochs) = add_to_u24_with_wraps(cur_tick, delta_tick); + // Epoch wraps after thousands of years, so we don't mind. + let end_epoch = self.epoch + passed_epochs; + Self::Timer { + end_epoch, + end_tick, + } + } + + fn check_timer(&mut self, timer: Self::Timer) -> Option { + self.tickle(); + if (self.epoch, self.tick) < (timer.end_epoch, timer.end_tick) { + Some(timer) + } else { + None + } + } + + /// Elapses timers before the clock wraps. + /// + /// Call this regularly to timeout reliably despite wrapping clock ticks. + // TODO we implicitly update in the other commands, is this desirable? + fn tickle(&mut self) { + let cur_tick = syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_VALUE, 0, 0) + .ok() + .unwrap(); + if wrapping_sub_u24(self.tick, cur_tick) < 0x80_0000 { + self.epoch += 1; + } + self.tick = cur_tick; + } + + #[cfg(feature = "debug_ctap")] + fn timestamp_us(&mut self) -> usize { + let clock_frequency = + syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_FREQUENCY, 0, 0) + .ok() + .unwrap(); + let total_ticks = 0x100_0000 * self.epoch + self.tick; + total_ticks * 1_000_000 / clock_frequency + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_add_to_u24_with_wraps() { + // non-wrapping cases + assert_eq!(add_to_u24_with_wraps(0, 0), (0, 0)); + assert_eq!(add_to_u24_with_wraps(0xffffff, 0), (0xffffff, 0)); + assert_eq!(add_to_u24_with_wraps(0, 0xffffff), (0xffffff, 0)); + // wrapping cases + assert_eq!(add_to_u24_with_wraps(1, 0xffffff), (0, 1)); + assert_eq!(add_to_u24_with_wraps(0xffffff, 0xffffff), (0xfffffe, 1)); + assert_eq!(add_to_u24_with_wraps(0, 0x1000000), (0, 1)); + assert_eq!(add_to_u24_with_wraps(0, 0x2000000), (0, 2)); + assert_eq!(add_to_u24_with_wraps(0xffffff, 0x2ffffff), (0xfffffe, 3)); + } + + #[test] + fn test_wrapping_sub_u24() { + // non-wrapping cases + assert_eq!(wrapping_sub_u24(0, 0), 0); + assert_eq!(wrapping_sub_u24(0xffffff, 0), 0xffffff); + assert_eq!(wrapping_sub_u24(0xffffff, 0xffffff), 0); + // wrapping cases + assert_eq!(wrapping_sub_u24(0, 0xffffff), 1); + assert_eq!(wrapping_sub_u24(0, 1), 0xffffff); + } +} diff --git a/src/env/tock/mod.rs b/src/env/tock/mod.rs index ca050274..ad5e53a5 100644 --- a/src/env/tock/mod.rs +++ b/src/env/tock/mod.rs @@ -21,6 +21,7 @@ use crate::api::user_presence::{UserPresence, UserPresenceError, UserPresenceRes 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; @@ -35,6 +36,7 @@ use libtock_drivers::{crp, led, timer}; use persistent_store::{StorageResult, Store}; use rng256::TockRng256; +mod clock; mod storage; pub struct TockHidConnection { @@ -70,6 +72,7 @@ pub struct TockEnv { #[cfg(feature = "vendor_hid")] vendor_connection: TockHidConnection, blink_pattern: usize, + clock: TockClock, } impl TockEnv { @@ -95,6 +98,7 @@ impl TockEnv { endpoint: UsbEndpoint::VendorHid, }, blink_pattern: 0, + clock: TockClock::new(), } } } @@ -115,6 +119,7 @@ impl UserPresence for TockEnv { fn check_init(&mut self) { self.blink_pattern = 0; } + fn wait_with_timeout(&mut self, timeout: Milliseconds) -> UserPresenceResult { if timeout.integer() == 0 { return Err(UserPresenceError::Timeout); @@ -224,6 +229,7 @@ impl Env for TockEnv { type Storage = TockStorage; type KeyStore = Self; type AttestationStore = Self; + type Clock = TockClock; type UpgradeStorage = TockUpgradeStorage; type FirmwareProtection = Self; type Write = Console; @@ -250,6 +256,10 @@ impl Env for TockEnv { self } + fn clock(&mut self) -> &mut Self::Clock { + &mut self.clock + } + fn upgrade_storage(&mut self) -> Option<&mut Self::UpgradeStorage> { self.upgrade_storage.as_mut() } From da9814b56d1749e65c32c6eb8785fad45178cb9c Mon Sep 17 00:00:00 2001 From: Fabian Kaczmarczyck Date: Fri, 24 Feb 2023 10:45:28 +0100 Subject: [PATCH 3/8] 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) } From 99e189c79f014d81b308c36c1deffa55db57a82f Mon Sep 17 00:00:00 2001 From: Fabian Kaczmarczyck Date: Fri, 24 Feb 2023 18:43:59 +0100 Subject: [PATCH 4/8] TockTimer improvements --- src/env/test/mod.rs | 10 +++------- src/env/tock/clock.rs | 21 +++------------------ src/env/tock/mod.rs | 2 +- 3 files changed, 7 insertions(+), 26 deletions(-) diff --git a/src/env/test/mod.rs b/src/env/test/mod.rs index 575e357e..0c48c6df 100644 --- a/src/env/test/mod.rs +++ b/src/env/test/mod.rs @@ -62,16 +62,12 @@ pub struct TestTimer { end: usize, } -#[derive(Debug)] +#[derive(Debug, Default)] pub struct TestClock { cur_time: usize, } impl TestClock { - pub fn new() -> Self { - TestClock { cur_time: 0 } - } - pub fn advance(&mut self, milliseconds: usize) { self.cur_time += milliseconds; } @@ -149,7 +145,7 @@ impl TestEnv { let store = Store::new(storage).ok().unwrap(); let upgrade_storage = Some(BufferUpgradeStorage::new().unwrap()); let customization = DEFAULT_CUSTOMIZATION.into(); - let clock = TestClock::new(); + let clock = TestClock::default(); TestEnv { rng, user_presence, @@ -282,7 +278,7 @@ mod test { #[test] fn test_clock() { - let mut clock = TestClock::new(); + let mut clock = TestClock::default(); let timer = clock.make_timer(3); let timer = clock.check_timer(timer).unwrap(); clock.advance(2); diff --git a/src/env/tock/clock.rs b/src/env/tock/clock.rs index 303be641..587ba47d 100644 --- a/src/env/tock/clock.rs +++ b/src/env/tock/clock.rs @@ -38,41 +38,27 @@ fn wrapping_sub_u24(lhs: usize, rhs: usize) -> usize { lhs.wrapping_sub(rhs) & 0xffffff } +#[derive(Default)] pub struct TockClock { epoch: usize, tick: usize, } -impl TockClock { - pub fn new() -> Self { - TockClock { epoch: 0, tick: 0 } - } -} - impl Clock for TockClock { type Timer = TockTimer; fn make_timer(&mut self, milliseconds: usize) -> Self::Timer { + self.tickle(); let clock_frequency = syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_FREQUENCY, 0, 0) .ok() .unwrap(); - let cur_tick = syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_VALUE, 0, 0) - .ok() - .unwrap(); - // Update self first - if wrapping_sub_u24(self.tick, cur_tick) < 0x80_0000 { - self.epoch += 1; - } - self.tick = cur_tick; - - // Now create the Timer let delta_tick = match milliseconds.checked_mul(clock_frequency) { Some(x) => x / 1000, // All CTAP timeouts are multiples of 100 so far. Worst case we timeout too early. None => (milliseconds / 100).saturating_mul(clock_frequency / 10), }; - let (end_tick, passed_epochs) = add_to_u24_with_wraps(cur_tick, delta_tick); + let (end_tick, passed_epochs) = add_to_u24_with_wraps(self.tick, delta_tick); // Epoch wraps after thousands of years, so we don't mind. let end_epoch = self.epoch + passed_epochs; Self::Timer { @@ -93,7 +79,6 @@ impl Clock for TockClock { /// Elapses timers before the clock wraps. /// /// Call this regularly to timeout reliably despite wrapping clock ticks. - // TODO we implicitly update in the other commands, is this desirable? fn tickle(&mut self) { let cur_tick = syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_VALUE, 0, 0) .ok() diff --git a/src/env/tock/mod.rs b/src/env/tock/mod.rs index 30a99bf8..e0212699 100644 --- a/src/env/tock/mod.rs +++ b/src/env/tock/mod.rs @@ -91,7 +91,7 @@ impl TockEnv { endpoint: UsbEndpoint::VendorHid, }, blink_pattern: 0, - clock: TockClock::new(), + clock: TockClock::default(), } } } From a6a73b634289930bddb906dfc5da9e57dadc0756 Mon Sep 17 00:00:00 2001 From: Fabian Kaczmarczyck Date: Tue, 28 Feb 2023 10:57:35 +0100 Subject: [PATCH 5/8] new Clock interface --- src/api/clock.rs | 20 +++++++++----------- src/ctap/hid/receive.rs | 13 ++++++------- src/ctap/main_hid.rs | 16 ++++++++-------- src/ctap/mod.rs | 11 +++++------ src/ctap/token_state.rs | 11 +++++------ src/ctap/u2f_up.rs | 42 +++++++++++++++++++---------------------- src/env/test/mod.rs | 18 ++++++------------ src/env/tock/clock.rs | 42 ++++++++++++++++++++++------------------- src/lib.rs | 2 -- src/main.rs | 8 ++++---- 10 files changed, 85 insertions(+), 98 deletions(-) diff --git a/src/api/clock.rs b/src/api/clock.rs index 24c65184..b018a47a 100644 --- a/src/api/clock.rs +++ b/src/api/clock.rs @@ -13,21 +13,19 @@ // limitations under the License. pub trait Clock: Sized { - type Timer; + /// Stores data for the clock to recognize if this timer is elapsed or not. + /// + /// The Clock does not keep track of the timers it creates. Therefore, they should not wrap + /// unexpectedly. A timer that is elapsed may never return to a non-elapsed state. + /// + /// A default Timer should return `true` when checked with `is_elapsed`. + type Timer: Default; /// Creates a new timer that expires after the given time in ms. fn make_timer(&mut self, milliseconds: usize) -> Self::Timer; - /// Consumes a timer, and returns it if not expired. - fn check_timer(&mut self, timer: Self::Timer) -> Option; - - /// Checks the timer and takes it, if expired. - fn update_timer(&mut self, timer: &mut Option) { - *timer = timer.take().and_then(|t| Self::check_timer(self, t)); - } - - /// Tickles the clock and makes it check its timers. Often a NOOP. - fn tickle(&mut self); + /// Checks whether a given timer is expired. + fn is_elapsed(&mut self, timer: &Self::Timer) -> bool; /// Timestamp in microseconds. /// diff --git a/src/ctap/hid/receive.rs b/src/ctap/hid/receive.rs index 24d88a1b..30bbbdf1 100644 --- a/src/ctap/hid/receive.rs +++ b/src/ctap/hid/receive.rs @@ -31,7 +31,7 @@ pub struct MessageAssembler { // Current channel ID. cid: ChannelID, // Timestamp of the last packet received on the current channel. - timer: Option<<::Clock as Clock>::Timer>, + timer: <::Clock as Clock>::Timer, // Current command. cmd: u8, // Sequence number expected for the next packet. @@ -47,7 +47,7 @@ impl MessageAssembler { MessageAssembler { idle: true, cid: [0, 0, 0, 0], - timer: None, + timer: <::Clock as Clock>::Timer::default(), 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.timer = None; + self.timer = <::Clock as Clock>::Timer::default(); self.cmd = 0; self.seq = 0; self.remaining_payload_len = 0; @@ -81,8 +81,7 @@ impl MessageAssembler { // section 8.8.1 let (cid, processed_packet) = CtapHid::::process_single_packet(packet); - env.clock().update_timer(&mut self.timer); - if !self.idle && self.timer.is_none() { + if !self.idle && env.clock().is_elapsed(&self.timer) { // The current channel timed out. // Save the channel ID and reset the state. let current_cid = self.cid; @@ -133,7 +132,7 @@ impl MessageAssembler { Err((cid, CtapHidError::InvalidSeq)) } else { // Update the last timestamp. - self.timer = Some(env.clock().make_timer(TIMEOUT_DURATION)); + self.timer = env.clock().make_timer(TIMEOUT_DURATION); // Increment the sequence number for the next packet. self.seq += 1; Ok(self.append_payload(data)) @@ -157,7 +156,7 @@ impl MessageAssembler { return Err((cid, CtapHidError::InvalidLen)); } self.cid = cid; - self.timer = Some(env.clock().make_timer(TIMEOUT_DURATION)); + self.timer = env.clock().make_timer(TIMEOUT_DURATION); self.cmd = cmd; self.seq = 0; self.remaining_payload_len = len; diff --git a/src/ctap/main_hid.rs b/src/ctap/main_hid.rs index f99c05da..7a2b7ba1 100644 --- a/src/ctap/main_hid.rs +++ b/src/ctap/main_hid.rs @@ -28,7 +28,7 @@ const WINK_TIMEOUT_DURATION: usize = 5000; /// Implements the standard CTAP command processing for HID. pub struct MainHid { hid: CtapHid, - wink_permission: Option<<::Clock as Clock>::Timer>, + wink_permission: <::Clock as Clock>::Timer, } impl MainHid { @@ -42,7 +42,7 @@ impl MainHid { | CtapHid::::CAPABILITY_NMSG; let hid = CtapHid::::new(capabilities); - let wink_permission = None; + let wink_permission = <::Clock as Clock>::Timer::default(); MainHid { hid, wink_permission, @@ -73,7 +73,7 @@ impl MainHid { ctap_state: &mut CtapState, ) -> Message { // If another command arrives, stop winking to prevent accidential button touches. - self.wink_permission = None; + self.wink_permission = <::Clock as Clock>::Timer::default(); let cid = message.cid; match message.cmd { @@ -105,7 +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 = Some(env.clock().make_timer(WINK_TIMEOUT_DURATION)); + self.wink_permission = env.clock().make_timer(WINK_TIMEOUT_DURATION); // The response is empty like the request. message } else { @@ -118,9 +118,8 @@ impl MainHid { } /// Returns whether a wink permission is currently granted. - pub fn should_wink(&mut self, env: &mut E) -> bool { - env.clock().update_timer(&mut self.wink_permission); - self.wink_permission.is_some() + pub fn should_wink(&self, env: &mut E) -> bool { + !env.clock().is_elapsed(&self.wink_permission) } #[cfg(feature = "with_ctap1")] @@ -154,10 +153,11 @@ mod test { fn new_initialized() -> (MainHid, ChannelID) { let (hid, cid) = CtapHid::new_initialized(); + let wink_permission = <::Clock as Clock>::Timer::default(); ( MainHid:: { hid, - wink_permission: None, + wink_permission, }, cid, ) diff --git a/src/ctap/mod.rs b/src/ctap/mod.rs index 4c26a1ae..b1a7e57f 100644 --- a/src/ctap/mod.rs +++ b/src/ctap/mod.rs @@ -360,7 +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 { - permission: Option<<::Clock as Clock>::Timer>, + permission: <::Clock as Clock>::Timer, command_type: Option, channel: Option, } @@ -372,7 +372,7 @@ impl StatefulPermission { /// means allowing Reset, and Reset cannot be granted later. pub fn new_reset(env: &mut E) -> StatefulPermission { StatefulPermission { - permission: Some(env.clock().make_timer(RESET_TIMEOUT_DURATION)), + permission: env.clock().make_timer(RESET_TIMEOUT_DURATION), command_type: Some(StatefulCommand::Reset), channel: None, } @@ -380,7 +380,7 @@ impl StatefulPermission { /// Clears all permissions and state. pub fn clear(&mut self) { - self.permission = None; + self.permission = <::Clock as Clock>::Timer::default(); self.command_type = None; self.channel = None; } @@ -406,8 +406,7 @@ impl StatefulPermission { /// Clears all state if the permission timed out. pub fn clear_timer(&mut self, env: &mut E) { - env.clock().update_timer(&mut self.permission); - if self.permission.is_none() { + if env.clock().is_elapsed(&self.permission) { self.clear(); } } @@ -430,7 +429,7 @@ impl StatefulPermission { // Reset is only allowed after a power cycle. StatefulCommand::Reset => unreachable!(), _ => { - self.permission = Some(env.clock().make_timer(STATEFUL_COMMAND_TIMEOUT_DURATION)); + self.permission = env.clock().make_timer(STATEFUL_COMMAND_TIMEOUT_DURATION); self.command_type = Some(new_command_type); self.channel = Some(channel); } diff --git a/src/ctap/token_state.rs b/src/ctap/token_state.rs index 2cade02c..28b36f22 100644 --- a/src/ctap/token_state.rs +++ b/src/ctap/token_state.rs @@ -37,7 +37,7 @@ pub struct PinUvAuthTokenState { // Relies on the fact that all permissions are represented by powers of two. permissions_set: u8, permissions_rp_id: Option, - usage_timer: Option<<::Clock as Clock>::Timer>, + usage_timer: <::Clock as Clock>::Timer, user_verified: bool, in_use: bool, } @@ -48,7 +48,7 @@ impl PinUvAuthTokenState { PinUvAuthTokenState { permissions_set: 0, permissions_rp_id: None, - usage_timer: None, + usage_timer: <::Clock as Clock>::Timer::default(), user_verified: false, in_use: false, } @@ -113,7 +113,7 @@ impl PinUvAuthTokenState { /// Starts the timer for pinUvAuthToken usage. pub fn begin_using_pin_uv_auth_token(&mut self, env: &mut E) { self.user_verified = true; - self.usage_timer = Some(env.clock().make_timer(INITIAL_USAGE_TIME_LIMIT)); + self.usage_timer = env.clock().make_timer(INITIAL_USAGE_TIME_LIMIT); self.in_use = true; } @@ -122,8 +122,7 @@ impl PinUvAuthTokenState { if !self.in_use { return; } - env.clock().update_timer(&mut self.usage_timer); - if self.usage_timer.is_none() { + if env.clock().is_elapsed(&self.usage_timer) { self.stop_using_pin_uv_auth_token(); } } @@ -147,7 +146,7 @@ impl PinUvAuthTokenState { pub fn stop_using_pin_uv_auth_token(&mut self) { self.permissions_rp_id = None; self.permissions_set = 0; - self.usage_timer = None; + self.usage_timer = <::Clock as Clock>::Timer::default(); self.user_verified = false; self.in_use = false; } diff --git a/src/ctap/u2f_up.rs b/src/ctap/u2f_up.rs index 0dde2b14..69e2e2ad 100644 --- a/src/ctap/u2f_up.rs +++ b/src/ctap/u2f_up.rs @@ -20,51 +20,47 @@ 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>, + needs_up: <::Clock as Clock>::Timer, /// Button touch timeouts, while user presence is requested, are saved here. - has_up: Option<<::Clock as Clock>::Timer>, + has_up: <::Clock as Clock>::Timer, } impl U2fUserPresenceState { pub fn new() -> U2fUserPresenceState { U2fUserPresenceState { - needs_up: None, - has_up: None, + needs_up: <::Clock as Clock>::Timer::default(), + has_up: <::Clock as Clock>::Timer::default(), } } - // Granting user presence is ignored if it needs activation, but waits. Also cleans up. + /// Allows consuming user presence until timeout, if it was needed. + /// + /// If user presence was not requested, granting user presence does nothing. pub fn grant_up(&mut self, env: &mut E) { - self.check_expiration(env); - if self.needs_up.is_some() { - self.needs_up = None; - self.has_up = Some(env.clock().make_timer(TOUCH_TIMEOUT)); + if !env.clock().is_elapsed(&self.needs_up) { + self.needs_up = <::Clock as Clock>::Timer::default(); + self.has_up = env.clock().make_timer(TOUCH_TIMEOUT); } } - // This marks user presence as needed or uses it up if already granted. Also cleans up. + /// Returns whether user presence was granted within the timeout and not yet consumed. pub fn consume_up(&mut self, env: &mut E) -> bool { - self.check_expiration(env); - if self.has_up.is_some() { - self.has_up = None; + if !env.clock().is_elapsed(&self.has_up) { + self.has_up = <::Clock as Clock>::Timer::default(); true } else { - self.needs_up = Some(env.clock().make_timer(U2F_UP_PROMPT_TIMEOUT)); + self.needs_up = env.clock().make_timer(U2F_UP_PROMPT_TIMEOUT); false } } - // Returns if user presence was requested. Also cleans up. + /// Returns whether user presence was requested. + /// + /// This function doesn't represent interaction with the environment, and does not change the + /// state, i.e. neither grants nor consumes user presence. pub fn is_up_needed(&mut self, env: &mut E) -> bool { - 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); + !env.clock().is_elapsed(&self.needs_up) } } diff --git a/src/env/test/mod.rs b/src/env/test/mod.rs index 0c48c6df..d59f5ff2 100644 --- a/src/env/test/mod.rs +++ b/src/env/test/mod.rs @@ -57,7 +57,7 @@ impl Rng256 for TestRng256 { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, Default, PartialEq)] pub struct TestTimer { end: usize, } @@ -82,16 +82,10 @@ impl Clock for TestClock { } } - fn check_timer(&mut self, timer: Self::Timer) -> Option { - if self.cur_time < timer.end { - Some(timer) - } else { - None - } + fn is_elapsed(&mut self, timer: &Self::Timer) -> bool { + self.cur_time >= timer.end } - fn tickle(&mut self) {} - #[cfg(feature = "debug_ctap")] fn timestamp_us(&mut self) -> usize { // Unused, but let's implement something because it's easy. @@ -280,10 +274,10 @@ mod test { fn test_clock() { let mut clock = TestClock::default(); let timer = clock.make_timer(3); - let timer = clock.check_timer(timer).unwrap(); + assert!(!clock.is_elapsed(&timer)); clock.advance(2); - let timer = clock.check_timer(timer).unwrap(); + assert!(!clock.is_elapsed(&timer)); clock.advance(1); - assert_eq!(clock.check_timer(timer), None); + assert!(clock.is_elapsed(&timer)); } } diff --git a/src/env/tock/clock.rs b/src/env/tock/clock.rs index 587ba47d..0a500bfd 100644 --- a/src/env/tock/clock.rs +++ b/src/env/tock/clock.rs @@ -22,6 +22,7 @@ mod command_nr { const DRIVER_NUMBER: usize = 0x00000; +#[derive(Default)] pub struct TockTimer { end_epoch: usize, end_tick: usize, @@ -38,12 +39,32 @@ fn wrapping_sub_u24(lhs: usize, rhs: usize) -> usize { lhs.wrapping_sub(rhs) & 0xffffff } +/// Clock that produces timers through Tock syscalls. +/// +/// To guarantee correctness, you have to call any of its functions at least once per full tick +/// counter wrap. In our case, 24 bit ticks with a 32 kHz frequency wrap after 512 seconds. If you +/// can't guarantee to regularly create or check timers, call tickle at least every 8 minutes. #[derive(Default)] pub struct TockClock { epoch: usize, tick: usize, } +impl TockClock { + /// Elapses timers before the clock wraps. + /// + /// Call this regularly to timeout reliably despite wrapping clock ticks. + pub fn tickle(&mut self) { + let cur_tick = syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_VALUE, 0, 0) + .ok() + .unwrap(); + if wrapping_sub_u24(self.tick, cur_tick) < 0x80_0000 { + self.epoch += 1; + } + self.tick = cur_tick; + } +} + impl Clock for TockClock { type Timer = TockTimer; @@ -67,26 +88,9 @@ impl Clock for TockClock { } } - fn check_timer(&mut self, timer: Self::Timer) -> Option { + fn is_elapsed(&mut self, timer: &Self::Timer) -> bool { self.tickle(); - if (self.epoch, self.tick) < (timer.end_epoch, timer.end_tick) { - Some(timer) - } else { - None - } - } - - /// Elapses timers before the clock wraps. - /// - /// Call this regularly to timeout reliably despite wrapping clock ticks. - fn tickle(&mut self) { - let cur_tick = syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_VALUE, 0, 0) - .ok() - .unwrap(); - if wrapping_sub_u24(self.tick, cur_tick) < 0x80_0000 { - self.epoch += 1; - } - self.tick = cur_tick; + (self.epoch, self.tick) >= (timer.end_epoch, timer.end_tick) } #[cfg(feature = "debug_ctap")] diff --git a/src/lib.rs b/src/lib.rs index a75cebd2..a04183e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,6 @@ 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")] @@ -109,7 +108,6 @@ impl Ctap { } pub fn update_timeouts(&mut self) { - self.env.clock().tickle(); self.state.update_timeouts(&mut self.env); } diff --git a/src/main.rs b/src/main.rs index 2d2b6fa3..b88405b4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -118,7 +118,7 @@ fn main() { let mut ctap = ctap2::Ctap::new(env); let mut led_counter = 0; - let mut led_blink_timer = None; + let mut led_blink_timer = <::Clock as Clock>::Timer::default(); let mut replies = EndpointReplies::new(); @@ -216,6 +216,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(); + ctap.env().clock().tickle(); if let Some(endpoint) = usb_endpoint { let transport = match endpoint { @@ -244,12 +245,11 @@ fn main() { } } - ctap.env().clock().update_timer(&mut led_blink_timer); - if led_blink_timer.is_none() { + if ctap.env().clock().is_elapsed(&led_blink_timer) { // Loops quickly when waiting for U2F user presence, so the next LED blink // state is only set if enough time has elapsed. led_counter += 1; - led_blink_timer = Some(ctap.env().clock().make_timer(KEEPALIVE_DELAY)) + led_blink_timer = ctap.env().clock().make_timer(KEEPALIVE_DELAY) } if ctap.should_wink() { From b61a94b22858fe9122121a86b71ba6877eddd0de Mon Sep 17 00:00:00 2001 From: Fabian Kaczmarczyck Date: Tue, 28 Feb 2023 12:13:00 +0100 Subject: [PATCH 6/8] addressed comments --- src/api/clock.rs | 4 ++++ src/ctap/hid/receive.rs | 6 +++--- src/ctap/main_hid.rs | 6 +++--- src/ctap/mod.rs | 4 ++-- src/ctap/token_state.rs | 6 +++--- src/ctap/u2f_up.rs | 12 ++++++------ src/env/tock/clock.rs | 17 +---------------- 7 files changed, 22 insertions(+), 33 deletions(-) diff --git a/src/api/clock.rs b/src/api/clock.rs index b018a47a..f65cdef3 100644 --- a/src/api/clock.rs +++ b/src/api/clock.rs @@ -25,6 +25,10 @@ pub trait Clock: Sized { fn make_timer(&mut self, milliseconds: usize) -> Self::Timer; /// Checks whether a given timer is expired. + /// + /// Until a timer expires, this function consistently returns false. Once it expires, this + /// function consistently returns true. In particular, it is valid to continue calling this + /// function after the first time it returns true. fn is_elapsed(&mut self, timer: &Self::Timer) -> bool; /// Timestamp in microseconds. diff --git a/src/ctap/hid/receive.rs b/src/ctap/hid/receive.rs index 30bbbdf1..a587b9c3 100644 --- a/src/ctap/hid/receive.rs +++ b/src/ctap/hid/receive.rs @@ -31,7 +31,7 @@ pub struct MessageAssembler { // Current channel ID. cid: ChannelID, // Timestamp of the last packet received on the current channel. - timer: <::Clock as Clock>::Timer, + timer: ::Timer, // Current command. cmd: u8, // Sequence number expected for the next packet. @@ -47,7 +47,7 @@ impl MessageAssembler { MessageAssembler { idle: true, cid: [0, 0, 0, 0], - timer: <::Clock as Clock>::Timer::default(), + timer: ::Timer::default(), 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.timer = <::Clock as Clock>::Timer::default(); + self.timer = ::Timer::default(); self.cmd = 0; self.seq = 0; self.remaining_payload_len = 0; diff --git a/src/ctap/main_hid.rs b/src/ctap/main_hid.rs index 7a2b7ba1..b7bc0271 100644 --- a/src/ctap/main_hid.rs +++ b/src/ctap/main_hid.rs @@ -28,7 +28,7 @@ const WINK_TIMEOUT_DURATION: usize = 5000; /// Implements the standard CTAP command processing for HID. pub struct MainHid { hid: CtapHid, - wink_permission: <::Clock as Clock>::Timer, + wink_permission: ::Timer, } impl MainHid { @@ -42,7 +42,7 @@ impl MainHid { | CtapHid::::CAPABILITY_NMSG; let hid = CtapHid::::new(capabilities); - let wink_permission = <::Clock as Clock>::Timer::default(); + let wink_permission = ::Timer::default(); MainHid { hid, wink_permission, @@ -73,7 +73,7 @@ impl MainHid { ctap_state: &mut CtapState, ) -> Message { // If another command arrives, stop winking to prevent accidential button touches. - self.wink_permission = <::Clock as Clock>::Timer::default(); + self.wink_permission = ::Timer::default(); let cid = message.cid; match message.cmd { diff --git a/src/ctap/mod.rs b/src/ctap/mod.rs index b1a7e57f..ddab6bc3 100644 --- a/src/ctap/mod.rs +++ b/src/ctap/mod.rs @@ -360,7 +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 { - permission: <::Clock as Clock>::Timer, + permission: ::Timer, command_type: Option, channel: Option, } @@ -380,7 +380,7 @@ impl StatefulPermission { /// Clears all permissions and state. pub fn clear(&mut self) { - self.permission = <::Clock as Clock>::Timer::default(); + self.permission = ::Timer::default(); self.command_type = None; self.channel = None; } diff --git a/src/ctap/token_state.rs b/src/ctap/token_state.rs index 28b36f22..112fc2ee 100644 --- a/src/ctap/token_state.rs +++ b/src/ctap/token_state.rs @@ -37,7 +37,7 @@ pub struct PinUvAuthTokenState { // Relies on the fact that all permissions are represented by powers of two. permissions_set: u8, permissions_rp_id: Option, - usage_timer: <::Clock as Clock>::Timer, + usage_timer: ::Timer, user_verified: bool, in_use: bool, } @@ -48,7 +48,7 @@ impl PinUvAuthTokenState { PinUvAuthTokenState { permissions_set: 0, permissions_rp_id: None, - usage_timer: <::Clock as Clock>::Timer::default(), + usage_timer: ::Timer::default(), user_verified: false, in_use: false, } @@ -146,7 +146,7 @@ impl PinUvAuthTokenState { pub fn stop_using_pin_uv_auth_token(&mut self) { self.permissions_rp_id = None; self.permissions_set = 0; - self.usage_timer = <::Clock as Clock>::Timer::default(); + self.usage_timer = ::Timer::default(); self.user_verified = false; self.in_use = false; } diff --git a/src/ctap/u2f_up.rs b/src/ctap/u2f_up.rs index 69e2e2ad..6800aa58 100644 --- a/src/ctap/u2f_up.rs +++ b/src/ctap/u2f_up.rs @@ -20,17 +20,17 @@ const U2F_UP_PROMPT_TIMEOUT: usize = 10000; pub struct U2fUserPresenceState { /// If user presence was recently requested, its timeout is saved here. - needs_up: <::Clock as Clock>::Timer, + needs_up: ::Timer, /// Button touch timeouts, while user presence is requested, are saved here. - has_up: <::Clock as Clock>::Timer, + has_up: ::Timer, } impl U2fUserPresenceState { pub fn new() -> U2fUserPresenceState { U2fUserPresenceState { - needs_up: <::Clock as Clock>::Timer::default(), - has_up: <::Clock as Clock>::Timer::default(), + needs_up: ::Timer::default(), + has_up: ::Timer::default(), } } @@ -39,7 +39,7 @@ impl U2fUserPresenceState { /// If user presence was not requested, granting user presence does nothing. pub fn grant_up(&mut self, env: &mut E) { if !env.clock().is_elapsed(&self.needs_up) { - self.needs_up = <::Clock as Clock>::Timer::default(); + self.needs_up = ::Timer::default(); self.has_up = env.clock().make_timer(TOUCH_TIMEOUT); } } @@ -47,7 +47,7 @@ impl U2fUserPresenceState { /// Returns whether user presence was granted within the timeout and not yet consumed. pub fn consume_up(&mut self, env: &mut E) -> bool { if !env.clock().is_elapsed(&self.has_up) { - self.has_up = <::Clock as Clock>::Timer::default(); + self.has_up = ::Timer::default(); true } else { self.needs_up = env.clock().make_timer(U2F_UP_PROMPT_TIMEOUT); diff --git a/src/env/tock/clock.rs b/src/env/tock/clock.rs index 0a500bfd..10a60963 100644 --- a/src/env/tock/clock.rs +++ b/src/env/tock/clock.rs @@ -35,10 +35,6 @@ fn add_to_u24_with_wraps(lhs: usize, rhs: usize) -> (usize, usize) { (sum & 0xffffff, sum >> 24) } -fn wrapping_sub_u24(lhs: usize, rhs: usize) -> usize { - lhs.wrapping_sub(rhs) & 0xffffff -} - /// Clock that produces timers through Tock syscalls. /// /// To guarantee correctness, you have to call any of its functions at least once per full tick @@ -58,7 +54,7 @@ impl TockClock { let cur_tick = syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_VALUE, 0, 0) .ok() .unwrap(); - if wrapping_sub_u24(self.tick, cur_tick) < 0x80_0000 { + if cur_tick < self.tick { self.epoch += 1; } self.tick = cur_tick; @@ -121,15 +117,4 @@ mod test { assert_eq!(add_to_u24_with_wraps(0, 0x2000000), (0, 2)); assert_eq!(add_to_u24_with_wraps(0xffffff, 0x2ffffff), (0xfffffe, 3)); } - - #[test] - fn test_wrapping_sub_u24() { - // non-wrapping cases - assert_eq!(wrapping_sub_u24(0, 0), 0); - assert_eq!(wrapping_sub_u24(0xffffff, 0), 0xffffff); - assert_eq!(wrapping_sub_u24(0xffffff, 0xffffff), 0); - // wrapping cases - assert_eq!(wrapping_sub_u24(0, 0xffffff), 1); - assert_eq!(wrapping_sub_u24(0, 1), 0xffffff); - } } From e2f12b7060676b749c695b4b065a0d1d10574233 Mon Sep 17 00:00:00 2001 From: Fabian Kaczmarczyck Date: Tue, 28 Feb 2023 16:37:43 +0100 Subject: [PATCH 7/8] renames constants to milliseconds, other style fixes --- src/api/clock.rs | 2 +- src/api/connection.rs | 2 +- src/api/user_presence.rs | 2 +- src/ctap/client_pin.rs | 5 +- src/ctap/credential_management.rs | 23 +++--- src/ctap/ctap1.rs | 8 +- src/ctap/hid/mod.rs | 4 +- src/ctap/hid/receive.rs | 12 +-- src/ctap/main_hid.rs | 8 +- src/ctap/mod.rs | 34 ++++---- src/ctap/token_state.rs | 6 +- src/ctap/u2f_up.rs | 12 +-- src/env/test/mod.rs | 17 ++-- src/env/tock/clock.rs | 100 +++++++++++------------ src/env/tock/mod.rs | 10 +-- src/main.rs | 12 +-- third_party/libtock-drivers/src/timer.rs | 18 ++++ 17 files changed, 146 insertions(+), 129 deletions(-) diff --git a/src/api/clock.rs b/src/api/clock.rs index f65cdef3..7cefd167 100644 --- a/src/api/clock.rs +++ b/src/api/clock.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -pub trait Clock: Sized { +pub trait Clock { /// Stores data for the clock to recognize if this timer is elapsed or not. /// /// The Clock does not keep track of the timers it creates. Therefore, they should not wrap diff --git a/src/api/connection.rs b/src/api/connection.rs index d0bbcb6a..4d909b9f 100644 --- a/src/api/connection.rs +++ b/src/api/connection.rs @@ -25,5 +25,5 @@ pub struct SendOrRecvError; pub type SendOrRecvResult = Result; pub trait HidConnection { - fn send_and_maybe_recv(&mut self, buf: &mut [u8; 64], timeout: usize) -> SendOrRecvResult; + fn send_and_maybe_recv(&mut self, buf: &mut [u8; 64], timeout_ms: usize) -> SendOrRecvResult; } diff --git a/src/api/user_presence.rs b/src/api/user_presence.rs index 42296d78..72531937 100644 --- a/src/api/user_presence.rs +++ b/src/api/user_presence.rs @@ -33,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: usize) -> UserPresenceResult; + fn wait_with_timeout(&mut self, timeout_ms: usize) -> UserPresenceResult; /// Finalizes a user presence check. /// diff --git a/src/ctap/client_pin.rs b/src/ctap/client_pin.rs index 4e580112..8cfca6ea 100644 --- a/src/ctap/client_pin.rs +++ b/src/ctap/client_pin.rs @@ -1,4 +1,5 @@ // 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 @@ -116,7 +117,7 @@ impl ClientPin { pin_protocol_v1: PinProtocol::new(env.rng()), pin_protocol_v2: PinProtocol::new(env.rng()), consecutive_pin_mismatches: 0, - pin_uv_auth_token_state: PinUvAuthTokenState::::new(), + pin_uv_auth_token_state: PinUvAuthTokenState::new(), } } @@ -565,7 +566,7 @@ impl ClientPin { PinUvAuthProtocol::V1 => (key_agreement_key, crypto::ecdh::SecKey::gensk(env.rng())), PinUvAuthProtocol::V2 => (crypto::ecdh::SecKey::gensk(env.rng()), key_agreement_key), }; - let mut pin_uv_auth_token_state = PinUvAuthTokenState::::new(); + 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(env); Self { diff --git a/src/ctap/credential_management.rs b/src/ctap/credential_management.rs index 8c192a75..f560ee1e 100644 --- a/src/ctap/credential_management.rs +++ b/src/ctap/credential_management.rs @@ -228,7 +228,7 @@ fn process_delete_credential( .credential_id .ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)? .key_id; - check_rp_id_permissions::(env, client_pin, &credential_id)?; + check_rp_id_permissions(env, client_pin, &credential_id)?; storage::delete_credential(env, &credential_id) } @@ -245,7 +245,7 @@ fn process_update_user_information( let user = sub_command_params .user .ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?; - check_rp_id_permissions::(env, client_pin, &credential_id)?; + check_rp_id_permissions(env, client_pin, &credential_id)?; storage::update_credential(env, &credential_id, user) } @@ -311,17 +311,17 @@ pub fn process_credential_management( } CredentialManagementSubCommand::EnumerateRpsBegin => { client_pin.has_no_rp_id_permission()?; - Some(process_enumerate_rps_begin::( + Some(process_enumerate_rps_begin( env, stateful_command_permission, channel, )?) } CredentialManagementSubCommand::EnumerateRpsGetNextRp => Some( - process_enumerate_rps_get_next_rp::(env, stateful_command_permission)?, + process_enumerate_rps_get_next_rp(env, stateful_command_permission)?, ), CredentialManagementSubCommand::EnumerateCredentialsBegin => { - Some(process_enumerate_credentials_begin::( + Some(process_enumerate_credentials_begin( env, stateful_command_permission, client_pin, @@ -329,14 +329,11 @@ pub fn process_credential_management( channel, )?) } - CredentialManagementSubCommand::EnumerateCredentialsGetNextCredential => { - Some(process_enumerate_credentials_get_next_credential::( - env, - stateful_command_permission, - )?) - } + CredentialManagementSubCommand::EnumerateCredentialsGetNextCredential => Some( + process_enumerate_credentials_get_next_credential(env, stateful_command_permission)?, + ), CredentialManagementSubCommand::DeleteCredential => { - process_delete_credential::( + process_delete_credential( env, client_pin, sub_command_params.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?, @@ -344,7 +341,7 @@ pub fn process_credential_management( None } CredentialManagementSubCommand::UpdateUserInformation => { - process_update_user_information::( + process_update_user_information( env, client_pin, sub_command_params.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?, diff --git a/src/ctap/ctap1.rs b/src/ctap/ctap1.rs index fcdc9361..ae80eddc 100644 --- a/src/ctap/ctap1.rs +++ b/src/ctap/ctap1.rs @@ -342,7 +342,7 @@ 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::super::TOUCH_TIMEOUT_MS; use super::*; use crate::api::customization::Customization; use crate::ctap::storage; @@ -470,7 +470,7 @@ mod test { ctap_state.u2f_up_state.consume_up(&mut env); ctap_state.u2f_up_state.grant_up(&mut env); - env.clock().advance(TOUCH_TIMEOUT); + env.clock().advance(TOUCH_TIMEOUT_MS); let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state); assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED)); } @@ -652,7 +652,7 @@ mod test { &key_handle, ); - env.clock().advance(TOUCH_TIMEOUT); + env.clock().advance(TOUCH_TIMEOUT_MS); let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state).unwrap(); assert_eq!(response[0], 0x01); let global_signature_counter = storage::global_signature_counter(&mut env).unwrap(); @@ -695,7 +695,7 @@ mod test { ctap_state.u2f_up_state.consume_up(&mut env); ctap_state.u2f_up_state.grant_up(&mut env); - env.clock().advance(TOUCH_TIMEOUT); + env.clock().advance(TOUCH_TIMEOUT_MS); let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state); assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED)); } diff --git a/src/ctap/hid/mod.rs b/src/ctap/hid/mod.rs index 92b69179..5d329037 100644 --- a/src/ctap/hid/mod.rs +++ b/src/ctap/hid/mod.rs @@ -200,7 +200,7 @@ impl CtapHid { /// The capabilities passed in are reported to the client in Init. pub fn new(capabilities: u8) -> CtapHid { Self { - assembler: MessageAssembler::::new(), + assembler: MessageAssembler::new(), allocated_cids: 0, capabilities, } @@ -396,7 +396,7 @@ impl CtapHid { pub fn new_initialized() -> (Self, ChannelID) { ( Self { - assembler: MessageAssembler::::new(), + assembler: MessageAssembler::new(), allocated_cids: 1, capabilities: 0x0D, }, diff --git a/src/ctap/hid/receive.rs b/src/ctap/hid/receive.rs index a587b9c3..cbd824df 100644 --- a/src/ctap/hid/receive.rs +++ b/src/ctap/hid/receive.rs @@ -22,7 +22,7 @@ use alloc::vec::Vec; use core::mem::swap; // TODO: Is this timeout duration specified? -const TIMEOUT_DURATION: usize = 100; +const TIMEOUT_DURATION_MS: usize = 100; /// A structure to assemble CTAPHID commands from a series of incoming USB HID packets. pub struct MessageAssembler { @@ -132,7 +132,7 @@ impl MessageAssembler { Err((cid, CtapHidError::InvalidSeq)) } else { // Update the last timestamp. - self.timer = env.clock().make_timer(TIMEOUT_DURATION); + self.timer = env.clock().make_timer(TIMEOUT_DURATION_MS); // Increment the sequence number for the next packet. self.seq += 1; Ok(self.append_payload(data)) @@ -156,7 +156,7 @@ impl MessageAssembler { return Err((cid, CtapHidError::InvalidLen)); } self.cid = cid; - self.timer = env.clock().make_timer(TIMEOUT_DURATION); + self.timer = env.clock().make_timer(TIMEOUT_DURATION_MS); self.cmd = cmd; self.seq = 0; self.remaining_payload_len = len; @@ -210,7 +210,7 @@ mod test { let mut env = TestEnv::new(); let mut assembler = MessageAssembler::new(); assert_eq!( - assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x90]),), + assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x90])), Ok(Some(Message { cid: [0x12, 0x34, 0x56, 0x78], cmd: CtapHidCommand::Cbor, @@ -520,7 +520,7 @@ mod test { ), Ok(None) ); - env.clock().advance(TIMEOUT_DURATION); + env.clock().advance(TIMEOUT_DURATION_MS); assert_eq!( assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]),), Err(([0x12, 0x34, 0x56, 0x78], CtapHidError::MsgTimeout)) @@ -531,7 +531,7 @@ mod test { fn test_just_in_time_packets() { let mut env = TestEnv::new(); // Delay between each packet is just below the threshold. - let delay = TIMEOUT_DURATION - 1; + let delay = TIMEOUT_DURATION_MS - 1; let mut assembler = MessageAssembler::new(); assert_eq!( diff --git a/src/ctap/main_hid.rs b/src/ctap/main_hid.rs index b7bc0271..d149fb41 100644 --- a/src/ctap/main_hid.rs +++ b/src/ctap/main_hid.rs @@ -23,7 +23,7 @@ use crate::ctap::hid::{ use crate::ctap::{Channel, CtapState}; use crate::env::Env; -const WINK_TIMEOUT_DURATION: usize = 5000; +const WINK_TIMEOUT_DURATION_MS: usize = 5000; /// Implements the standard CTAP command processing for HID. pub struct MainHid { @@ -41,7 +41,7 @@ impl MainHid { | CtapHid::::CAPABILITY_CBOR | CtapHid::::CAPABILITY_NMSG; - let hid = CtapHid::::new(capabilities); + let hid = CtapHid::new(capabilities); let wink_permission = ::Timer::default(); MainHid { hid, @@ -105,7 +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 = env.clock().make_timer(WINK_TIMEOUT_DURATION); + self.wink_permission = env.clock().make_timer(WINK_TIMEOUT_DURATION_MS); // The response is empty like the request. message } else { @@ -207,7 +207,7 @@ mod test { assert_eq!(response.next(), Some(wink_packet)); assert_eq!(response.next(), None); assert!(main_hid.should_wink(&mut env)); - env.clock().advance(WINK_TIMEOUT_DURATION); + env.clock().advance(WINK_TIMEOUT_DURATION_MS); assert!(!main_hid.should_wink(&mut env)); } } diff --git a/src/ctap/mod.rs b/src/ctap/mod.rs index ddab6bc3..7ec0a470 100644 --- a/src/ctap/mod.rs +++ b/src/ctap/mod.rs @@ -98,11 +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 KEEPALIVE_DELAY: usize = 100; -pub const TOUCH_TIMEOUT: usize = 30000; +pub const KEEPALIVE_DELAY_MS: usize = 100; +pub const TOUCH_TIMEOUT_MS: usize = 30000; // TODO(kaczmarczyck) 2.1 allows Reset after Reset and 15 seconds? -const RESET_TIMEOUT_DURATION: usize = 10000; -const STATEFUL_COMMAND_TIMEOUT_DURATION: usize = 30000; +const RESET_TIMEOUT_DURATION_MS: usize = 10000; +const STATEFUL_COMMAND_TIMEOUT_DURATION_MS: usize = 30000; pub const FIDO2_VERSION_STRING: &str = "FIDO_2_0"; #[cfg(feature = "with_ctap1")] @@ -208,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: usize, + timeout_ms: usize, ) -> Result<(), UserPresenceError> { let (cid, transport) = match channel { Channel::MainHid(cid) => (cid, Transport::MainHid), @@ -218,7 +218,7 @@ fn send_keepalive_up_needed( let keepalive_msg = CtapHid::::keepalive(cid, KeepaliveStatus::UpNeeded); for mut pkt in keepalive_msg { let ctap_hid_connection = transport.hid_connection(env); - match ctap_hid_connection.send_and_maybe_recv(&mut pkt, timeout) { + match ctap_hid_connection.send_and_maybe_recv(&mut pkt, timeout_ms) { Ok(SendOrRecvStatus::Timeout) => { debug_ctap!(env, "Sending a KEEPALIVE packet timed out"); // TODO: abort user presence test? @@ -288,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 / KEEPALIVE_DELAY; + const TIMEOUT_ITERATIONS: usize = TOUCH_TIMEOUT_MS / KEEPALIVE_DELAY_MS; // All fallible functions are called without '?' operator to always reach // check_complete(...) cleanup function. @@ -299,7 +299,7 @@ fn check_user_presence(env: &mut E, channel: Channel) -> Result<(), Ctap // user presence check result immediately to client, without sending any keepalive packets. result = env .user_presence() - .wait_with_timeout(if i == 0 { 0 } else { KEEPALIVE_DELAY }); + .wait_with_timeout(if i == 0 { 0 } else { KEEPALIVE_DELAY_MS }); if !matches!(result, Err(UserPresenceError::Timeout)) { break; } @@ -307,7 +307,7 @@ fn check_user_presence(env: &mut E, channel: Channel) -> Result<(), Ctap // accordingly, so that all wait_with_timeout invocations are separated by // equal time intervals. That way token indicators, such as LEDs, will blink // with a consistent pattern. - let keepalive_result = send_keepalive_up_needed(env, channel, KEEPALIVE_DELAY); + let keepalive_result = send_keepalive_up_needed(env, channel, KEEPALIVE_DELAY_MS); if keepalive_result.is_err() { debug_ctap!( env, @@ -372,7 +372,7 @@ impl StatefulPermission { /// means allowing Reset, and Reset cannot be granted later. pub fn new_reset(env: &mut E) -> StatefulPermission { StatefulPermission { - permission: env.clock().make_timer(RESET_TIMEOUT_DURATION), + permission: env.clock().make_timer(RESET_TIMEOUT_DURATION_MS), command_type: Some(StatefulCommand::Reset), channel: None, } @@ -429,7 +429,7 @@ impl StatefulPermission { // Reset is only allowed after a power cycle. StatefulCommand::Reset => unreachable!(), _ => { - self.permission = env.clock().make_timer(STATEFUL_COMMAND_TIMEOUT_DURATION); + self.permission = env.clock().make_timer(STATEFUL_COMMAND_TIMEOUT_DURATION_MS); self.command_type = Some(new_command_type); self.channel = Some(channel); } @@ -492,12 +492,12 @@ pub struct CtapState { impl CtapState { pub fn new(env: &mut E) -> Self { storage::init(env).ok().unwrap(); - let client_pin = ClientPin::::new(env); + let client_pin = ClientPin::new(env); CtapState { client_pin, #[cfg(feature = "with_ctap1")] u2f_up_state: U2fUserPresenceState::new(), - stateful_command_permission: StatefulPermission::::new_reset(env), + stateful_command_permission: StatefulPermission::new_reset(env), large_blobs: LargeBlobs::new(), } } @@ -3465,7 +3465,8 @@ mod test { Command::AuthenticatorGetAssertion(get_assertion_params), DUMMY_CHANNEL, ); - env.clock().advance(STATEFUL_COMMAND_TIMEOUT_DURATION - 1); + env.clock() + .advance(STATEFUL_COMMAND_TIMEOUT_DURATION_MS - 1); assert!(get_assertion_response.is_ok()); let get_next_assertion_response = ctap_state.process_parsed_command( &mut env, @@ -3490,7 +3491,7 @@ mod test { let mut env = TestEnv::new(); let mut ctap_state = CtapState::::new(&mut env); - env.clock().advance(RESET_TIMEOUT_DURATION); + env.clock().advance(RESET_TIMEOUT_DURATION_MS); let response = ctap_state.process_parsed_command(&mut env, Command::AuthenticatorReset, DUMMY_CHANNEL); assert_eq!(response, Err(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED)); @@ -3561,7 +3562,8 @@ mod test { pin_uv_auth_protocol: None, pin_uv_auth_param: None, }; - env.clock().advance(STATEFUL_COMMAND_TIMEOUT_DURATION - 1); + env.clock() + .advance(STATEFUL_COMMAND_TIMEOUT_DURATION_MS - 1); let response = ctap_state.process_parsed_command( &mut env, Command::AuthenticatorCredentialManagement(cred_management_params), diff --git a/src/ctap/token_state.rs b/src/ctap/token_state.rs index 112fc2ee..180735bb 100644 --- a/src/ctap/token_state.rs +++ b/src/ctap/token_state.rs @@ -25,7 +25,7 @@ use crypto::Hash256; /// 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: usize = 30000; +const INITIAL_USAGE_TIME_LIMIT_MS: usize = 30000; /// Implements pinUvAuthToken state from section 6.5.2.1. /// @@ -113,7 +113,7 @@ impl PinUvAuthTokenState { /// Starts the timer for pinUvAuthToken usage. pub fn begin_using_pin_uv_auth_token(&mut self, env: &mut E) { self.user_verified = true; - self.usage_timer = env.clock().make_timer(INITIAL_USAGE_TIME_LIMIT); + self.usage_timer = env.clock().make_timer(INITIAL_USAGE_TIME_LIMIT_MS); self.in_use = true; } @@ -167,7 +167,7 @@ mod test { env.clock().advance(100); token_state.pin_uv_auth_token_usage_timer_observer(&mut env); assert!(token_state.is_in_use()); - env.clock().advance(INITIAL_USAGE_TIME_LIMIT); + env.clock().advance(INITIAL_USAGE_TIME_LIMIT_MS); token_state.pin_uv_auth_token_usage_timer_observer(&mut env); assert!(!token_state.is_in_use()); } diff --git a/src/ctap/u2f_up.rs b/src/ctap/u2f_up.rs index 6800aa58..bf9f0ef7 100644 --- a/src/ctap/u2f_up.rs +++ b/src/ctap/u2f_up.rs @@ -12,11 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::TOUCH_TIMEOUT; +use super::TOUCH_TIMEOUT_MS; use crate::api::clock::Clock; use crate::env::Env; -const U2F_UP_PROMPT_TIMEOUT: usize = 10000; +const U2F_UP_PROMPT_TIMEOUT_MS: usize = 10000; pub struct U2fUserPresenceState { /// If user presence was recently requested, its timeout is saved here. @@ -40,7 +40,7 @@ impl U2fUserPresenceState { pub fn grant_up(&mut self, env: &mut E) { if !env.clock().is_elapsed(&self.needs_up) { self.needs_up = ::Timer::default(); - self.has_up = env.clock().make_timer(TOUCH_TIMEOUT); + self.has_up = env.clock().make_timer(TOUCH_TIMEOUT_MS); } } @@ -50,7 +50,7 @@ impl U2fUserPresenceState { self.has_up = ::Timer::default(); true } else { - self.needs_up = env.clock().make_timer(U2F_UP_PROMPT_TIMEOUT); + self.needs_up = env.clock().make_timer(U2F_UP_PROMPT_TIMEOUT_MS); false } } @@ -87,7 +87,7 @@ mod test { 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); + env.clock().advance(U2F_UP_PROMPT_TIMEOUT_MS); // The timeout excludes equality, so it should be over at this instant. assert!(!u2f_state.is_up_needed(env)); } @@ -97,7 +97,7 @@ mod test { assert!(!u2f_state.consume_up(env)); assert!(u2f_state.is_up_needed(env)); u2f_state.grant_up(env); - env.clock().advance(TOUCH_TIMEOUT); + env.clock().advance(TOUCH_TIMEOUT_MS); // The timeout excludes equality, so it should be over at this instant. assert!(!u2f_state.consume_up(env)); } diff --git a/src/env/test/mod.rs b/src/env/test/mod.rs index d59f5ff2..24e3fa67 100644 --- a/src/env/test/mod.rs +++ b/src/env/test/mod.rs @@ -59,17 +59,18 @@ impl Rng256 for TestRng256 { #[derive(Debug, Default, PartialEq)] pub struct TestTimer { - end: usize, + end_ms: usize, } #[derive(Debug, Default)] pub struct TestClock { - cur_time: usize, + /// The current time, as advanced, in milliseconds. + now_ms: usize, } impl TestClock { pub fn advance(&mut self, milliseconds: usize) { - self.cur_time += milliseconds; + self.now_ms += milliseconds; } } @@ -78,18 +79,18 @@ impl Clock for TestClock { fn make_timer(&mut self, milliseconds: usize) -> Self::Timer { TestTimer { - end: self.cur_time + milliseconds, + end_ms: self.now_ms + milliseconds, } } fn is_elapsed(&mut self, timer: &Self::Timer) -> bool { - self.cur_time >= timer.end + self.now_ms >= timer.end_ms } #[cfg(feature = "debug_ctap")] fn timestamp_us(&mut self) -> usize { // Unused, but let's implement something because it's easy. - self.cur_time * 1000 + self.now_ms * 1000 } } @@ -121,7 +122,7 @@ fn new_storage() -> BufferStorage { } impl HidConnection for TestEnv { - fn send_and_maybe_recv(&mut self, _buf: &mut [u8; 64], _timeout: usize) -> SendOrRecvResult { + fn send_and_maybe_recv(&mut self, _buf: &mut [u8; 64], _timeout_ms: usize) -> SendOrRecvResult { // TODO: Implement I/O from canned requests/responses for integration testing. Ok(SendOrRecvStatus::Sent) } @@ -171,7 +172,7 @@ impl TestUserPresence { impl UserPresence for TestUserPresence { fn check_init(&mut self) {} - fn wait_with_timeout(&mut self, _timeout: usize) -> UserPresenceResult { + fn wait_with_timeout(&mut self, _timeout_ms: usize) -> UserPresenceResult { (self.check)() } fn check_complete(&mut self) {} diff --git a/src/env/tock/clock.rs b/src/env/tock/clock.rs index 10a60963..8ca01e71 100644 --- a/src/env/tock/clock.rs +++ b/src/env/tock/clock.rs @@ -13,26 +13,28 @@ // limitations under the License. use crate::api::clock::Clock; -use libtock_core::syscalls; +use libtock_drivers::timer::{get_clock_frequency, get_ticks}; -mod command_nr { - pub const GET_CLOCK_FREQUENCY: usize = 1; - pub const GET_CLOCK_VALUE: usize = 2; +/// 56-bits timestamp (valid for 70k+ years) +#[derive(Clone, Copy, Debug, Default, PartialOrd, Ord, PartialEq, Eq)] +struct Timestamp { + epoch: usize, // 32-bits + tick: usize, // 24-bits (32kHz) } -const DRIVER_NUMBER: usize = 0x00000; +impl Timestamp { + /// Adds (potentially more than 24 bit of) ticks to this timestamp. + pub fn add_ticks(&mut self, ticks: usize) { + // Saturating should never happen, but it fails gracefully. + let sum = self.tick.saturating_add(ticks); + self.epoch += sum >> 24; + self.tick = sum & 0xff_ffff; + } +} #[derive(Default)] pub struct TockTimer { - end_epoch: usize, - end_tick: usize, -} - -/// Returns a tupel of u24 sum and how many times the sum wraps. -fn add_to_u24_with_wraps(lhs: usize, rhs: usize) -> (usize, usize) { - // Saturating should never happen, but it fails gracefully. - let sum = lhs.saturating_add(rhs); - (sum & 0xffffff, sum >> 24) + deadline: Timestamp, } /// Clock that produces timers through Tock syscalls. @@ -42,8 +44,7 @@ fn add_to_u24_with_wraps(lhs: usize, rhs: usize) -> (usize, usize) { /// can't guarantee to regularly create or check timers, call tickle at least every 8 minutes. #[derive(Default)] pub struct TockClock { - epoch: usize, - tick: usize, + now: Timestamp, } impl TockClock { @@ -51,13 +52,11 @@ impl TockClock { /// /// Call this regularly to timeout reliably despite wrapping clock ticks. pub fn tickle(&mut self) { - let cur_tick = syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_VALUE, 0, 0) - .ok() - .unwrap(); - if cur_tick < self.tick { - self.epoch += 1; + let cur_tick = get_ticks().ok().unwrap(); + if cur_tick < self.now.tick { + self.now.epoch += 1; } - self.tick = cur_tick; + self.now.tick = cur_tick; } } @@ -66,37 +65,27 @@ impl Clock for TockClock { fn make_timer(&mut self, milliseconds: usize) -> Self::Timer { self.tickle(); - let clock_frequency = - syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_FREQUENCY, 0, 0) - .ok() - .unwrap(); + let clock_frequency = get_clock_frequency().ok().unwrap(); let delta_tick = match milliseconds.checked_mul(clock_frequency) { Some(x) => x / 1000, // All CTAP timeouts are multiples of 100 so far. Worst case we timeout too early. None => (milliseconds / 100).saturating_mul(clock_frequency / 10), }; - let (end_tick, passed_epochs) = add_to_u24_with_wraps(self.tick, delta_tick); - // Epoch wraps after thousands of years, so we don't mind. - let end_epoch = self.epoch + passed_epochs; - Self::Timer { - end_epoch, - end_tick, - } + let mut deadline = self.now; + deadline.add_ticks(delta_tick); + Self::Timer { deadline } } fn is_elapsed(&mut self, timer: &Self::Timer) -> bool { self.tickle(); - (self.epoch, self.tick) >= (timer.end_epoch, timer.end_tick) + self.now >= timer.deadline } #[cfg(feature = "debug_ctap")] fn timestamp_us(&mut self) -> usize { - let clock_frequency = - syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_FREQUENCY, 0, 0) - .ok() - .unwrap(); - let total_ticks = 0x100_0000 * self.epoch + self.tick; - total_ticks * 1_000_000 / clock_frequency + let clock_frequency = get_clock_frequency().ok().unwrap(); + let total_ticks = 0x100_0000u64 * self.now.epoch as u64 + self.now.tick as u64; + (total_ticks.wrapping_mul(1_000_000u64) / clock_frequency as u64) as usize } } @@ -105,16 +94,25 @@ mod test { use super::*; #[test] - fn test_add_to_u24_with_wraps() { - // non-wrapping cases - assert_eq!(add_to_u24_with_wraps(0, 0), (0, 0)); - assert_eq!(add_to_u24_with_wraps(0xffffff, 0), (0xffffff, 0)); - assert_eq!(add_to_u24_with_wraps(0, 0xffffff), (0xffffff, 0)); - // wrapping cases - assert_eq!(add_to_u24_with_wraps(1, 0xffffff), (0, 1)); - assert_eq!(add_to_u24_with_wraps(0xffffff, 0xffffff), (0xfffffe, 1)); - assert_eq!(add_to_u24_with_wraps(0, 0x1000000), (0, 1)); - assert_eq!(add_to_u24_with_wraps(0, 0x2000000), (0, 2)); - assert_eq!(add_to_u24_with_wraps(0xffffff, 0x2ffffff), (0xfffffe, 3)); + fn test_timestamp_add_ticks() { + let mut timestamp = Timestamp::default(); + timestamp.add_ticks(1); + let expected = Timestamp { epoch: 0, tick: 1 }; + assert_eq!(timestamp, expected); + timestamp.add_ticks(0xff_ffff); + let expected = Timestamp { epoch: 1, tick: 0 }; + assert_eq!(timestamp, expected); + timestamp.add_ticks(0x100_0000); + let expected = Timestamp { epoch: 2, tick: 0 }; + assert_eq!(timestamp, expected); + timestamp.add_ticks(0x1ff_ffff); + let expected = Timestamp { + epoch: 3, + tick: 0xff_ffff, + }; + assert_eq!(timestamp, expected); + timestamp.add_ticks(1); + let expected = Timestamp { epoch: 4, tick: 0 }; + assert_eq!(timestamp, expected); } } diff --git a/src/env/tock/mod.rs b/src/env/tock/mod.rs index e0212699..006f708f 100644 --- a/src/env/tock/mod.rs +++ b/src/env/tock/mod.rs @@ -41,10 +41,10 @@ pub struct TockHidConnection { } impl HidConnection for TockHidConnection { - fn send_and_maybe_recv(&mut self, buf: &mut [u8; 64], timeout: usize) -> SendOrRecvResult { + fn send_and_maybe_recv(&mut self, buf: &mut [u8; 64], timeout_ms: usize) -> SendOrRecvResult { match usb_ctap_hid::send_or_recv_with_timeout( buf, - Duration::from_ms(timeout as isize), + Duration::from_ms(timeout_ms as isize), self.endpoint, ) { Ok(usb_ctap_hid::SendOrRecvStatus::Timeout) => Ok(SendOrRecvStatus::Timeout), @@ -113,8 +113,8 @@ impl UserPresence for TockEnv { self.blink_pattern = 0; } - fn wait_with_timeout(&mut self, timeout: usize) -> UserPresenceResult { - if timeout == 0 { + fn wait_with_timeout(&mut self, timeout_ms: usize) -> UserPresenceResult { + if timeout_ms == 0 { return Err(UserPresenceError::Timeout); } blink_leds(self.blink_pattern); @@ -139,7 +139,7 @@ impl UserPresence for TockEnv { }); let mut keepalive = keepalive_callback.init().flex_unwrap(); let keepalive_alarm = keepalive - .set_alarm(Duration::from_ms(timeout as isize)) + .set_alarm(Duration::from_ms(timeout_ms as isize)) .flex_unwrap(); // Wait for a button touch or an alarm. diff --git a/src/main.rs b/src/main.rs index b88405b4..8862ccbd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,7 +28,7 @@ use core::fmt::Write; use ctap2::api::clock::Clock; use ctap2::api::connection::{HidConnection, SendOrRecvStatus}; use ctap2::ctap::hid::HidPacketIterator; -use ctap2::ctap::KEEPALIVE_DELAY; +use ctap2::ctap::KEEPALIVE_DELAY_MS; #[cfg(feature = "with_ctap1")] use ctap2::env::tock::blink_leds; use ctap2::env::tock::{switch_off_leds, wink_leds, TockEnv}; @@ -45,8 +45,8 @@ use usb_ctap_hid::UsbEndpoint; libtock_core::stack_size! {0x4000} -const SEND_TIMEOUT: usize = 1000; -const KEEPALIVE_DELAY_TOCK: Duration = Duration::from_ms(KEEPALIVE_DELAY as isize); +const SEND_TIMEOUT_MS: usize = 1000; +const KEEPALIVE_DELAY_MS_TOCK: Duration = Duration::from_ms(KEEPALIVE_DELAY_MS as isize); #[cfg(not(feature = "vendor_hid"))] const NUM_ENDPOINTS: usize = 1; @@ -151,7 +151,7 @@ fn main() { if let Some(mut packet) = replies.next_packet() { // send and receive. let hid_connection = packet.transport.hid_connection(ctap.env()); - match hid_connection.send_and_maybe_recv(&mut packet.packet, SEND_TIMEOUT) { + match hid_connection.send_and_maybe_recv(&mut packet.packet, SEND_TIMEOUT_MS) { Ok(SendOrRecvStatus::Timeout) => { #[cfg(feature = "debug_ctap")] print_packet_notice( @@ -183,7 +183,7 @@ fn main() { } else { // receive usb_endpoint = - match usb_ctap_hid::recv_with_timeout(&mut pkt_request, KEEPALIVE_DELAY_TOCK) + match usb_ctap_hid::recv_with_timeout(&mut pkt_request, KEEPALIVE_DELAY_MS_TOCK) .flex_unwrap() { usb_ctap_hid::SendOrRecvStatus::Received(endpoint) => { @@ -249,7 +249,7 @@ fn main() { // 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; - led_blink_timer = ctap.env().clock().make_timer(KEEPALIVE_DELAY) + led_blink_timer = ctap.env().clock().make_timer(KEEPALIVE_DELAY_MS) } if ctap.should_wink() { diff --git a/third_party/libtock-drivers/src/timer.rs b/third_party/libtock-drivers/src/timer.rs index f3faadd5..9ab53360 100644 --- a/third_party/libtock-drivers/src/timer.rs +++ b/third_party/libtock-drivers/src/timer.rs @@ -41,6 +41,24 @@ pub fn sleep(duration: Duration) -> TockResult<()> { } } +pub fn get_ticks() -> TockResult { + Ok(syscalls::command( + DRIVER_NUMBER, + command_nr::GET_CLOCK_VALUE, + 0, + 0, + )?) +} + +pub fn get_clock_frequency() -> TockResult { + Ok(syscalls::command( + DRIVER_NUMBER, + command_nr::GET_CLOCK_FREQUENCY, + 0, + 0, + )?) +} + pub fn with_callback(callback: CB) -> WithCallback<'static, CB> { WithCallback { callback, From 54552302ca958b04f9fcf6380f5f180520eccb50 Mon Sep 17 00:00:00 2001 From: Fabian Kaczmarczyck Date: Tue, 28 Feb 2023 16:59:58 +0100 Subject: [PATCH 8/8] removes all cargo fmt artifacts --- src/ctap/hid/receive.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/ctap/hid/receive.rs b/src/ctap/hid/receive.rs index cbd824df..539f81f3 100644 --- a/src/ctap/hid/receive.rs +++ b/src/ctap/hid/receive.rs @@ -268,7 +268,7 @@ mod test { Ok(None) ); assert_eq!( - assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]),), + assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00])), Ok(Some(Message { cid: [0x12, 0x34, 0x56, 0x78], cmd: CtapHidCommand::Ping, @@ -289,11 +289,11 @@ mod test { Ok(None) ); assert_eq!( - assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]),), + 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]),), + assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x01])), Ok(Some(Message { cid: [0x12, 0x34, 0x56, 0x78], cmd: CtapHidCommand::Ping, @@ -315,12 +315,12 @@ mod test { ); for seq in 0..0x7F { assert_eq!( - assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, seq]),), + 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]),), + assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x7F])), Ok(Some(Message { cid: [0x12, 0x34, 0x56, 0x78], cmd: CtapHidCommand::Ping, @@ -390,12 +390,12 @@ mod test { ); assert_eq!( assembler - .parse_packet(&mut env, &byte_extend(&[0x12, 0x34, 0x56, cid, 0x00], byte),), + .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),), + .parse_packet(&mut env, &byte_extend(&[0x12, 0x34, 0x56, cid, 0x01], byte)), Ok(Some(Message { cid: [0x12, 0x34, 0x56, cid], cmd, @@ -432,7 +432,7 @@ mod test { } assert_eq!( - assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]),), + assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00])), Ok(Some(Message { cid: [0x12, 0x34, 0x56, 0x78], cmd: CtapHidCommand::Ping, @@ -466,7 +466,7 @@ mod test { // Spurious continuation packet. let seq = i; assert_eq!( - assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, seq]),), + assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, seq])), Err(( [0x12, 0x34, 0x56, 0x78], CtapHidError::UnexpectedContinuation @@ -487,7 +487,7 @@ mod test { Ok(None) ); assert_eq!( - assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x80]),), + assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x80])), Err(([0x12, 0x34, 0x56, 0x78], CtapHidError::InvalidSeq)) ); } @@ -504,7 +504,7 @@ mod test { Ok(None) ); assert_eq!( - assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x01]),), + assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x01])), Err(([0x12, 0x34, 0x56, 0x78], CtapHidError::InvalidSeq)) ); } @@ -522,7 +522,7 @@ mod test { ); env.clock().advance(TIMEOUT_DURATION_MS); assert_eq!( - assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]),), + assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00])), Err(([0x12, 0x34, 0x56, 0x78], CtapHidError::MsgTimeout)) ); } @@ -544,13 +544,13 @@ mod test { for seq in 0..0x7F { env.clock().advance(delay); assert_eq!( - assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, seq]),), + assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, seq])), Ok(None) ); } env.clock().advance(delay); assert_eq!( - assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x7F]),), + assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x7F])), Ok(Some(Message { cid: [0x12, 0x34, 0x56, 0x78], cmd: CtapHidCommand::Ping,