From 18faf9f38f9d290db7ce1062e90e9ce46d2e7a29 Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Wed, 2 Mar 2022 13:50:08 +0100 Subject: [PATCH] Introduce a trait to abstract the CTAP environment The end goal is to provide users with: - the Env trait that they should implement - the Ctap struct that they can use --- fuzz/fuzz_helper/src/lib.rs | 50 +--- src/ctap/credential_management.rs | 67 ++--- src/ctap/ctap1.rs | 247 ++++++++------- src/ctap/hid/mod.rs | 80 +++-- src/ctap/mod.rs | 479 +++++++++++++++--------------- src/ctap/status_code.rs | 2 +- src/env/mod.rs | 18 ++ src/env/test.rs | 48 +++ src/lib.rs | 39 ++- src/main.rs | 62 ++-- 10 files changed, 611 insertions(+), 481 deletions(-) create mode 100644 src/env/mod.rs create mode 100644 src/env/test.rs diff --git a/fuzz/fuzz_helper/src/lib.rs b/fuzz/fuzz_helper/src/lib.rs index ee349e2..647749e 100644 --- a/fuzz/fuzz_helper/src/lib.rs +++ b/fuzz/fuzz_helper/src/lib.rs @@ -18,16 +18,16 @@ extern crate lang_items; use arrayref::array_ref; use core::convert::TryFrom; -use crypto::rng256::ThreadRng256; +use ctap2::ctap::cbor_read; use ctap2::ctap::command::{ AuthenticatorClientPinParameters, AuthenticatorGetAssertionParameters, AuthenticatorMakeCredentialParameters, }; use ctap2::ctap::hid::receive::MessageAssembler; use ctap2::ctap::hid::send::HidPacketIterator; -use ctap2::ctap::hid::{ChannelID, CtapHid, HidPacket, Message}; -use ctap2::ctap::status_code::Ctap2StatusCode; -use ctap2::ctap::{cbor_read, CtapState}; +use ctap2::ctap::hid::{ChannelID, HidPacket, Message}; +use ctap2::env::test::TestEnv; +use ctap2::Ctap; use libtock_drivers::timer::{ClockValue, Timestamp}; const COMMAND_INIT: u8 = 0x06; @@ -46,10 +46,6 @@ pub enum InputType { Ctap1, } -fn user_immediately_present(_: ChannelID) -> Result<(), Ctap2StatusCode> { - Ok(()) -} - // Converts a byte slice into Message fn raw_to_message(data: &[u8]) -> Message { if data.len() <= 4 { @@ -71,13 +67,7 @@ fn raw_to_message(data: &[u8]) -> Message { // Returns an initialized ctap state, hid and the allocated cid // after processing the init command. -fn initialize( - ctap_state: &mut CtapState, - ctap_hid: &mut CtapHid, -) -> ChannelID -where - CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>, -{ +fn initialize(ctap: &mut Ctap) -> ChannelID { let nonce = vec![0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]; let message = Message { cid: CHANNEL_BROADCAST, @@ -87,7 +77,7 @@ where 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_hid.process_hid_packet(&pkt_request, DUMMY_CLOCK_VALUE, ctap_state) { + for pkt_reply in ctap.process_hid_packet(&pkt_request, DUMMY_CLOCK_VALUE) { if let Ok(Some(result)) = assembler_reply.parse_packet(&pkt_reply, DUMMY_TIMESTAMP) { result_cid.copy_from_slice(&result.payload[8..12]); } @@ -120,20 +110,12 @@ fn is_type(data: &[u8], input_type: InputType) -> bool { // Interprets the raw data as a complete message (with channel id, command type and payload) and // invokes message splitting, packet processing at CTAP HID level and response assembling. -fn process_message( - data: &[u8], - ctap_state: &mut CtapState, - ctap_hid: &mut CtapHid, -) where - CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>, -{ +fn process_message(data: &[u8], ctap: &mut Ctap) { let message = raw_to_message(data); 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_hid.process_hid_packet(&pkt_request, DUMMY_CLOCK_VALUE, ctap_state) - { + for pkt_reply in ctap.process_hid_packet(&pkt_request, DUMMY_CLOCK_VALUE) { // Only checks for assembling crashes, not for semantics. let _ = assembler_reply.parse_packet(&pkt_reply, DUMMY_TIMESTAMP); } @@ -146,14 +128,12 @@ fn process_message( // using an initialized and allocated channel. pub fn process_ctap_any_type(data: &[u8]) { // Initialize ctap state and hid and get the allocated cid. - let mut rng = ThreadRng256 {}; - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); - let mut ctap_hid = CtapHid::new(); - let cid = initialize(&mut ctap_state, &mut ctap_hid); + let mut ctap = Ctap::new(TestEnv::new(), DUMMY_CLOCK_VALUE); + let cid = initialize(&mut ctap); // Wrap input as message with the allocated cid. let mut command = cid.to_vec(); command.extend(data); - process_message(&command, &mut ctap_state, &mut ctap_hid); + process_message(&command, &mut ctap); } // Interprets the raw data as of the given input type and @@ -164,10 +144,8 @@ pub fn process_ctap_specific_type(data: &[u8], input_type: InputType) { return; } // Initialize ctap state and hid and get the allocated cid. - let mut rng = ThreadRng256 {}; - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); - let mut ctap_hid = CtapHid::new(); - let cid = initialize(&mut ctap_state, &mut ctap_hid); + let mut ctap = Ctap::new(TestEnv::new(), DUMMY_CLOCK_VALUE); + let cid = initialize(&mut ctap); // Wrap input as message with allocated cid and command type. let mut command = cid.to_vec(); match input_type { @@ -185,7 +163,7 @@ pub fn process_ctap_specific_type(data: &[u8], input_type: InputType) { } } command.extend(data); - process_message(&command, &mut ctap_state, &mut ctap_hid); + process_message(&command, &mut ctap); } // Splits the given data as HID packets and reassembles it, verifying that the original input message is reconstructed. diff --git a/src/ctap/credential_management.rs b/src/ctap/credential_management.rs index f31ea43..52ad6e5 100644 --- a/src/ctap/credential_management.rs +++ b/src/ctap/credential_management.rs @@ -359,7 +359,9 @@ mod test { use super::super::pin_protocol::authenticate_pin_uv_auth_token; use super::super::CtapState; use super::*; - use crypto::rng256::{Rng256, ThreadRng256}; + use crate::env::test::TestEnv; + use crate::env::Env; + use crypto::rng256::Rng256; const CLOCK_FREQUENCY_HZ: usize = 32768; const DUMMY_CLOCK_VALUE: ClockValue = ClockValue::new(0, CLOCK_FREQUENCY_HZ); @@ -383,15 +385,14 @@ mod test { } fn test_helper_process_get_creds_metadata(pin_uv_auth_protocol: PinUvAuthProtocol) { - let mut rng = ThreadRng256 {}; - let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); + 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 credential_source = create_credential_source(&mut rng); + let credential_source = create_credential_source(env.rng()); - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); ctap_state.client_pin = client_pin; ctap_state.persistent_store.set_pin(&[0u8; 16], 4).unwrap(); @@ -467,17 +468,16 @@ mod test { #[test] fn test_process_enumerate_rps_with_uv() { - let mut rng = ThreadRng256 {}; - let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); + 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 credential_source1 = create_credential_source(&mut rng); - let mut credential_source2 = create_credential_source(&mut rng); + let credential_source1 = create_credential_source(env.rng()); + let mut credential_source2 = create_credential_source(env.rng()); credential_source2.rp_id = "another.example.com".to_string(); - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); ctap_state.client_pin = client_pin; ctap_state @@ -565,15 +565,14 @@ mod test { #[test] fn test_process_enumerate_rps_completeness() { - let mut rng = ThreadRng256 {}; - let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); + 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 credential_source = create_credential_source(&mut rng); + let credential_source = create_credential_source(env.rng()); - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); ctap_state.client_pin = client_pin; const NUM_CREDENTIALS: usize = 20; @@ -648,20 +647,19 @@ mod test { #[test] fn test_process_enumerate_credentials_with_uv() { - let mut rng = ThreadRng256 {}; - let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); + 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 credential_source1 = create_credential_source(&mut rng); - let mut credential_source2 = create_credential_source(&mut rng); + let credential_source1 = create_credential_source(env.rng()); + let mut credential_source2 = create_credential_source(env.rng()); credential_source2.user_handle = vec![0x02]; credential_source2.user_name = Some("user2".to_string()); credential_source2.user_display_name = Some("User Two".to_string()); credential_source2.user_icon = Some("icon2".to_string()); - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); ctap_state.client_pin = client_pin; ctap_state @@ -754,16 +752,15 @@ mod test { #[test] fn test_process_delete_credential() { - let mut rng = ThreadRng256 {}; - let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); + 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 mut credential_source = create_credential_source(&mut rng); + let mut credential_source = create_credential_source(env.rng()); credential_source.credential_id = vec![0x1D; 32]; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); ctap_state.client_pin = client_pin; ctap_state @@ -826,16 +823,15 @@ mod test { #[test] fn test_process_update_user_information() { - let mut rng = ThreadRng256 {}; - let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); + 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 mut credential_source = create_credential_source(&mut rng); + let mut credential_source = create_credential_source(env.rng()); credential_source.credential_id = vec![0x1D; 32]; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); ctap_state.client_pin = client_pin; ctap_state @@ -899,9 +895,8 @@ mod test { #[test] fn test_process_credential_management_invalid_pin_uv_auth_param() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); ctap_state.persistent_store.set_pin(&[0u8; 16], 4).unwrap(); diff --git a/src/ctap/ctap1.rs b/src/ctap/ctap1.rs index 1fc3b03..857c12b 100644 --- a/src/ctap/ctap1.rs +++ b/src/ctap/ctap1.rs @@ -13,14 +13,12 @@ // limitations under the License. use super::apdu::{Apdu, ApduStatusCode}; -use super::hid::ChannelID; -use super::status_code::Ctap2StatusCode; use super::CtapState; +use crate::env::Env; use alloc::vec::Vec; use arrayref::array_ref; use core::convert::Into; use core::convert::TryFrom; -use crypto::rng256::Rng256; use libtock_drivers::timer::ClockValue; // For now, they're the same thing with apdu.rs containing the authoritative definition @@ -180,15 +178,12 @@ impl Ctap1Command { const VENDOR_SPECIFIC_FIRST: u8 = 0x40; const VENDOR_SPECIFIC_LAST: u8 = 0xBF; - pub fn process_command( + pub fn process_command( + env: &mut impl Env, message: &[u8], - ctap_state: &mut CtapState, + ctap_state: &mut CtapState, clock_value: ClockValue, - ) -> Result, Ctap1StatusCode> - where - R: Rng256, - CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>, - { + ) -> Result, Ctap1StatusCode> { if !ctap_state .allows_ctap1() .map_err(|_| Ctap1StatusCode::SW_INTERNAL_EXCEPTION)? @@ -204,7 +199,7 @@ impl Ctap1Command { if !ctap_state.u2f_up_state.consume_up(clock_value) { return Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED); } - Ctap1Command::process_register(challenge, application, ctap_state) + Ctap1Command::process_register(env, challenge, application, ctap_state) } U2fCommand::Authenticate { @@ -220,6 +215,7 @@ impl Ctap1Command { return Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED); } Ctap1Command::process_authenticate( + env, challenge, application, key_handle, @@ -247,19 +243,16 @@ impl Ctap1Command { // +------+-------------------+-----------------+------------+--------------------+ // + 0x00 | application (32B) | challenge (32B) | key handle | User pub key (65B) | // +------+-------------------+-----------------+------------+--------------------+ - fn process_register( + fn process_register( + env: &mut impl Env, challenge: [u8; 32], application: [u8; 32], - ctap_state: &mut CtapState, - ) -> Result, Ctap1StatusCode> - where - R: Rng256, - CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>, - { - let sk = crypto::ecdsa::SecKey::gensk(ctap_state.rng); + ctap_state: &mut CtapState, + ) -> Result, Ctap1StatusCode> { + let sk = crypto::ecdsa::SecKey::gensk(env.rng()); let pk = sk.genpk(); let key_handle = ctap_state - .encrypt_key_handle(sk, &application) + .encrypt_key_handle(env, sk, &application) .map_err(|_| Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?; if key_handle.len() > 0xFF { // This is just being defensive with unreachable code. @@ -314,17 +307,14 @@ impl Ctap1Command { // +-------------------+---------+--------------+-----------------+ // + application (32B) | UP (1B) | Counter (4B) | challenge (32B) | // +-------------------+---------+--------------+-----------------+ - fn process_authenticate( + fn process_authenticate( + env: &mut impl Env, challenge: [u8; 32], application: [u8; 32], key_handle: Vec, flags: Ctap1Flags, - ctap_state: &mut CtapState, - ) -> Result, Ctap1StatusCode> - where - R: Rng256, - CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>, - { + ctap_state: &mut CtapState, + ) -> Result, Ctap1StatusCode> { let credential_source = ctap_state .decrypt_credential_source(key_handle, &application) .map_err(|_| Ctap1StatusCode::SW_WRONG_DATA)?; @@ -333,7 +323,7 @@ impl Ctap1Command { return Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED); } ctap_state - .increment_global_signature_counter() + .increment_global_signature_counter(env) .map_err(|_| Ctap1StatusCode::SW_WRONG_DATA)?; let mut signature_data = ctap_state .generate_auth_data(&application, Ctap1Command::USER_PRESENCE_INDICATOR_BYTE) @@ -356,7 +346,7 @@ impl Ctap1Command { mod test { use super::super::{key_material, CREDENTIAL_ID_SIZE, USE_SIGNATURE_COUNTER}; use super::*; - use crypto::rng256::ThreadRng256; + use crate::env::test::TestEnv; use crypto::Hash256; const CLOCK_FREQUENCY_HZ: usize = 32768; @@ -406,30 +396,34 @@ mod test { #[test] fn test_process_allowed() { - let mut rng = ThreadRng256 {}; - let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); - let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); + let mut env = TestEnv::new(); + env.user_presence() + .set(|_| panic!("Unexpected user presence check in CTAP1")); + let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE); ctap_state.persistent_store.toggle_always_uv().unwrap(); let application = [0x0A; 32]; let message = create_register_message(&application); ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE); ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE); - let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); + let response = + Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE); assert_eq!(response, Err(Ctap1StatusCode::SW_COMMAND_NOT_ALLOWED)); } #[test] fn test_process_register() { - let mut rng = ThreadRng256 {}; - let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); - let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); + let mut env = TestEnv::new(); + env.user_presence() + .set(|_| panic!("Unexpected user presence check in CTAP1")); + let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE); let application = [0x0A; 32]; let message = create_register_message(&application); ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE); ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE); - let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); + let response = + Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE); // Certificate and private key are missing assert_eq!(response, Err(Ctap1StatusCode::SW_INTERNAL_EXCEPTION)); @@ -440,7 +434,8 @@ mod test { .is_ok()); ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE); ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE); - let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); + let response = + Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE); // Certificate is still missing assert_eq!(response, Err(Ctap1StatusCode::SW_INTERNAL_EXCEPTION)); @@ -452,7 +447,8 @@ mod test { ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE); ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE); let response = - Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE).unwrap(); + Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE) + .unwrap(); assert_eq!(response[0], Ctap1Command::LEGACY_BYTE); assert_eq!(response[66], CREDENTIAL_ID_SIZE as u8); assert!(ctap_state @@ -468,13 +464,15 @@ mod test { #[test] fn test_process_register_bad_message() { - let mut rng = ThreadRng256 {}; - let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); - let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); + let mut env = TestEnv::new(); + env.user_presence() + .set(|_| panic!("Unexpected user presence check in CTAP1")); + let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE); let application = [0x0A; 32]; let message = create_register_message(&application); let response = Ctap1Command::process_command( + &mut env, &message[..message.len() - 1], &mut ctap_state, START_CLOCK_VALUE, @@ -488,60 +486,72 @@ mod test { let application = [0x0A; 32]; let message = create_register_message(&application); - let mut rng = ThreadRng256 {}; - let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); - let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); + let mut env = TestEnv::new(); + env.user_presence() + .set(|_| panic!("Unexpected user presence check in CTAP1")); + let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE); ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE); ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE); let response = - Ctap1Command::process_command(&message, &mut ctap_state, TIMEOUT_CLOCK_VALUE); + Ctap1Command::process_command(&mut env, &message, &mut ctap_state, TIMEOUT_CLOCK_VALUE); assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED)); } #[test] fn test_process_authenticate_check_only() { - let mut rng = ThreadRng256 {}; - let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); - let sk = crypto::ecdsa::SecKey::gensk(&mut rng); - let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); + let mut env = TestEnv::new(); + env.user_presence() + .set(|_| panic!("Unexpected user presence check in CTAP1")); + let sk = crypto::ecdsa::SecKey::gensk(env.rng()); + let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE); let rp_id = "example.com"; let application = crypto::sha256::Sha256::hash(rp_id.as_bytes()); - let key_handle = ctap_state.encrypt_key_handle(sk, &application).unwrap(); + let key_handle = ctap_state + .encrypt_key_handle(&mut env, sk, &application) + .unwrap(); let message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle); - let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); + let response = + Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE); assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED)); } #[test] fn test_process_authenticate_check_only_wrong_rp() { - let mut rng = ThreadRng256 {}; - let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); - let sk = crypto::ecdsa::SecKey::gensk(&mut rng); - let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); + let mut env = TestEnv::new(); + env.user_presence() + .set(|_| panic!("Unexpected user presence check in CTAP1")); + let sk = crypto::ecdsa::SecKey::gensk(env.rng()); + let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE); let rp_id = "example.com"; let application = crypto::sha256::Sha256::hash(rp_id.as_bytes()); - let key_handle = ctap_state.encrypt_key_handle(sk, &application).unwrap(); + let key_handle = ctap_state + .encrypt_key_handle(&mut env, sk, &application) + .unwrap(); let application = [0x55; 32]; let message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle); - let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); + let response = + Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE); assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_DATA)); } #[test] fn test_process_authenticate_check_only_wrong_length() { - let mut rng = ThreadRng256 {}; - let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); - let sk = crypto::ecdsa::SecKey::gensk(&mut rng); - let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); + let mut env = TestEnv::new(); + env.user_presence() + .set(|_| panic!("Unexpected user presence check in CTAP1")); + let sk = crypto::ecdsa::SecKey::gensk(env.rng()); + let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE); let rp_id = "example.com"; let application = crypto::sha256::Sha256::hash(rp_id.as_bytes()); - let key_handle = ctap_state.encrypt_key_handle(sk, &application).unwrap(); + let key_handle = ctap_state + .encrypt_key_handle(&mut env, sk, &application) + .unwrap(); let mut message = create_authenticate_message( &application, Ctap1Flags::DontEnforceUpAndSign, @@ -549,73 +559,89 @@ mod test { ); message.push(0x00); - let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); + let response = + Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE); assert!(response.is_ok()); message.push(0x00); - let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); + let response = + Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE); assert!(response.is_ok()); message.push(0x00); - let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); + let response = + Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE); assert!(response.is_ok()); message.push(0x00); - let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); + let response = + Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE); assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_LENGTH)); } #[test] fn test_process_authenticate_check_only_wrong_cla() { - let mut rng = ThreadRng256 {}; - let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); - let sk = crypto::ecdsa::SecKey::gensk(&mut rng); - let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); + let mut env = TestEnv::new(); + env.user_presence() + .set(|_| panic!("Unexpected user presence check in CTAP1")); + let sk = crypto::ecdsa::SecKey::gensk(env.rng()); + let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE); let rp_id = "example.com"; let application = crypto::sha256::Sha256::hash(rp_id.as_bytes()); - let key_handle = ctap_state.encrypt_key_handle(sk, &application).unwrap(); + let key_handle = ctap_state + .encrypt_key_handle(&mut env, sk, &application) + .unwrap(); let mut message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle); message[0] = 0xEE; - let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); + let response = + Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE); assert_eq!(response, Err(Ctap1StatusCode::SW_CLA_INVALID)); } #[test] fn test_process_authenticate_check_only_wrong_ins() { - let mut rng = ThreadRng256 {}; - let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); - let sk = crypto::ecdsa::SecKey::gensk(&mut rng); - let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); + let mut env = TestEnv::new(); + env.user_presence() + .set(|_| panic!("Unexpected user presence check in CTAP1")); + let sk = crypto::ecdsa::SecKey::gensk(env.rng()); + let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE); let rp_id = "example.com"; let application = crypto::sha256::Sha256::hash(rp_id.as_bytes()); - let key_handle = ctap_state.encrypt_key_handle(sk, &application).unwrap(); + let key_handle = ctap_state + .encrypt_key_handle(&mut env, sk, &application) + .unwrap(); let mut message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle); message[1] = 0xEE; - let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); + let response = + Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE); assert_eq!(response, Err(Ctap1StatusCode::SW_INS_INVALID)); } #[test] fn test_process_authenticate_check_only_wrong_flags() { - let mut rng = ThreadRng256 {}; - let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); - let sk = crypto::ecdsa::SecKey::gensk(&mut rng); - let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); + let mut env = TestEnv::new(); + env.user_presence() + .set(|_| panic!("Unexpected user presence check in CTAP1")); + let sk = crypto::ecdsa::SecKey::gensk(env.rng()); + let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE); let rp_id = "example.com"; let application = crypto::sha256::Sha256::hash(rp_id.as_bytes()); - let key_handle = ctap_state.encrypt_key_handle(sk, &application).unwrap(); + let key_handle = ctap_state + .encrypt_key_handle(&mut env, sk, &application) + .unwrap(); let mut message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle); message[2] = 0xEE; - let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); + let response = + Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE); assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_DATA)); } @@ -629,21 +655,25 @@ mod test { #[test] fn test_process_authenticate_enforce() { - let mut rng = ThreadRng256 {}; - let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); - let sk = crypto::ecdsa::SecKey::gensk(&mut rng); - let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); + let mut env = TestEnv::new(); + env.user_presence() + .set(|_| panic!("Unexpected user presence check in CTAP1")); + let sk = crypto::ecdsa::SecKey::gensk(env.rng()); + let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE); let rp_id = "example.com"; let application = crypto::sha256::Sha256::hash(rp_id.as_bytes()); - let key_handle = ctap_state.encrypt_key_handle(sk, &application).unwrap(); + let key_handle = ctap_state + .encrypt_key_handle(&mut env, sk, &application) + .unwrap(); let message = create_authenticate_message(&application, Ctap1Flags::EnforceUpAndSign, &key_handle); ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE); ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE); let response = - Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE).unwrap(); + Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE) + .unwrap(); assert_eq!(response[0], 0x01); check_signature_counter( array_ref!(response, 1, 4), @@ -656,14 +686,17 @@ mod test { #[test] fn test_process_authenticate_dont_enforce() { - let mut rng = ThreadRng256 {}; - let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); - let sk = crypto::ecdsa::SecKey::gensk(&mut rng); - let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); + let mut env = TestEnv::new(); + env.user_presence() + .set(|_| panic!("Unexpected user presence check in CTAP1")); + let sk = crypto::ecdsa::SecKey::gensk(env.rng()); + let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE); let rp_id = "example.com"; let application = crypto::sha256::Sha256::hash(rp_id.as_bytes()); - let key_handle = ctap_state.encrypt_key_handle(sk, &application).unwrap(); + let key_handle = ctap_state + .encrypt_key_handle(&mut env, sk, &application) + .unwrap(); let message = create_authenticate_message( &application, Ctap1Flags::DontEnforceUpAndSign, @@ -671,7 +704,8 @@ mod test { ); let response = - Ctap1Command::process_command(&message, &mut ctap_state, TIMEOUT_CLOCK_VALUE).unwrap(); + Ctap1Command::process_command(&mut env, &message, &mut ctap_state, TIMEOUT_CLOCK_VALUE) + .unwrap(); assert_eq!(response[0], 0x01); check_signature_counter( array_ref!(response, 1, 4), @@ -689,13 +723,15 @@ mod test { let message = create_authenticate_message(&application, Ctap1Flags::EnforceUpAndSign, &key_handle); - let mut rng = ThreadRng256 {}; - let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); - let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); + let mut env = TestEnv::new(); + env.user_presence() + .set(|_| panic!("Unexpected user presence check in CTAP1")); + let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE); ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE); ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE); - let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); + let response = + Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE); assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_DATA)); } @@ -706,14 +742,15 @@ mod test { let message = create_authenticate_message(&application, Ctap1Flags::EnforceUpAndSign, &key_handle); - let mut rng = ThreadRng256 {}; - let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); - let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); + let mut env = TestEnv::new(); + env.user_presence() + .set(|_| panic!("Unexpected user presence check in CTAP1")); + let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE); ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE); ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE); let response = - Ctap1Command::process_command(&message, &mut ctap_state, TIMEOUT_CLOCK_VALUE); + Ctap1Command::process_command(&mut env, &message, &mut ctap_state, TIMEOUT_CLOCK_VALUE); 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 57f2875..3daf2f9 100644 --- a/src/ctap/hid/mod.rs +++ b/src/ctap/hid/mod.rs @@ -22,12 +22,12 @@ use super::ctap1; use super::status_code::Ctap2StatusCode; use super::timed_permission::TimedPermission; use super::CtapState; +use crate::env::Env; use alloc::vec; use alloc::vec::Vec; use arrayref::{array_ref, array_refs}; #[cfg(feature = "debug_ctap")] use core::fmt::Write; -use crypto::rng256::Rng256; #[cfg(feature = "debug_ctap")] use libtock_drivers::console::Console; use libtock_drivers::timer::{ClockValue, Duration, Timestamp}; @@ -146,16 +146,13 @@ impl CtapHid { // Process an incoming USB HID packet, and optionally returns a list of outgoing packets to // send as a reply. - pub fn process_hid_packet( + pub fn process_hid_packet( &mut self, + env: &mut impl Env, packet: &HidPacket, clock_value: ClockValue, - ctap_state: &mut CtapState, - ) -> HidPacketIterator - where - R: Rng256, - CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>, - { + ctap_state: &mut CtapState, + ) -> HidPacketIterator { // TODO: Send COMMAND_KEEPALIVE every 100ms? match self .assembler @@ -183,6 +180,7 @@ impl CtapHid { #[cfg(feature = "with_ctap1")] match ctap1::Ctap1Command::process_command( + env, &message.payload, ctap_state, clock_value, @@ -200,7 +198,7 @@ impl CtapHid { // don't handle any other packet in the meantime. // TODO: Send keep-alive packets in the meantime. let response = - ctap_state.process_command(&message.payload, cid, clock_value); + ctap_state.process_command(env, &message.payload, cid, clock_value); if let Some(iterator) = CtapHid::split_message(Message { cid, cmd: CtapHid::COMMAND_CBOR, @@ -433,27 +431,25 @@ impl CtapHid { #[cfg(test)] mod test { use super::*; - use crypto::rng256::ThreadRng256; + use crate::env::test::TestEnv; const CLOCK_FREQUENCY_HZ: usize = 32768; // Except for tests for timeouts (done in ctap1.rs), transactions are time independant. const DUMMY_CLOCK_VALUE: ClockValue = ClockValue::new(0, CLOCK_FREQUENCY_HZ); const DUMMY_TIMESTAMP: Timestamp = Timestamp::from_ms(0); - fn process_messages( + fn process_messages( + env: &mut impl Env, ctap_hid: &mut CtapHid, - ctap_state: &mut CtapState, + ctap_state: &mut CtapState, request: Vec, - ) -> Option> - where - CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>, - { + ) -> Option> { let mut result = Vec::new(); let mut assembler_reply = MessageAssembler::new(); for msg_request in request { for pkt_request in HidPacketIterator::new(msg_request).unwrap() { for pkt_reply in - ctap_hid.process_hid_packet(&pkt_request, DUMMY_CLOCK_VALUE, ctap_state) + ctap_hid.process_hid_packet(env, &pkt_request, DUMMY_CLOCK_VALUE, ctap_state) { match assembler_reply.parse_packet(&pkt_reply, DUMMY_TIMESTAMP) { Ok(Some(message)) => result.push(message), @@ -466,15 +462,14 @@ mod test { Some(result) } - fn cid_from_init( + fn cid_from_init( + env: &mut impl Env, ctap_hid: &mut CtapHid, - ctap_state: &mut CtapState, - ) -> ChannelID - where - CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>, - { + ctap_state: &mut CtapState, + ) -> ChannelID { let nonce = vec![0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]; let reply = process_messages( + env, ctap_hid, ctap_state, vec![Message { @@ -521,15 +516,16 @@ mod test { #[test] fn test_spurious_continuation_packet() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); let mut ctap_hid = CtapHid::new(); let mut packet = [0x00; 64]; packet[0..7].copy_from_slice(&[0xC1, 0xC1, 0xC1, 0xC1, 0x00, 0x51, 0x51]); let mut assembler_reply = MessageAssembler::new(); - for pkt_reply in ctap_hid.process_hid_packet(&packet, DUMMY_CLOCK_VALUE, &mut ctap_state) { + for pkt_reply in + ctap_hid.process_hid_packet(&mut env, &packet, DUMMY_CLOCK_VALUE, &mut ctap_state) + { // Continuation packets are silently ignored. assert_eq!( assembler_reply @@ -542,12 +538,12 @@ mod test { #[test] fn test_command_init() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); let mut ctap_hid = CtapHid::new(); let reply = process_messages( + &mut env, &mut ctap_hid, &mut ctap_state, vec![Message { @@ -587,11 +583,10 @@ mod test { #[test] fn test_command_init_for_sync() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); let mut ctap_hid = CtapHid::new(); - let cid = cid_from_init(&mut ctap_hid, &mut ctap_state); + let cid = cid_from_init(&mut env, &mut ctap_hid, &mut ctap_state); // Ping packet with a length longer than one packet. let mut packet1 = [0x51; 64]; @@ -606,9 +601,12 @@ mod test { let mut result = Vec::new(); let mut assembler_reply = MessageAssembler::new(); for pkt_request in &[packet1, packet2] { - for pkt_reply in - ctap_hid.process_hid_packet(pkt_request, DUMMY_CLOCK_VALUE, &mut ctap_state) - { + for pkt_reply in ctap_hid.process_hid_packet( + &mut env, + pkt_request, + DUMMY_CLOCK_VALUE, + &mut ctap_state, + ) { if let Some(message) = assembler_reply .parse_packet(&pkt_reply, DUMMY_TIMESTAMP) .unwrap() @@ -647,13 +645,13 @@ mod test { #[test] fn test_command_ping() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); let mut ctap_hid = CtapHid::new(); - let cid = cid_from_init(&mut ctap_hid, &mut ctap_state); + let cid = cid_from_init(&mut env, &mut ctap_hid, &mut ctap_state); let reply = process_messages( + &mut env, &mut ctap_hid, &mut ctap_state, vec![Message { diff --git a/src/ctap/mod.rs b/src/ctap/mod.rs index 120feae..b2c0e58 100644 --- a/src/ctap/mod.rs +++ b/src/ctap/mod.rs @@ -65,6 +65,7 @@ use self::timed_permission::TimedPermission; #[cfg(feature = "with_ctap1")] use self::timed_permission::U2fUserPresenceState; use crate::embedded_flash::{UpgradeLocations, UpgradeStorage}; +use crate::env::{Env, UserPresence}; use alloc::boxed::Box; use alloc::string::{String, ToString}; use alloc::vec; @@ -329,12 +330,7 @@ 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<'a, R: Rng256, CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>> -{ - rng: &'a mut R, - // A function to check user presence, ultimately returning true if user presence was detected, - // false otherwise. - check_user_presence: CheckUserPresence, +pub struct CtapState { persistent_store: PersistentStore, client_pin: ClientPin, #[cfg(feature = "with_ctap1")] @@ -345,21 +341,11 @@ pub struct CtapState<'a, R: Rng256, CheckUserPresence: Fn(ChannelID) -> Result<( upgrade_locations: Option, } -impl<'a, R, CheckUserPresence> CtapState<'a, R, CheckUserPresence> -where - R: Rng256, - CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>, -{ - pub fn new( - rng: &'a mut R, - check_user_presence: CheckUserPresence, - now: ClockValue, - ) -> CtapState<'a, R, CheckUserPresence> { - let persistent_store = PersistentStore::new(rng); - let client_pin = ClientPin::new(rng); +impl CtapState { + pub fn new(env: &mut impl Env, now: ClockValue) -> CtapState { + let persistent_store = PersistentStore::new(env.rng()); + let client_pin = ClientPin::new(env.rng()); CtapState { - rng, - check_user_presence, persistent_store, client_pin, #[cfg(feature = "with_ctap1")] @@ -381,9 +367,12 @@ where self.client_pin.update_timeouts(now); } - pub fn increment_global_signature_counter(&mut self) -> Result<(), Ctap2StatusCode> { + pub fn increment_global_signature_counter( + &mut self, + env: &mut impl Env, + ) -> Result<(), Ctap2StatusCode> { if USE_SIGNATURE_COUNTER { - let increment = self.rng.gen_uniform_u32x8()[0] % 8 + 1; + let increment = env.rng().gen_uniform_u32x8()[0] % 8 + 1; self.persistent_store .incr_global_signature_counter(increment)?; } @@ -404,6 +393,7 @@ where // compatible with U2F. pub fn encrypt_key_handle( &mut self, + env: &mut impl Env, private_key: crypto::ecdsa::SecKey, application: &[u8; 32], ) -> Result, Ctap2StatusCode> { @@ -413,7 +403,7 @@ where private_key.to_bytes(array_mut_ref!(plaintext, 0, 32)); plaintext[32..64].copy_from_slice(application); - let mut encrypted_id = aes256_cbc_encrypt(self.rng, &aes_enc_key, &plaintext, true)?; + let mut encrypted_id = aes256_cbc_encrypt(env.rng(), &aes_enc_key, &plaintext, true)?; let id_hmac = hmac_256::(&master_keys.hmac, &encrypted_id[..]); encrypted_id.extend(&id_hmac); Ok(encrypted_id) @@ -464,6 +454,7 @@ where pub fn process_command( &mut self, + env: &mut impl Env, command_cbor: &[u8], cid: ChannelID, now: ClockValue, @@ -501,20 +492,22 @@ where } let response = match command { Command::AuthenticatorMakeCredential(params) => { - self.process_make_credential(params, cid) + self.process_make_credential(env, params, cid) } Command::AuthenticatorGetAssertion(params) => { - self.process_get_assertion(params, cid, now) + self.process_get_assertion(env, params, cid, now) + } + Command::AuthenticatorGetNextAssertion => { + self.process_get_next_assertion(env, now) } - Command::AuthenticatorGetNextAssertion => self.process_get_next_assertion(now), Command::AuthenticatorGetInfo => self.process_get_info(), Command::AuthenticatorClientPin(params) => self.client_pin.process_command( - self.rng, + env.rng(), &mut self.persistent_store, params, now, ), - Command::AuthenticatorReset => self.process_reset(cid, now), + Command::AuthenticatorReset => self.process_reset(env, cid, now), Command::AuthenticatorCredentialManagement(params) => { process_credential_management( &mut self.persistent_store, @@ -524,7 +517,7 @@ where now, ) } - Command::AuthenticatorSelection => self.process_selection(cid), + Command::AuthenticatorSelection => self.process_selection(env, cid), Command::AuthenticatorLargeBlobs(params) => self.large_blobs.process_command( &mut self.persistent_store, &mut self.client_pin, @@ -535,7 +528,7 @@ where } // Vendor specific commands Command::AuthenticatorVendorConfigure(params) => { - self.process_vendor_configure(params, cid) + self.process_vendor_configure(env, params, cid) } Command::AuthenticatorVendorUpgrade(params) => { self.process_vendor_upgrade(params) @@ -564,6 +557,7 @@ where fn pin_uv_auth_precheck( &mut self, + env: &mut impl Env, pin_uv_auth_param: &Option>, pin_uv_auth_protocol: Option, cid: ChannelID, @@ -571,7 +565,7 @@ where if let Some(auth_param) = &pin_uv_auth_param { // This case was added in FIDO 2.1. if auth_param.is_empty() { - (self.check_user_presence)(cid)?; + env.user_presence().check(cid)?; if self.persistent_store.pin_hash()?.is_none() { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_NOT_SET); } else { @@ -585,6 +579,7 @@ where fn process_make_credential( &mut self, + env: &mut impl Env, make_credential_params: AuthenticatorMakeCredentialParameters, cid: ChannelID, ) -> Result { @@ -601,7 +596,7 @@ where enterprise_attestation, } = make_credential_params; - self.pin_uv_auth_precheck(&pin_uv_auth_param, pin_uv_auth_protocol, cid)?; + self.pin_uv_auth_precheck(env, &pin_uv_auth_param, pin_uv_auth_protocol, cid)?; if !pub_key_cred_params.contains(&ES256_CRED_PARAM) { return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM); @@ -681,13 +676,13 @@ where { // Perform this check, so bad actors can't brute force exclude_list // without user interaction. - let _ = (self.check_user_presence)(cid); + let _ = env.user_presence().check(cid); return Err(Ctap2StatusCode::CTAP2_ERR_CREDENTIAL_EXCLUDED); } } } - (self.check_user_presence)(cid)?; + env.user_presence().check(cid)?; self.client_pin.clear_token_flags(); let mut cred_protect_policy = extensions.cred_protect; @@ -719,15 +714,15 @@ where flags |= ED_FLAG }; let large_blob_key = match (options.rk, extensions.large_blob_key) { - (true, Some(true)) => Some(self.rng.gen_uniform_u8x32().to_vec()), + (true, Some(true)) => Some(env.rng().gen_uniform_u8x32().to_vec()), _ => None, }; - let sk = crypto::ecdsa::SecKey::gensk(self.rng); + let sk = crypto::ecdsa::SecKey::gensk(env.rng()); let pk = sk.genpk(); let credential_id = if options.rk { - let random_id = self.rng.gen_uniform_u8x32().to_vec(); + let random_id = env.rng().gen_uniform_u8x32().to_vec(); let credential_source = PublicKeyCredentialSource { key_type: PublicKeyCredentialType::PublicKey, credential_id: random_id.clone(), @@ -753,7 +748,7 @@ where self.persistent_store.store_credential(credential_source)?; random_id } else { - self.encrypt_key_handle(sk.clone(), &rp_id_hash)? + self.encrypt_key_handle(env, sk.clone(), &rp_id_hash)? }; let mut auth_data = self.generate_auth_data(&rp_id_hash, flags)?; @@ -842,6 +837,7 @@ where // and returns the correct Get(Next)Assertion response. fn assertion_response( &mut self, + env: &mut impl Env, mut credential: PublicKeyCredentialSource, assertion_input: AssertionInput, number_of_credentials: Option, @@ -858,7 +854,7 @@ where let encrypted_output = if let Some(hmac_secret_input) = extensions.hmac_secret { let cred_random = self.generate_cred_random(&credential.private_key, has_uv)?; Some(self.client_pin.process_hmac_secret( - self.rng, + env.rng(), hmac_secret_input, &cred_random, )?) @@ -949,6 +945,7 @@ where fn process_get_assertion( &mut self, + env: &mut impl Env, get_assertion_params: AuthenticatorGetAssertionParameters, cid: ChannelID, now: ClockValue, @@ -963,7 +960,7 @@ where pin_uv_auth_protocol, } = get_assertion_params; - self.pin_uv_auth_precheck(&pin_uv_auth_param, pin_uv_auth_protocol, cid)?; + self.pin_uv_auth_precheck(env, &pin_uv_auth_param, pin_uv_auth_protocol, cid)?; if extensions.hmac_secret.is_some() && !options.up { // The extension is actually supported, but we need user presence. @@ -1045,11 +1042,11 @@ where // This check comes before CTAP2_ERR_NO_CREDENTIALS in CTAP 2.0. if options.up { - (self.check_user_presence)(cid)?; + env.user_presence().check(cid)?; self.client_pin.clear_token_flags(); } - self.increment_global_signature_counter()?; + self.increment_global_signature_counter(env)?; let assertion_input = AssertionInput { client_data_hash, @@ -1069,11 +1066,12 @@ where .set_command(now, assertion_state); number_of_credentials }; - self.assertion_response(credential, assertion_input, number_of_credentials) + self.assertion_response(env, credential, assertion_input, number_of_credentials) } fn process_get_next_assertion( &mut self, + env: &mut impl Env, now: ClockValue, ) -> Result { self.stateful_command_permission @@ -1082,7 +1080,7 @@ where .stateful_command_permission .next_assertion_credential()?; let credential = self.persistent_store.get_credential(credential_key)?; - self.assertion_response(credential, assertion_input, None) + self.assertion_response(env, credential, assertion_input, None) } fn process_get_info(&self) -> Result { @@ -1159,6 +1157,7 @@ where fn process_reset( &mut self, + env: &mut impl Env, cid: ChannelID, now: ClockValue, ) -> Result { @@ -1168,10 +1167,10 @@ where StatefulCommand::Reset => (), _ => return Err(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED), } - (self.check_user_presence)(cid)?; + env.user_presence().check(cid)?; - self.persistent_store.reset(self.rng)?; - self.client_pin.reset(self.rng); + self.persistent_store.reset(env.rng())?; + self.client_pin.reset(env.rng()); #[cfg(feature = "with_ctap1")] { self.u2f_up_state = U2fUserPresenceState::new( @@ -1182,18 +1181,23 @@ where Ok(ResponseData::AuthenticatorReset) } - fn process_selection(&self, cid: ChannelID) -> Result { - (self.check_user_presence)(cid)?; + fn process_selection( + &self, + env: &mut impl Env, + cid: ChannelID, + ) -> Result { + env.user_presence().check(cid)?; Ok(ResponseData::AuthenticatorSelection) } fn process_vendor_configure( &mut self, + env: &mut impl Env, params: AuthenticatorVendorConfigureParameters, cid: ChannelID, ) -> Result { if params.attestation_material.is_some() || params.lockdown { - (self.check_user_presence)(cid)?; + env.user_presence().check(cid)?; } // Sanity checks @@ -1341,8 +1345,8 @@ mod test { }; use super::pin_protocol::{authenticate_pin_uv_auth_token, PinProtocol}; use super::*; + use crate::env::test::TestEnv; use cbor::{cbor_array, cbor_array_vec, cbor_map}; - use crypto::rng256::ThreadRng256; const CLOCK_FREQUENCY_HZ: usize = 32768; const DUMMY_CLOCK_VALUE: ClockValue = ClockValue::new(0, CLOCK_FREQUENCY_HZ); @@ -1396,10 +1400,10 @@ mod test { #[test] fn test_get_info() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); - let info_reponse = ctap_state.process_command(&[0x04], DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); + let info_reponse = + ctap_state.process_command(&mut env, &[0x04], DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE); let expected_cbor = cbor_map_options! { 0x01 => cbor_array_vec![vec![ @@ -1508,13 +1512,12 @@ mod test { #[test] fn test_resident_process_make_credential() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); let make_credential_params = create_minimal_make_credential_parameters(); let make_credential_response = - ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID); + ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID); check_make_response( make_credential_response, @@ -1527,14 +1530,13 @@ mod test { #[test] fn test_non_resident_process_make_credential() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.options.rk = false; let make_credential_response = - ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID); + ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID); check_make_response( make_credential_response, @@ -1547,14 +1549,13 @@ mod test { #[test] fn test_process_make_credential_unsupported_algorithm() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.pub_key_cred_params = vec![]; let make_credential_response = - ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID); + ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID); assert_eq!( make_credential_response, @@ -1564,10 +1565,9 @@ mod test { #[test] fn test_process_make_credential_credential_excluded() { - let mut rng = ThreadRng256 {}; - let excluded_private_key = crypto::ecdsa::SecKey::gensk(&mut rng); - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let excluded_private_key = crypto::ecdsa::SecKey::gensk(env.rng()); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); let excluded_credential_id = vec![0x01, 0x23, 0x45, 0x67]; let make_credential_params = @@ -1592,7 +1592,7 @@ mod test { .is_ok()); let make_credential_response = - ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID); + ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID); assert_eq!( make_credential_response, Err(Ctap2StatusCode::CTAP2_ERR_CREDENTIAL_EXCLUDED) @@ -1601,15 +1601,14 @@ mod test { #[test] fn test_process_make_credential_credential_with_cred_protect() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); let test_policy = CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList; let make_credential_params = create_make_credential_parameters_with_cred_protect_policy(test_policy); let make_credential_response = - ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID); + ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID); assert!(make_credential_response.is_ok()); let mut iter_result = Ok(()); @@ -1626,7 +1625,7 @@ mod test { let make_credential_params = create_make_credential_parameters_with_exclude_list(&credential_id); let make_credential_response = - ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID); + ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID); assert_eq!( make_credential_response, Err(Ctap2StatusCode::CTAP2_ERR_CREDENTIAL_EXCLUDED) @@ -1636,7 +1635,7 @@ mod test { let make_credential_params = create_make_credential_parameters_with_cred_protect_policy(test_policy); let make_credential_response = - ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID); + ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID); assert!(make_credential_response.is_ok()); let mut iter_result = Ok(()); @@ -1653,15 +1652,14 @@ mod test { let make_credential_params = create_make_credential_parameters_with_exclude_list(&credential_id); let make_credential_response = - ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID); + ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID); assert!(make_credential_response.is_ok()); } #[test] fn test_process_make_credential_hmac_secret() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); let extensions = MakeCredentialExtensions { hmac_secret: true, @@ -1671,7 +1669,7 @@ mod test { make_credential_params.options.rk = false; make_credential_params.extensions = extensions; let make_credential_response = - ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID); + ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID); let expected_extension_cbor = [ 0xA1, 0x6B, 0x68, 0x6D, 0x61, 0x63, 0x2D, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0xF5, @@ -1687,9 +1685,8 @@ mod test { #[test] fn test_process_make_credential_hmac_secret_resident_key() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); let extensions = MakeCredentialExtensions { hmac_secret: true, @@ -1698,7 +1695,7 @@ mod test { let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.extensions = extensions; let make_credential_response = - ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID); + ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID); let expected_extension_cbor = [ 0xA1, 0x6B, 0x68, 0x6D, 0x61, 0x63, 0x2D, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0xF5, @@ -1714,9 +1711,8 @@ mod test { #[test] fn test_process_make_credential_min_pin_length() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); // First part: The extension is ignored, since the RP ID is not on the list. let extensions = MakeCredentialExtensions { @@ -1726,7 +1722,7 @@ mod test { let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.extensions = extensions; let make_credential_response = - ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID); + ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID); check_make_response( make_credential_response, 0x41, @@ -1750,7 +1746,7 @@ mod test { let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.extensions = extensions; let make_credential_response = - ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID); + ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID); let expected_extension_cbor = [ 0xA1, 0x6C, 0x6D, 0x69, 0x6E, 0x50, 0x69, 0x6E, 0x4C, 0x65, 0x6E, 0x67, 0x74, 0x68, 0x04, @@ -1766,9 +1762,8 @@ mod test { #[test] fn test_process_make_credential_cred_blob_ok() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); let extensions = MakeCredentialExtensions { cred_blob: Some(vec![0xCB]), @@ -1777,7 +1772,7 @@ mod test { let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.extensions = extensions; let make_credential_response = - ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID); + ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID); let expected_extension_cbor = [ 0xA1, 0x68, 0x63, 0x72, 0x65, 0x64, 0x42, 0x6C, 0x6F, 0x62, 0xF5, ]; @@ -1802,9 +1797,8 @@ mod test { #[test] fn test_process_make_credential_cred_blob_too_big() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); let extensions = MakeCredentialExtensions { cred_blob: Some(vec![0xCB; MAX_CRED_BLOB_LENGTH + 1]), @@ -1813,7 +1807,7 @@ mod test { let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.extensions = extensions; let make_credential_response = - ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID); + ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID); let expected_extension_cbor = [ 0xA1, 0x68, 0x63, 0x72, 0x65, 0x64, 0x42, 0x6C, 0x6F, 0x62, 0xF4, ]; @@ -1838,9 +1832,8 @@ mod test { #[test] fn test_process_make_credential_large_blob_key() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); let extensions = MakeCredentialExtensions { large_blob_key: Some(true), @@ -1849,7 +1842,7 @@ mod test { let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.extensions = extensions; let make_credential_response = - ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID); + ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID); let large_blob_key = match make_credential_response.unwrap() { ResponseData::AuthenticatorMakeCredential(make_credential_response) => { make_credential_response.large_blob_key.unwrap() @@ -1872,14 +1865,13 @@ mod test { fn test_helper_process_make_credential_with_pin_and_uv( pin_uv_auth_protocol: PinUvAuthProtocol, ) { - let mut rng = ThreadRng256 {}; - let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); + 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 user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); ctap_state.client_pin = client_pin; ctap_state.persistent_store.set_pin(&[0x88; 16], 4).unwrap(); @@ -1893,8 +1885,11 @@ mod test { make_credential_params.options.uv = true; make_credential_params.pin_uv_auth_param = Some(pin_uv_auth_param); make_credential_params.pin_uv_auth_protocol = Some(pin_uv_auth_protocol); - let make_credential_response = - ctap_state.process_make_credential(make_credential_params.clone(), DUMMY_CHANNEL_ID); + let make_credential_response = ctap_state.process_make_credential( + &mut env, + make_credential_params.clone(), + DUMMY_CHANNEL_ID, + ); check_make_response( make_credential_response, @@ -1905,7 +1900,7 @@ mod test { ); let make_credential_response = - ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID); + ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID); assert_eq!( make_credential_response, Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID) @@ -1924,15 +1919,14 @@ mod test { #[test] fn test_non_resident_process_make_credential_with_pin() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); ctap_state.persistent_store.set_pin(&[0x88; 16], 4).unwrap(); let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.options.rk = false; let make_credential_response = - ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID); + ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID); check_make_response( make_credential_response, @@ -1945,14 +1939,13 @@ mod test { #[test] fn test_resident_process_make_credential_with_pin() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); ctap_state.persistent_store.set_pin(&[0x88; 16], 4).unwrap(); let make_credential_params = create_minimal_make_credential_parameters(); let make_credential_response = - ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID); + ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID); assert_eq!( make_credential_response, Err(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED) @@ -1961,14 +1954,13 @@ mod test { #[test] fn test_process_make_credential_with_pin_always_uv() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); ctap_state.persistent_store.toggle_always_uv().unwrap(); let make_credential_params = create_minimal_make_credential_parameters(); let make_credential_response = - ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID); + ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID); assert_eq!( make_credential_response, Err(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED) @@ -1979,7 +1971,7 @@ mod test { make_credential_params.pin_uv_auth_param = Some(vec![0xA4; 16]); make_credential_params.pin_uv_auth_protocol = Some(PinUvAuthProtocol::V1); let make_credential_response = - ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID); + ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID); assert_eq!( make_credential_response, Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID) @@ -1988,14 +1980,14 @@ mod test { #[test] fn test_process_make_credential_cancelled() { - let mut rng = ThreadRng256 {}; - let user_presence_always_cancel = |_| Err(Ctap2StatusCode::CTAP2_ERR_KEEPALIVE_CANCEL); - let mut ctap_state = - CtapState::new(&mut rng, user_presence_always_cancel, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + env.user_presence() + .set(|_| Err(Ctap2StatusCode::CTAP2_ERR_KEEPALIVE_CANCEL)); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); let make_credential_params = create_minimal_make_credential_parameters(); let make_credential_response = - ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID); + ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID); assert_eq!( make_credential_response, @@ -2085,13 +2077,12 @@ mod test { #[test] fn test_resident_process_get_assertion() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); let make_credential_params = create_minimal_make_credential_parameters(); assert!(ctap_state - .process_make_credential(make_credential_params, DUMMY_CHANNEL_ID) + .process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID) .is_ok()); let get_assertion_params = AuthenticatorGetAssertionParameters { @@ -2107,6 +2098,7 @@ mod test { pin_uv_auth_protocol: None, }; let get_assertion_response = ctap_state.process_get_assertion( + &mut env, get_assertion_params, DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE, @@ -2124,7 +2116,7 @@ mod test { credential_id: Option>, pin_uv_auth_protocol: PinUvAuthProtocol, ) -> AuthenticatorGetAssertionParameters { - let mut rng = ThreadRng256 {}; + let mut env = TestEnv::new(); let platform_public_key = key_agreement_key.genpk(); let public_key = match key_agreement_response { ResponseData::AuthenticatorClientPin(Some(client_pin_response)) => { @@ -2138,7 +2130,7 @@ mod test { .unwrap(); let salt = vec![0x01; 32]; - let salt_enc = shared_secret.as_ref().encrypt(&mut rng, &salt).unwrap(); + let salt_enc = shared_secret.as_ref().encrypt(env.rng(), &salt).unwrap(); let salt_auth = shared_secret.authenticate(&salt_enc); let hmac_secret_input = GetAssertionHmacSecretInput { key_agreement: CoseKey::from(platform_public_key), @@ -2172,10 +2164,9 @@ mod test { } fn test_helper_process_get_assertion_hmac_secret(pin_uv_auth_protocol: PinUvAuthProtocol) { - let mut rng = ThreadRng256 {}; - let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); let make_extensions = MakeCredentialExtensions { hmac_secret: true, @@ -2185,7 +2176,7 @@ mod test { make_credential_params.options.rk = false; make_credential_params.extensions = make_extensions; let make_credential_response = - ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID); + ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID); assert!(make_credential_response.is_ok()); let credential_id = match make_credential_response.unwrap() { ResponseData::AuthenticatorMakeCredential(make_credential_response) => { @@ -2209,7 +2200,7 @@ mod test { permissions_rp_id: None, }; let key_agreement_response = ctap_state.client_pin.process_command( - ctap_state.rng, + env.rng(), &mut ctap_state.persistent_store, client_pin_params, DUMMY_CLOCK_VALUE, @@ -2221,6 +2212,7 @@ mod test { pin_uv_auth_protocol, ); let get_assertion_response = ctap_state.process_get_assertion( + &mut env, get_assertion_params, DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE, @@ -2241,10 +2233,9 @@ mod test { fn test_helper_resident_process_get_assertion_hmac_secret( pin_uv_auth_protocol: PinUvAuthProtocol, ) { - let mut rng = ThreadRng256 {}; - let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); let make_extensions = MakeCredentialExtensions { hmac_secret: true, @@ -2253,7 +2244,7 @@ mod test { let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.extensions = make_extensions; assert!(ctap_state - .process_make_credential(make_credential_params, DUMMY_CHANNEL_ID) + .process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID) .is_ok()); let client_pin_params = AuthenticatorClientPinParameters { @@ -2267,7 +2258,7 @@ mod test { permissions_rp_id: None, }; let key_agreement_response = ctap_state.client_pin.process_command( - ctap_state.rng, + env.rng(), &mut ctap_state.persistent_store, client_pin_params, DUMMY_CLOCK_VALUE, @@ -2279,6 +2270,7 @@ mod test { pin_uv_auth_protocol, ); let get_assertion_response = ctap_state.process_get_assertion( + &mut env, get_assertion_params, DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE, @@ -2298,11 +2290,10 @@ mod test { #[test] fn test_resident_process_get_assertion_with_cred_protect() { - let mut rng = ThreadRng256 {}; - let private_key = crypto::ecdsa::SecKey::gensk(&mut rng); - let credential_id = rng.gen_uniform_u8x32().to_vec(); - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let private_key = crypto::ecdsa::SecKey::gensk(env.rng()); + let credential_id = env.rng().gen_uniform_u8x32().to_vec(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); let cred_desc = PublicKeyCredentialDescriptor { key_type: PublicKeyCredentialType::PublicKey, @@ -2343,6 +2334,7 @@ mod test { pin_uv_auth_protocol: None, }; let get_assertion_response = ctap_state.process_get_assertion( + &mut env, get_assertion_params, DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE, @@ -2365,6 +2357,7 @@ mod test { pin_uv_auth_protocol: None, }; let get_assertion_response = ctap_state.process_get_assertion( + &mut env, get_assertion_params, DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE, @@ -2407,6 +2400,7 @@ mod test { pin_uv_auth_protocol: None, }; let get_assertion_response = ctap_state.process_get_assertion( + &mut env, get_assertion_params, DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE, @@ -2419,11 +2413,10 @@ mod test { #[test] fn test_process_get_assertion_with_cred_blob() { - let mut rng = ThreadRng256 {}; - let private_key = crypto::ecdsa::SecKey::gensk(&mut rng); - let credential_id = rng.gen_uniform_u8x32().to_vec(); - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let private_key = crypto::ecdsa::SecKey::gensk(env.rng()); + let credential_id = env.rng().gen_uniform_u8x32().to_vec(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); let credential = PublicKeyCredentialSource { key_type: PublicKeyCredentialType::PublicKey, @@ -2461,6 +2454,7 @@ mod test { pin_uv_auth_protocol: None, }; let get_assertion_response = ctap_state.process_get_assertion( + &mut env, get_assertion_params, DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE, @@ -2483,11 +2477,10 @@ mod test { #[test] fn test_process_get_assertion_with_large_blob_key() { - let mut rng = ThreadRng256 {}; - let private_key = crypto::ecdsa::SecKey::gensk(&mut rng); - let credential_id = rng.gen_uniform_u8x32().to_vec(); - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let private_key = crypto::ecdsa::SecKey::gensk(env.rng()); + let credential_id = env.rng().gen_uniform_u8x32().to_vec(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); let credential = PublicKeyCredentialSource { key_type: PublicKeyCredentialType::PublicKey, @@ -2525,6 +2518,7 @@ mod test { pin_uv_auth_protocol: None, }; let get_assertion_response = ctap_state.process_get_assertion( + &mut env, get_assertion_params, DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE, @@ -2541,14 +2535,13 @@ mod test { fn test_helper_process_get_next_assertion_two_credentials_with_uv( pin_uv_auth_protocol: PinUvAuthProtocol, ) { - let mut rng = ThreadRng256 {}; - let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); + 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 user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); let mut make_credential_params = create_minimal_make_credential_parameters(); let user1 = PublicKeyCredentialUserEntity { @@ -2559,7 +2552,7 @@ mod test { }; make_credential_params.user = user1.clone(); assert!(ctap_state - .process_make_credential(make_credential_params, DUMMY_CHANNEL_ID) + .process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID) .is_ok()); let mut make_credential_params = create_minimal_make_credential_parameters(); let user2 = PublicKeyCredentialUserEntity { @@ -2570,7 +2563,7 @@ mod test { }; make_credential_params.user = user2.clone(); assert!(ctap_state - .process_make_credential(make_credential_params, DUMMY_CHANNEL_ID) + .process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID) .is_ok()); ctap_state.client_pin = client_pin; @@ -2596,6 +2589,7 @@ mod test { 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_ID, DUMMY_CLOCK_VALUE, @@ -2613,7 +2607,8 @@ mod test { &[], ); - let get_assertion_response = ctap_state.process_get_next_assertion(DUMMY_CLOCK_VALUE); + let get_assertion_response = + ctap_state.process_get_next_assertion(&mut env, DUMMY_CLOCK_VALUE); check_assertion_response_with_user( get_assertion_response, user1, @@ -2623,7 +2618,8 @@ mod test { &[], ); - let get_assertion_response = ctap_state.process_get_next_assertion(DUMMY_CLOCK_VALUE); + let get_assertion_response = + ctap_state.process_get_next_assertion(&mut env, DUMMY_CLOCK_VALUE); assert_eq!( get_assertion_response, Err(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED) @@ -2642,9 +2638,8 @@ mod test { #[test] fn test_process_get_next_assertion_three_credentials_no_uv() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.user.user_id = vec![0x01]; @@ -2652,7 +2647,7 @@ mod test { make_credential_params.user.user_display_name = Some("removed".to_string()); make_credential_params.user.user_icon = Some("removed".to_string()); assert!(ctap_state - .process_make_credential(make_credential_params, DUMMY_CHANNEL_ID) + .process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID) .is_ok()); let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.user.user_id = vec![0x02]; @@ -2660,7 +2655,7 @@ mod test { make_credential_params.user.user_display_name = Some("removed".to_string()); make_credential_params.user.user_icon = Some("removed".to_string()); assert!(ctap_state - .process_make_credential(make_credential_params, DUMMY_CHANNEL_ID) + .process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID) .is_ok()); let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.user.user_id = vec![0x03]; @@ -2668,7 +2663,7 @@ mod test { make_credential_params.user.user_display_name = Some("removed".to_string()); make_credential_params.user.user_icon = Some("removed".to_string()); assert!(ctap_state - .process_make_credential(make_credential_params, DUMMY_CHANNEL_ID) + .process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID) .is_ok()); let get_assertion_params = AuthenticatorGetAssertionParameters { @@ -2684,6 +2679,7 @@ mod test { pin_uv_auth_protocol: None, }; let get_assertion_response = ctap_state.process_get_assertion( + &mut env, get_assertion_params, DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE, @@ -2699,13 +2695,16 @@ mod test { Some(3), ); - let get_assertion_response = ctap_state.process_get_next_assertion(DUMMY_CLOCK_VALUE); + let get_assertion_response = + ctap_state.process_get_next_assertion(&mut env, DUMMY_CLOCK_VALUE); check_assertion_response(get_assertion_response, vec![0x02], signature_counter, None); - let get_assertion_response = ctap_state.process_get_next_assertion(DUMMY_CLOCK_VALUE); + let get_assertion_response = + ctap_state.process_get_next_assertion(&mut env, DUMMY_CLOCK_VALUE); check_assertion_response(get_assertion_response, vec![0x01], signature_counter, None); - let get_assertion_response = ctap_state.process_get_next_assertion(DUMMY_CLOCK_VALUE); + let get_assertion_response = + ctap_state.process_get_next_assertion(&mut env, DUMMY_CLOCK_VALUE); assert_eq!( get_assertion_response, Err(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED) @@ -2714,11 +2713,11 @@ mod test { #[test] fn test_process_get_next_assertion_not_allowed() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); - let get_assertion_response = ctap_state.process_get_next_assertion(DUMMY_CLOCK_VALUE); + let get_assertion_response = + ctap_state.process_get_next_assertion(&mut env, DUMMY_CLOCK_VALUE); assert_eq!( get_assertion_response, Err(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED) @@ -2727,12 +2726,12 @@ mod test { let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.user.user_id = vec![0x01]; assert!(ctap_state - .process_make_credential(make_credential_params, DUMMY_CHANNEL_ID) + .process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID) .is_ok()); let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.user.user_id = vec![0x02]; assert!(ctap_state - .process_make_credential(make_credential_params, DUMMY_CHANNEL_ID) + .process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID) .is_ok()); let get_assertion_params = AuthenticatorGetAssertionParameters { @@ -2748,6 +2747,7 @@ mod test { pin_uv_auth_protocol: None, }; let get_assertion_response = ctap_state.process_get_assertion( + &mut env, get_assertion_params, DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE, @@ -2767,9 +2767,10 @@ mod test { 4 => cbor_array![ES256_CRED_PARAM], }; assert!(cbor_write(cbor_value, &mut command_cbor).is_ok()); - ctap_state.process_command(&command_cbor, DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE); + ctap_state.process_command(&mut env, &command_cbor, DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE); - let get_assertion_response = ctap_state.process_get_next_assertion(DUMMY_CLOCK_VALUE); + let get_assertion_response = + ctap_state.process_get_next_assertion(&mut env, DUMMY_CLOCK_VALUE); assert_eq!( get_assertion_response, Err(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED) @@ -2778,10 +2779,9 @@ mod test { #[test] fn test_process_reset() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let private_key = crypto::ecdsa::SecKey::gensk(&mut rng); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let private_key = crypto::ecdsa::SecKey::gensk(env.rng()); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); let credential_id = vec![0x01, 0x23, 0x45, 0x67]; let credential_source = PublicKeyCredentialSource { @@ -2805,7 +2805,7 @@ mod test { assert!(ctap_state.persistent_store.count_credentials().unwrap() > 0); let reset_reponse = - ctap_state.process_command(&[0x07], DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE); + ctap_state.process_command(&mut env, &[0x07], DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE); let expected_response = vec![0x00]; assert_eq!(reset_reponse, expected_response); assert!(ctap_state.persistent_store.count_credentials().unwrap() == 0); @@ -2813,12 +2813,12 @@ mod test { #[test] fn test_process_reset_cancelled() { - let mut rng = ThreadRng256 {}; - let user_presence_always_cancel = |_| Err(Ctap2StatusCode::CTAP2_ERR_KEEPALIVE_CANCEL); - let mut ctap_state = - CtapState::new(&mut rng, user_presence_always_cancel, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + env.user_presence() + .set(|_| Err(Ctap2StatusCode::CTAP2_ERR_KEEPALIVE_CANCEL)); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); - let reset_reponse = ctap_state.process_reset(DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE); + let reset_reponse = ctap_state.process_reset(&mut env, DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE); assert_eq!( reset_reponse, @@ -2828,25 +2828,24 @@ mod test { #[test] fn test_process_reset_not_first() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); // This is a GetNextAssertion command. - ctap_state.process_command(&[0x08], DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE); + ctap_state.process_command(&mut env, &[0x08], DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE); - let reset_reponse = ctap_state.process_reset(DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE); + let reset_reponse = ctap_state.process_reset(&mut env, DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE); assert_eq!(reset_reponse, Err(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED)); } #[test] fn test_process_credential_management_unknown_subcommand() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); // The subcommand 0xEE does not exist. let reponse = ctap_state.process_command( + &mut env, &[0x0A, 0xA1, 0x01, 0x18, 0xEE], DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE, @@ -2857,28 +2856,27 @@ mod test { #[test] fn test_process_unknown_command() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); // This command does not exist. - let reponse = ctap_state.process_command(&[0xDF], DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE); + let reponse = + ctap_state.process_command(&mut env, &[0xDF], DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE); let expected_response = vec![Ctap2StatusCode::CTAP1_ERR_INVALID_COMMAND as u8]; assert_eq!(reponse, expected_response); } #[test] fn test_encrypt_decrypt_credential() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let private_key = crypto::ecdsa::SecKey::gensk(&mut rng); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let private_key = crypto::ecdsa::SecKey::gensk(env.rng()); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); // Usually, the relying party ID or its hash is provided by the client. // We are not testing the correctness of our SHA256 here, only if it is checked. let rp_id_hash = [0x55; 32]; let encrypted_id = ctap_state - .encrypt_key_handle(private_key.clone(), &rp_id_hash) + .encrypt_key_handle(&mut env, private_key.clone(), &rp_id_hash) .unwrap(); let decrypted_source = ctap_state .decrypt_credential_source(encrypted_id, &rp_id_hash) @@ -2890,15 +2888,14 @@ mod test { #[test] fn test_encrypt_decrypt_bad_hmac() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let private_key = crypto::ecdsa::SecKey::gensk(&mut rng); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let private_key = crypto::ecdsa::SecKey::gensk(env.rng()); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); // Same as above. let rp_id_hash = [0x55; 32]; let encrypted_id = ctap_state - .encrypt_key_handle(private_key, &rp_id_hash) + .encrypt_key_handle(&mut env, private_key, &rp_id_hash) .unwrap(); for i in 0..encrypted_id.len() { let mut modified_id = encrypted_id.clone(); @@ -2912,9 +2909,8 @@ mod test { #[test] fn test_signature_counter() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); let mut last_counter = ctap_state .persistent_store @@ -2922,7 +2918,9 @@ mod test { .unwrap(); assert!(last_counter > 0); for _ in 0..100 { - assert!(ctap_state.increment_global_signature_counter().is_ok()); + assert!(ctap_state + .increment_global_signature_counter(&mut env) + .is_ok()); let next_counter = ctap_state .persistent_store .global_signature_counter() @@ -2934,12 +2932,12 @@ mod test { #[test] fn test_vendor_configure() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); // Nothing should be configured at the beginning let response = ctap_state.process_vendor_configure( + &mut env, AuthenticatorVendorConfigureParameters { lockdown: false, attestation_material: None, @@ -2960,6 +2958,7 @@ mod test { let dummy_key = [0x41u8; key_material::ATTESTATION_PRIVATE_KEY_LENGTH]; let dummy_cert = [0xddu8; 20]; let response = ctap_state.process_vendor_configure( + &mut env, AuthenticatorVendorConfigureParameters { lockdown: false, attestation_material: Some(AuthenticatorAttestationMaterial { @@ -2998,6 +2997,7 @@ mod test { // Try to inject other dummy values and check that initial values are retained. let other_dummy_key = [0x44u8; key_material::ATTESTATION_PRIVATE_KEY_LENGTH]; let response = ctap_state.process_vendor_configure( + &mut env, AuthenticatorVendorConfigureParameters { lockdown: false, attestation_material: Some(AuthenticatorAttestationMaterial { @@ -3035,6 +3035,7 @@ mod test { // Now try to lock the device let response = ctap_state.process_vendor_configure( + &mut env, AuthenticatorVendorConfigureParameters { lockdown: true, attestation_material: None, @@ -3054,9 +3055,8 @@ mod test { #[test] fn test_parse_metadata() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); // The test buffer starts fully erased with 0xFF bytes. // The compiler issues an incorrect warning. #[allow(unused_mut)] @@ -3094,8 +3094,8 @@ mod test { #[test] fn test_verify_signature() { - let mut rng = ThreadRng256 {}; - let private_key = crypto::ecdsa::SecKey::gensk(&mut rng); + let mut env = TestEnv::new(); + let private_key = crypto::ecdsa::SecKey::gensk(env.rng()); let message = [0x44; 64]; let signed_hash = Sha256::hash(&message); let signature = private_key.sign_rfc6979::(&message); @@ -3153,10 +3153,9 @@ mod test { // The test partition storage has size 0x40000. // The test metadata storage has size 0x1000. // The test identifier matches partition B. - let mut rng = ThreadRng256 {}; - let private_key = crypto::ecdsa::SecKey::gensk(&mut rng); - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let private_key = crypto::ecdsa::SecKey::gensk(env.rng()); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); const METADATA_LEN: usize = 40; let data = vec![0xFF; 0x1000]; @@ -3230,9 +3229,8 @@ mod test { #[test] fn test_vendor_upgrade_no_second_partition() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); ctap_state.upgrade_locations = None; let data = vec![0xFF; 0x1000]; @@ -3248,9 +3246,8 @@ mod test { #[test] fn test_vendor_upgrade_info() { - let mut rng = ThreadRng256 {}; - let user_immediately_present = |_| Ok(()); - let ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); + let mut env = TestEnv::new(); + let ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); let partition_address = ctap_state .upgrade_locations .as_ref() diff --git a/src/ctap/status_code.rs b/src/ctap/status_code.rs index a593dad..d026ec1 100644 --- a/src/ctap/status_code.rs +++ b/src/ctap/status_code.rs @@ -16,7 +16,7 @@ // For now, only the CTAP2 codes are here, the CTAP1 are not included. #[allow(non_camel_case_types)] #[allow(dead_code)] -#[derive(Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq)] pub enum Ctap2StatusCode { CTAP2_OK = 0x00, CTAP1_ERR_INVALID_COMMAND = 0x01, diff --git a/src/env/mod.rs b/src/env/mod.rs new file mode 100644 index 0000000..dc79184 --- /dev/null +++ b/src/env/mod.rs @@ -0,0 +1,18 @@ +use crate::ctap::hid::ChannelID; +use crate::ctap::status_code::Ctap2StatusCode; +use crypto::rng256::Rng256; + +#[cfg(feature = "std")] +pub mod test; + +pub trait UserPresence { + fn check(&self, cid: ChannelID) -> Result<(), Ctap2StatusCode>; +} + +pub trait Env { + type Rng: Rng256; + type UserPresence: UserPresence; + + fn rng(&mut self) -> &mut Self::Rng; + fn user_presence(&mut self) -> &mut Self::UserPresence; +} diff --git a/src/env/test.rs b/src/env/test.rs new file mode 100644 index 0000000..7d81f4a --- /dev/null +++ b/src/env/test.rs @@ -0,0 +1,48 @@ +use crate::ctap::hid::ChannelID; +use crate::ctap::status_code::Ctap2StatusCode; +use crate::env::{Env, UserPresence}; +use crypto::rng256::ThreadRng256; + +pub struct TestEnv { + rng: ThreadRng256, + user_presence: TestUserPresence, +} + +pub struct TestUserPresence { + check: Box Result<(), Ctap2StatusCode>>, +} + +impl TestEnv { + pub fn new() -> Self { + let rng = ThreadRng256 {}; + let user_presence = TestUserPresence { + check: Box::new(|_| Ok(())), + }; + TestEnv { rng, user_presence } + } +} + +impl TestUserPresence { + pub fn set(&mut self, check: impl Fn(ChannelID) -> Result<(), Ctap2StatusCode> + 'static) { + self.check = Box::new(check); + } +} + +impl UserPresence for TestUserPresence { + fn check(&self, cid: ChannelID) -> Result<(), Ctap2StatusCode> { + (self.check)(cid) + } +} + +impl Env for TestEnv { + type Rng = ThreadRng256; + type UserPresence = TestUserPresence; + + fn rng(&mut self) -> &mut Self::Rng { + &mut self.rng + } + + fn user_presence(&mut self) -> &mut Self::UserPresence { + &mut self.user_presence + } +} diff --git a/src/lib.rs b/src/lib.rs index a5c4cba..12d2301 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,9 +15,44 @@ #![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; +#[macro_use] +extern crate arrayref; + +use crate::ctap::hid::send::HidPacketIterator; +use crate::ctap::hid::{CtapHid, HidPacket}; +use crate::ctap::CtapState; +use crate::env::Env; +use libtock_drivers::timer::ClockValue; pub mod ctap; pub mod embedded_flash; +pub mod env; -#[macro_use] -extern crate arrayref; +pub struct Ctap { + env: E, + state: CtapState, + hid: CtapHid, +} + +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: ClockValue) -> Self { + let state = CtapState::new(&mut env, now); + let hid = CtapHid::new(); + Ctap { env, state, hid } + } + + pub fn state(&mut self) -> &mut CtapState { + &mut self.state + } + + pub fn hid(&mut self) -> &mut CtapHid { + &mut self.hid + } + + pub fn process_hid_packet(&mut self, packet: &HidPacket, now: ClockValue) -> HidPacketIterator { + self.hid + .process_hid_packet(&mut self.env, packet, now, &mut self.state) + } +} diff --git a/src/main.rs b/src/main.rs index a471e55..4332fe3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,23 +15,19 @@ #![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; +extern crate arrayref; +extern crate byteorder; #[cfg(feature = "std")] extern crate core; extern crate lang_items; -#[macro_use] -extern crate arrayref; -extern crate byteorder; - -mod ctap; -pub mod embedded_flash; use core::cell::Cell; #[cfg(feature = "debug_ctap")] use core::fmt::Write; use crypto::rng256::TockRng256; -use ctap::hid::{ChannelID, CtapHid, KeepaliveStatus, ProcessedPacket}; -use ctap::status_code::Ctap2StatusCode; -use ctap::CtapState; +use ctap2::ctap::hid::{ChannelID, CtapHid, KeepaliveStatus, ProcessedPacket}; +use ctap2::ctap::status_code::Ctap2StatusCode; +use ctap2::env; use libtock_core::result::{CommandError, EALREADY}; use libtock_drivers::buttons; use libtock_drivers::buttons::ButtonState; @@ -53,6 +49,31 @@ const KEEPALIVE_DELAY_MS: isize = 100; const KEEPALIVE_DELAY: Duration = Duration::from_ms(KEEPALIVE_DELAY_MS); const SEND_TIMEOUT: Duration = Duration::from_ms(1000); +struct TockEnv { + rng: TockRng256, + user_presence: UserPresence, +} + +struct UserPresence; +impl env::UserPresence for UserPresence { + fn check(&self, cid: ChannelID) -> Result<(), Ctap2StatusCode> { + check_user_presence(cid) + } +} + +impl env::Env for TockEnv { + type Rng = TockRng256; + type UserPresence = UserPresence; + + fn rng(&mut self) -> &mut Self::Rng { + &mut self.rng + } + + fn user_presence(&mut self) -> &mut Self::UserPresence { + &mut self.user_presence + } +} + fn main() { // Setup the timer with a dummy callback (we only care about reading the current time, but the // API forces us to set an alarm callback too). @@ -65,9 +86,11 @@ fn main() { } let boot_time = timer.get_current_clock().flex_unwrap(); - let mut rng = TockRng256 {}; - let mut ctap_state = CtapState::new(&mut rng, check_user_presence, boot_time); - let mut ctap_hid = CtapHid::new(); + let env = TockEnv { + rng: TockRng256 {}, + user_presence: UserPresence, + }; + let mut ctap = ctap2::Ctap::new(env, boot_time); let mut led_counter = 0; let mut last_led_increment = boot_time; @@ -109,7 +132,7 @@ fn main() { #[cfg(feature = "with_ctap1")] { if button_touched.get() { - ctap_state.u2f_up_state.grant_up(now); + ctap.state().u2f_up_state.grant_up(now); } // Cleanup button callbacks. We miss button presses while processing though. // Heavy computation mostly follows a registered touch luckily. Unregistering @@ -123,11 +146,11 @@ fn main() { // These calls are making sure that even for long inactivity, wrapping clock values // don't cause problems with timers. - ctap_state.update_timeouts(now); - ctap_hid.wink_permission = ctap_hid.wink_permission.check_expiration(now); + ctap.state().update_timeouts(now); + ctap.hid().wink_permission = ctap.hid().wink_permission.check_expiration(now); if has_packet { - let reply = ctap_hid.process_hid_packet(&pkt_request, now, &mut ctap_state); + let reply = ctap.process_hid_packet(&pkt_request, now); // This block handles sending packets. for mut pkt_reply in reply { let status = usb_ctap_hid::send_or_recv_with_timeout(&mut pkt_reply, SEND_TIMEOUT); @@ -167,14 +190,14 @@ fn main() { last_led_increment = now; } - if ctap_hid.wink_permission.is_granted(now) { + if ctap.hid().wink_permission.is_granted(now) { wink_leds(led_counter); } else { #[cfg(not(feature = "with_ctap1"))] switch_off_leds(); #[cfg(feature = "with_ctap1")] { - if ctap_state.u2f_up_state.is_up_needed(now) { + if ctap.state().u2f_up_state.is_up_needed(now) { // Flash the LEDs with an almost regular pattern. The inaccuracy comes from // delay caused by processing and sending of packets. blink_leds(led_counter); @@ -315,7 +338,8 @@ fn switch_off_leds() { fn check_user_presence(cid: ChannelID) -> Result<(), Ctap2StatusCode> { // The timeout is N times the keepalive delay. - const TIMEOUT_ITERATIONS: usize = ctap::TOUCH_TIMEOUT_MS as usize / KEEPALIVE_DELAY_MS as usize; + const TIMEOUT_ITERATIONS: usize = + ctap2::ctap::TOUCH_TIMEOUT_MS as usize / KEEPALIVE_DELAY_MS as usize; // First, send a keep-alive packet to notify that the keep-alive status has changed. send_keepalive_up_needed(cid, KEEPALIVE_DELAY)?;