From c4a27bf9357f3281be3a17733b5daeff59ae1e20 Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Thu, 3 Mar 2022 16:36:45 +0100 Subject: [PATCH] Add Storage and UpgradeStorage to Env --- examples/erase_storage.rs | 4 +- examples/store_latency.rs | 9 +- src/api/mod.rs | 6 + .../upgrade_storage}/helper.rs | 0 .../upgrade_storage/mod.rs} | 2 + src/ctap/client_pin.rs | 228 +++++++++--------- src/ctap/config_command.rs | 67 ++--- src/ctap/credential_management.rs | 41 ++-- src/ctap/ctap1.rs | 18 +- src/ctap/hid/mod.rs | 18 +- src/ctap/large_blobs.rs | 49 ++-- src/ctap/mod.rs | 41 ++-- src/ctap/storage.rs | 181 +++++++------- src/embedded_flash/mod.rs | 65 ----- src/env/mod.rs | 14 ++ src/env/{test.rs => test/mod.rs} | 25 ++ .../test/upgrade_storage.rs} | 4 +- src/env/{tock.rs => tock/mod.rs} | 52 +++- .../syscall.rs => env/tock/storage.rs} | 4 +- src/lib.rs | 7 +- src/main.rs | 2 +- 21 files changed, 438 insertions(+), 399 deletions(-) create mode 100644 src/api/mod.rs rename src/{embedded_flash => api/upgrade_storage}/helper.rs (100%) rename src/{embedded_flash/upgrade_storage.rs => api/upgrade_storage/mod.rs} (98%) delete mode 100644 src/embedded_flash/mod.rs rename src/env/{test.rs => test/mod.rs} (57%) rename src/{embedded_flash/buffer_upgrade.rs => env/test/upgrade_storage.rs} (98%) rename src/env/{tock.rs => tock/mod.rs} (82%) rename src/{embedded_flash/syscall.rs => env/tock/storage.rs} (98%) diff --git a/examples/erase_storage.rs b/examples/erase_storage.rs index da0e445..29ba5ea 100644 --- a/examples/erase_storage.rs +++ b/examples/erase_storage.rs @@ -17,7 +17,7 @@ extern crate lang_items; use core::fmt::Write; -use ctap2::embedded_flash::new_storage; +use ctap2::env::tock::steal_storage; use libtock_drivers::console::Console; use libtock_drivers::led; use libtock_drivers::result::FlexUnwrap; @@ -37,7 +37,7 @@ fn is_page_erased(storage: &dyn Storage, page: usize) -> bool { fn main() { led::get(1).flex_unwrap().on().flex_unwrap(); // red on dongle - let mut storage = new_storage().unwrap(); + let mut storage = unsafe { steal_storage() }.unwrap(); let num_pages = storage.num_pages(); writeln!(Console::new(), "Erase {} pages of storage:", num_pages).unwrap(); for page in 0..num_pages { diff --git a/examples/store_latency.rs b/examples/store_latency.rs index 2ea3a91..27f2af7 100644 --- a/examples/store_latency.rs +++ b/examples/store_latency.rs @@ -21,7 +21,8 @@ use alloc::string::{String, ToString}; use alloc::vec::Vec; use alloc::{format, vec}; use core::fmt::Write; -use ctap2::embedded_flash::{new_storage, Storage}; +use ctap2::env::tock::{steal_storage, TockEnv}; +use ctap2::env::Env; use libtock_drivers::console::Console; use libtock_drivers::timer::{self, Duration, Timer, Timestamp}; use persistent_store::Store; @@ -40,9 +41,9 @@ fn measure(timer: &Timer, operation: impl FnOnce() -> T) -> (T, Duration } // Only use one store at a time. -unsafe fn boot_store(erase: bool) -> Store { +unsafe fn boot_store(erase: bool) -> Store<::Storage> { use persistent_store::Storage; - let mut storage = new_storage().unwrap(); + let mut storage = steal_storage().unwrap(); let num_pages = storage.num_pages(); if erase { for page in 0..num_pages { @@ -59,7 +60,7 @@ struct StorageConfig { fn storage_config() -> StorageConfig { use persistent_store::Storage; - let storage = new_storage().unwrap(); + let storage = unsafe { steal_storage() }.unwrap(); StorageConfig { num_pages: storage.num_pages(), } diff --git a/src/api/mod.rs b/src/api/mod.rs new file mode 100644 index 0000000..b8c5bcb --- /dev/null +++ b/src/api/mod.rs @@ -0,0 +1,6 @@ +//! APIs for the environment. +//! +//! The [environment](crate::env::Env) is split into components. Each component has an API described +//! by a trait. This module gathers the API of those components. + +pub mod upgrade_storage; diff --git a/src/embedded_flash/helper.rs b/src/api/upgrade_storage/helper.rs similarity index 100% rename from src/embedded_flash/helper.rs rename to src/api/upgrade_storage/helper.rs diff --git a/src/embedded_flash/upgrade_storage.rs b/src/api/upgrade_storage/mod.rs similarity index 98% rename from src/embedded_flash/upgrade_storage.rs rename to src/api/upgrade_storage/mod.rs index 44e8583..408fcf1 100644 --- a/src/embedded_flash/upgrade_storage.rs +++ b/src/api/upgrade_storage/mod.rs @@ -14,6 +14,8 @@ use persistent_store::StorageResult; +pub(crate) mod helper; + /// Accessors to storage locations used for upgrading from a CTAP command. pub trait UpgradeStorage { /// Reads a slice of the partition, if within bounds. diff --git a/src/ctap/client_pin.rs b/src/ctap/client_pin.rs index d8ab06e..2cac1e0 100644 --- a/src/ctap/client_pin.rs +++ b/src/ctap/client_pin.rs @@ -21,6 +21,7 @@ use super::response::{AuthenticatorClientPinResponse, ResponseData}; use super::status_code::Ctap2StatusCode; use super::storage::PersistentStore; use super::token_state::PinUvAuthTokenState; +use crate::env::Env; use alloc::boxed::Box; use alloc::str; use alloc::string::String; @@ -75,8 +76,8 @@ fn decrypt_pin( /// The new PIN is passed encrypted, so it is first decrypted and stripped from /// padding. Next, it is checked against the PIN policy. Last, it is hashed and /// truncated for persistent storage. -fn check_and_store_new_pin( - persistent_store: &mut PersistentStore, +fn check_and_store_new_pin( + persistent_store: &mut PersistentStore, shared_secret: &dyn SharedSecret, new_pin_enc: Vec, ) -> Result<(), Ctap2StatusCode> { @@ -155,10 +156,10 @@ impl ClientPin { /// Decrypts the encrypted pin_hash and compares it to the stored pin_hash. /// Resets or decreases the PIN retries, depending on success or failure. /// Also, in case of failure, the key agreement key is randomly reset. - fn verify_pin_hash_enc( + fn verify_pin_hash_enc( &mut self, rng: &mut impl Rng256, - persistent_store: &mut PersistentStore, + persistent_store: &mut PersistentStore, pin_uv_auth_protocol: PinUvAuthProtocol, shared_secret: &dyn SharedSecret, pin_hash_enc: Vec, @@ -194,9 +195,9 @@ impl ClientPin { Ok(()) } - fn process_get_pin_retries( + fn process_get_pin_retries( &self, - persistent_store: &PersistentStore, + persistent_store: &PersistentStore, ) -> Result { Ok(AuthenticatorClientPinResponse { key_agreement: None, @@ -222,9 +223,9 @@ impl ClientPin { }) } - fn process_set_pin( + fn process_set_pin( &mut self, - persistent_store: &mut PersistentStore, + persistent_store: &mut PersistentStore, client_pin_params: AuthenticatorClientPinParameters, ) -> Result<(), Ctap2StatusCode> { let AuthenticatorClientPinParameters { @@ -249,10 +250,10 @@ impl ClientPin { Ok(()) } - fn process_change_pin( + fn process_change_pin( &mut self, rng: &mut impl Rng256, - persistent_store: &mut PersistentStore, + persistent_store: &mut PersistentStore, client_pin_params: AuthenticatorClientPinParameters, ) -> Result<(), Ctap2StatusCode> { let AuthenticatorClientPinParameters { @@ -289,10 +290,10 @@ impl ClientPin { Ok(()) } - fn process_get_pin_token( + fn process_get_pin_token( &mut self, rng: &mut impl Rng256, - persistent_store: &mut PersistentStore, + persistent_store: &mut PersistentStore, client_pin_params: AuthenticatorClientPinParameters, now: ClockValue, ) -> Result { @@ -359,10 +360,10 @@ impl ClientPin { Err(Ctap2StatusCode::CTAP2_ERR_INVALID_SUBCOMMAND) } - fn process_get_pin_uv_auth_token_using_pin_with_permissions( + fn process_get_pin_uv_auth_token_using_pin_with_permissions( &mut self, rng: &mut impl Rng256, - persistent_store: &mut PersistentStore, + persistent_store: &mut PersistentStore, mut client_pin_params: AuthenticatorClientPinParameters, now: ClockValue, ) -> Result { @@ -389,10 +390,10 @@ impl ClientPin { } /// Processes the authenticatorClientPin command. - pub fn process_command( + pub fn process_command( &mut self, rng: &mut impl Rng256, - persistent_store: &mut PersistentStore, + persistent_store: &mut PersistentStore, client_pin_params: AuthenticatorClientPinParameters, now: ClockValue, ) -> Result { @@ -570,11 +571,10 @@ impl ClientPin { pin_uv_auth_token: [u8; PIN_TOKEN_LENGTH], pin_uv_auth_protocol: PinUvAuthProtocol, ) -> ClientPin { - use crypto::rng256::ThreadRng256; - let mut rng = ThreadRng256 {}; + let mut env = crate::env::test::TestEnv::new(); let (key_agreement_key_v1, key_agreement_key_v2) = match pin_uv_auth_protocol { - PinUvAuthProtocol::V1 => (key_agreement_key, crypto::ecdh::SecKey::gensk(&mut rng)), - PinUvAuthProtocol::V2 => (crypto::ecdh::SecKey::gensk(&mut rng), key_agreement_key), + PinUvAuthProtocol::V1 => (key_agreement_key, crypto::ecdh::SecKey::gensk(env.rng())), + PinUvAuthProtocol::V2 => (crypto::ecdh::SecKey::gensk(env.rng()), key_agreement_key), }; let mut pin_uv_auth_token_state = PinUvAuthTokenState::new(); pin_uv_auth_token_state.set_permissions(0xFF); @@ -594,15 +594,15 @@ impl ClientPin { mod test { use super::super::pin_protocol::authenticate_pin_uv_auth_token; use super::*; + use crate::env::test::TestEnv; use alloc::vec; - use crypto::rng256::ThreadRng256; use libtock_drivers::timer::Duration; const CLOCK_FREQUENCY_HZ: usize = 32768; const DUMMY_CLOCK_VALUE: ClockValue = ClockValue::new(0, CLOCK_FREQUENCY_HZ); /// Stores a PIN hash corresponding to the dummy PIN "1234". - fn set_standard_pin(persistent_store: &mut PersistentStore) { + fn set_standard_pin(persistent_store: &mut PersistentStore) { let mut pin = [0u8; 64]; pin[..4].copy_from_slice(b"1234"); let mut pin_hash = [0u8; 16]; @@ -613,10 +613,10 @@ mod test { /// Fails on PINs bigger than 64 bytes. fn encrypt_pin(shared_secret: &dyn SharedSecret, pin: Vec) -> Vec { assert!(pin.len() <= 64); - let mut rng = ThreadRng256 {}; + let mut env = TestEnv::new(); let mut padded_pin = [0u8; 64]; padded_pin[..pin.len()].copy_from_slice(&pin[..]); - shared_secret.encrypt(&mut rng, &padded_pin).unwrap() + shared_secret.encrypt(env.rng(), &padded_pin).unwrap() } /// Generates a ClientPin instance and a shared secret for testing. @@ -628,8 +628,8 @@ mod test { fn create_client_pin_and_shared_secret( pin_uv_auth_protocol: PinUvAuthProtocol, ) -> (ClientPin, Box) { - 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 pk = key_agreement_key.genpk(); let key_agreement = CoseKey::from(pk); let pin_uv_auth_token = [0x91; PIN_TOKEN_LENGTH]; @@ -649,7 +649,7 @@ mod test { pin_uv_auth_protocol: PinUvAuthProtocol, sub_command: ClientPinSubCommand, ) -> (ClientPin, AuthenticatorClientPinParameters) { - let mut rng = ThreadRng256 {}; + let mut env = TestEnv::new(); let (client_pin, shared_secret) = create_client_pin_and_shared_secret(pin_uv_auth_protocol); let pin = b"1234"; @@ -658,12 +658,12 @@ mod test { let pin_hash = Sha256::hash(&padded_pin); let new_pin_enc = shared_secret .as_ref() - .encrypt(&mut rng, &padded_pin) + .encrypt(env.rng(), &padded_pin) .unwrap(); let pin_uv_auth_param = shared_secret.as_ref().authenticate(&new_pin_enc); let pin_hash_enc = shared_secret .as_ref() - .encrypt(&mut rng, &pin_hash[..16]) + .encrypt(env.rng(), &pin_hash[..16]) .unwrap(); let (permissions, permissions_rp_id) = match sub_command { ClientPinSubCommand::GetPinUvAuthTokenUsingUvWithPermissions @@ -691,8 +691,8 @@ mod test { #[test] fn test_mix_pin_protocols() { - let mut rng = ThreadRng256 {}; - let client_pin = ClientPin::new(&mut rng); + let mut env = TestEnv::new(); + let client_pin = ClientPin::new(env.rng()); let pin_protocol_v1 = client_pin.get_pin_protocol(PinUvAuthProtocol::V1); let pin_protocol_v2 = client_pin.get_pin_protocol(PinUvAuthProtocol::V2); let message = vec![0xAA; 16]; @@ -703,38 +703,38 @@ mod test { let shared_secret_v2 = pin_protocol_v2 .decapsulate(pin_protocol_v2.get_public_key(), PinUvAuthProtocol::V2) .unwrap(); - let ciphertext = shared_secret_v1.encrypt(&mut rng, &message).unwrap(); + let ciphertext = shared_secret_v1.encrypt(env.rng(), &message).unwrap(); let plaintext = shared_secret_v2.decrypt(&ciphertext).unwrap(); assert_ne!(&message, &plaintext); - let ciphertext = shared_secret_v2.encrypt(&mut rng, &message).unwrap(); + let ciphertext = shared_secret_v2.encrypt(env.rng(), &message).unwrap(); let plaintext = shared_secret_v1.decrypt(&ciphertext).unwrap(); assert_ne!(&message, &plaintext); let fake_secret_v1 = pin_protocol_v1 .decapsulate(pin_protocol_v2.get_public_key(), PinUvAuthProtocol::V1) .unwrap(); - let ciphertext = shared_secret_v1.encrypt(&mut rng, &message).unwrap(); + let ciphertext = shared_secret_v1.encrypt(env.rng(), &message).unwrap(); let plaintext = fake_secret_v1.decrypt(&ciphertext).unwrap(); assert_ne!(&message, &plaintext); - let ciphertext = fake_secret_v1.encrypt(&mut rng, &message).unwrap(); + let ciphertext = fake_secret_v1.encrypt(env.rng(), &message).unwrap(); let plaintext = shared_secret_v1.decrypt(&ciphertext).unwrap(); assert_ne!(&message, &plaintext); let fake_secret_v2 = pin_protocol_v2 .decapsulate(pin_protocol_v1.get_public_key(), PinUvAuthProtocol::V2) .unwrap(); - let ciphertext = shared_secret_v2.encrypt(&mut rng, &message).unwrap(); + let ciphertext = shared_secret_v2.encrypt(env.rng(), &message).unwrap(); let plaintext = fake_secret_v2.decrypt(&ciphertext).unwrap(); assert_ne!(&message, &plaintext); - let ciphertext = fake_secret_v2.encrypt(&mut rng, &message).unwrap(); + let ciphertext = fake_secret_v2.encrypt(env.rng(), &message).unwrap(); let plaintext = shared_secret_v2.decrypt(&ciphertext).unwrap(); assert_ne!(&message, &plaintext); } fn test_helper_verify_pin_hash_enc(pin_uv_auth_protocol: PinUvAuthProtocol) { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); - let mut client_pin = ClientPin::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); + let mut client_pin = ClientPin::new(env.rng()); let pin_protocol = client_pin.get_pin_protocol(pin_uv_auth_protocol); let shared_secret = pin_protocol .decapsulate(pin_protocol.get_public_key(), pin_uv_auth_protocol) @@ -746,10 +746,13 @@ mod test { ]; persistent_store.set_pin(&pin_hash, 4).unwrap(); - let pin_hash_enc = shared_secret.as_ref().encrypt(&mut rng, &pin_hash).unwrap(); + let pin_hash_enc = shared_secret + .as_ref() + .encrypt(env.rng(), &pin_hash) + .unwrap(); assert_eq!( client_pin.verify_pin_hash_enc( - &mut rng, + env.rng(), &mut persistent_store, pin_uv_auth_protocol, shared_secret.as_ref(), @@ -761,7 +764,7 @@ mod test { let pin_hash_enc = vec![0xEE; 16]; assert_eq!( client_pin.verify_pin_hash_enc( - &mut rng, + env.rng(), &mut persistent_store, pin_uv_auth_protocol, shared_secret.as_ref(), @@ -770,11 +773,14 @@ mod test { Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID) ); - let pin_hash_enc = shared_secret.as_ref().encrypt(&mut rng, &pin_hash).unwrap(); + let pin_hash_enc = shared_secret + .as_ref() + .encrypt(env.rng(), &pin_hash) + .unwrap(); client_pin.consecutive_pin_mismatches = 3; assert_eq!( client_pin.verify_pin_hash_enc( - &mut rng, + env.rng(), &mut persistent_store, pin_uv_auth_protocol, shared_secret.as_ref(), @@ -787,7 +793,7 @@ mod test { let pin_hash_enc = vec![0x77; PIN_AUTH_LENGTH - 1]; assert_eq!( client_pin.verify_pin_hash_enc( - &mut rng, + env.rng(), &mut persistent_store, pin_uv_auth_protocol, shared_secret.as_ref(), @@ -799,7 +805,7 @@ mod test { let pin_hash_enc = vec![0x77; PIN_AUTH_LENGTH + 1]; assert_eq!( client_pin.verify_pin_hash_enc( - &mut rng, + env.rng(), &mut persistent_store, pin_uv_auth_protocol, shared_secret.as_ref(), @@ -824,8 +830,8 @@ mod test { pin_uv_auth_protocol, ClientPinSubCommand::GetPinRetries, ); - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); let expected_response = Some(AuthenticatorClientPinResponse { key_agreement: None, pin_uv_auth_token: None, @@ -834,7 +840,7 @@ mod test { }); assert_eq!( client_pin.process_command( - &mut rng, + env.rng(), &mut persistent_store, params.clone(), DUMMY_CLOCK_VALUE @@ -850,7 +856,7 @@ mod test { power_cycle_state: Some(true), }); assert_eq!( - client_pin.process_command(&mut rng, &mut persistent_store, params, DUMMY_CLOCK_VALUE), + client_pin.process_command(env.rng(), &mut persistent_store, params, DUMMY_CLOCK_VALUE), Ok(ResponseData::AuthenticatorClientPin(expected_response)) ); } @@ -870,8 +876,8 @@ mod test { pin_uv_auth_protocol, ClientPinSubCommand::GetKeyAgreement, ); - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); let expected_response = Some(AuthenticatorClientPinResponse { key_agreement: params.key_agreement.clone(), pin_uv_auth_token: None, @@ -879,7 +885,7 @@ mod test { power_cycle_state: None, }); assert_eq!( - client_pin.process_command(&mut rng, &mut persistent_store, params, DUMMY_CLOCK_VALUE), + client_pin.process_command(env.rng(), &mut persistent_store, params, DUMMY_CLOCK_VALUE), Ok(ResponseData::AuthenticatorClientPin(expected_response)) ); } @@ -897,10 +903,10 @@ mod test { fn test_helper_process_set_pin(pin_uv_auth_protocol: PinUvAuthProtocol) { let (mut client_pin, params) = create_client_pin_and_parameters(pin_uv_auth_protocol, ClientPinSubCommand::SetPin); - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); assert_eq!( - client_pin.process_command(&mut rng, &mut persistent_store, params, DUMMY_CLOCK_VALUE), + client_pin.process_command(env.rng(), &mut persistent_store, params, DUMMY_CLOCK_VALUE), Ok(ResponseData::AuthenticatorClientPin(None)) ); } @@ -925,8 +931,8 @@ mod test { params.pin_uv_auth_protocol, ) .unwrap(); - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); set_standard_pin(&mut persistent_store); let mut auth_param_data = params.new_pin_enc.clone().unwrap(); @@ -935,7 +941,7 @@ mod test { params.pin_uv_auth_param = Some(pin_uv_auth_param); assert_eq!( client_pin.process_command( - &mut rng, + env.rng(), &mut persistent_store, params.clone(), DUMMY_CLOCK_VALUE @@ -947,7 +953,7 @@ mod test { bad_params.pin_hash_enc = Some(vec![0xEE; 16]); assert_eq!( client_pin.process_command( - &mut rng, + env.rng(), &mut persistent_store, bad_params, DUMMY_CLOCK_VALUE @@ -959,7 +965,7 @@ mod test { persistent_store.decr_pin_retries().unwrap(); } assert_eq!( - client_pin.process_command(&mut rng, &mut persistent_store, params, DUMMY_CLOCK_VALUE), + client_pin.process_command(env.rng(), &mut persistent_store, params, DUMMY_CLOCK_VALUE), Err(Ctap2StatusCode::CTAP2_ERR_PIN_BLOCKED) ); } @@ -986,13 +992,13 @@ mod test { params.pin_uv_auth_protocol, ) .unwrap(); - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); set_standard_pin(&mut persistent_store); let response = client_pin .process_command( - &mut rng, + env.rng(), &mut persistent_store, params.clone(), DUMMY_CLOCK_VALUE, @@ -1033,7 +1039,7 @@ mod test { bad_params.pin_hash_enc = Some(vec![0xEE; 16]); assert_eq!( client_pin.process_command( - &mut rng, + env.rng(), &mut persistent_store, bad_params, DUMMY_CLOCK_VALUE @@ -1057,13 +1063,13 @@ mod test { pin_uv_auth_protocol, ClientPinSubCommand::GetPinToken, ); - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); set_standard_pin(&mut persistent_store); assert_eq!(persistent_store.force_pin_change(), Ok(())); assert_eq!( - client_pin.process_command(&mut rng, &mut persistent_store, params, DUMMY_CLOCK_VALUE), + client_pin.process_command(env.rng(), &mut persistent_store, params, DUMMY_CLOCK_VALUE), Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID), ); } @@ -1092,13 +1098,13 @@ mod test { params.pin_uv_auth_protocol, ) .unwrap(); - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); set_standard_pin(&mut persistent_store); let response = client_pin .process_command( - &mut rng, + env.rng(), &mut persistent_store, params.clone(), DUMMY_CLOCK_VALUE, @@ -1139,7 +1145,7 @@ mod test { bad_params.permissions = Some(0x00); assert_eq!( client_pin.process_command( - &mut rng, + env.rng(), &mut persistent_store, bad_params, DUMMY_CLOCK_VALUE @@ -1151,7 +1157,7 @@ mod test { bad_params.permissions_rp_id = None; assert_eq!( client_pin.process_command( - &mut rng, + env.rng(), &mut persistent_store, bad_params, DUMMY_CLOCK_VALUE @@ -1163,7 +1169,7 @@ mod test { bad_params.pin_hash_enc = Some(vec![0xEE; 16]); assert_eq!( client_pin.process_command( - &mut rng, + env.rng(), &mut persistent_store, bad_params, DUMMY_CLOCK_VALUE @@ -1189,13 +1195,13 @@ mod test { pin_uv_auth_protocol, ClientPinSubCommand::GetPinUvAuthTokenUsingPinWithPermissions, ); - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); set_standard_pin(&mut persistent_store); assert_eq!(persistent_store.force_pin_change(), Ok(())); assert_eq!( - client_pin.process_command(&mut rng, &mut persistent_store, params, DUMMY_CLOCK_VALUE), + client_pin.process_command(env.rng(), &mut persistent_store, params, DUMMY_CLOCK_VALUE), Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID) ); } @@ -1215,8 +1221,8 @@ mod test { } fn test_helper_decrypt_pin(pin_uv_auth_protocol: PinUvAuthProtocol) { - let mut rng = ThreadRng256 {}; - let pin_protocol = PinProtocol::new(&mut rng); + let mut env = TestEnv::new(); + let pin_protocol = PinProtocol::new(env.rng()); let shared_secret = pin_protocol .decapsulate(pin_protocol.get_public_key(), pin_uv_auth_protocol) .unwrap(); @@ -1259,9 +1265,9 @@ mod test { } fn test_helper_check_and_store_new_pin(pin_uv_auth_protocol: PinUvAuthProtocol) { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); - let pin_protocol = PinProtocol::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); + let pin_protocol = PinProtocol::new(env.rng()); let shared_secret = pin_protocol .decapsulate(pin_protocol.get_public_key(), pin_uv_auth_protocol) .unwrap(); @@ -1317,10 +1323,10 @@ mod test { cred_random: &[u8; 32], salt: Vec, ) -> Result, Ctap2StatusCode> { - let mut rng = ThreadRng256 {}; + let mut env = TestEnv::new(); let (client_pin, shared_secret) = create_client_pin_and_shared_secret(pin_uv_auth_protocol); - 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: client_pin @@ -1330,12 +1336,12 @@ mod test { salt_auth, pin_uv_auth_protocol, }; - let output = client_pin.process_hmac_secret(&mut rng, hmac_secret_input, cred_random); + let output = client_pin.process_hmac_secret(env.rng(), hmac_secret_input, cred_random); output.map(|v| shared_secret.as_ref().decrypt(&v).unwrap()) } fn test_helper_process_hmac_secret_bad_salt_auth(pin_uv_auth_protocol: PinUvAuthProtocol) { - let mut rng = ThreadRng256 {}; + let mut env = TestEnv::new(); let (client_pin, shared_secret) = create_client_pin_and_shared_secret(pin_uv_auth_protocol); let cred_random = [0xC9; 32]; @@ -1350,7 +1356,7 @@ mod test { salt_auth, pin_uv_auth_protocol, }; - let output = client_pin.process_hmac_secret(&mut rng, hmac_secret_input, &cred_random); + let output = client_pin.process_hmac_secret(env.rng(), hmac_secret_input, &cred_random); assert_eq!(output, Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)); } @@ -1451,8 +1457,8 @@ mod test { #[test] fn test_has_permission() { - let mut rng = ThreadRng256 {}; - let mut client_pin = ClientPin::new(&mut rng); + let mut env = TestEnv::new(); + let mut client_pin = ClientPin::new(env.rng()); client_pin.pin_uv_auth_token_state.set_permissions(0x7F); for permission in PinPermission::into_enum_iter() { assert_eq!( @@ -1475,8 +1481,8 @@ mod test { #[test] fn test_has_no_rp_id_permission() { - let mut rng = ThreadRng256 {}; - let mut client_pin = ClientPin::new(&mut rng); + let mut env = TestEnv::new(); + let mut client_pin = ClientPin::new(env.rng()); assert_eq!(client_pin.has_no_rp_id_permission(), Ok(())); client_pin .pin_uv_auth_token_state @@ -1489,8 +1495,8 @@ mod test { #[test] fn test_has_no_or_rp_id_permission() { - let mut rng = ThreadRng256 {}; - let mut client_pin = ClientPin::new(&mut rng); + let mut env = TestEnv::new(); + let mut client_pin = ClientPin::new(env.rng()); assert_eq!(client_pin.has_no_or_rp_id_permission("example.com"), Ok(())); client_pin .pin_uv_auth_token_state @@ -1504,8 +1510,8 @@ mod test { #[test] fn test_has_no_or_rp_id_hash_permission() { - let mut rng = ThreadRng256 {}; - let mut client_pin = ClientPin::new(&mut rng); + let mut env = TestEnv::new(); + let mut client_pin = ClientPin::new(env.rng()); let rp_id_hash = Sha256::hash(b"example.com"); assert_eq!( client_pin.has_no_or_rp_id_hash_permission(&rp_id_hash), @@ -1526,8 +1532,8 @@ mod test { #[test] fn test_ensure_rp_id_permission() { - let mut rng = ThreadRng256 {}; - let mut client_pin = ClientPin::new(&mut rng); + let mut env = TestEnv::new(); + let mut client_pin = ClientPin::new(env.rng()); assert_eq!(client_pin.ensure_rp_id_permission("example.com"), Ok(())); assert_eq!( client_pin @@ -1544,8 +1550,8 @@ mod test { #[test] fn test_verify_pin_uv_auth_token() { - let mut rng = ThreadRng256 {}; - let mut client_pin = ClientPin::new(&mut rng); + let mut env = TestEnv::new(); + let mut client_pin = ClientPin::new(env.rng()); let message = [0xAA]; client_pin .pin_uv_auth_token_state @@ -1618,8 +1624,8 @@ mod test { #[test] fn test_verify_pin_uv_auth_token_not_in_use() { - let mut rng = ThreadRng256 {}; - let client_pin = ClientPin::new(&mut rng); + let mut env = TestEnv::new(); + let client_pin = ClientPin::new(env.rng()); let message = [0xAA]; let pin_uv_auth_token_v1 = client_pin @@ -1640,8 +1646,8 @@ mod test { #[test] fn test_reset() { - let mut rng = ThreadRng256 {}; - let mut client_pin = ClientPin::new(&mut rng); + let mut env = TestEnv::new(); + let mut client_pin = ClientPin::new(env.rng()); let public_key_v1 = client_pin.pin_protocol_v1.get_public_key(); let public_key_v2 = client_pin.pin_protocol_v2.get_public_key(); let token_v1 = *client_pin.pin_protocol_v1.get_pin_uv_auth_token(); @@ -1650,7 +1656,7 @@ mod test { client_pin .pin_uv_auth_token_state .set_permissions_rp_id(Some(String::from("example.com"))); - client_pin.reset(&mut rng); + client_pin.reset(env.rng()); assert_ne!(public_key_v1, client_pin.pin_protocol_v1.get_public_key()); assert_ne!(public_key_v2, client_pin.pin_protocol_v2.get_public_key()); assert_ne!( @@ -1676,13 +1682,13 @@ mod test { PinUvAuthProtocol::V2, ClientPinSubCommand::GetPinUvAuthTokenUsingPinWithPermissions, ); - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); set_standard_pin(&mut persistent_store); params.permissions = Some(0xFF); assert!(client_pin - .process_command(&mut rng, &mut persistent_store, params, DUMMY_CLOCK_VALUE) + .process_command(env.rng(), &mut persistent_store, params, DUMMY_CLOCK_VALUE) .is_ok()); for permission in PinPermission::into_enum_iter() { assert_eq!( @@ -1723,13 +1729,13 @@ mod test { PinUvAuthProtocol::V2, ClientPinSubCommand::GetPinUvAuthTokenUsingPinWithPermissions, ); - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); set_standard_pin(&mut persistent_store); params.permissions = Some(0xFF); assert!(client_pin - .process_command(&mut rng, &mut persistent_store, params, DUMMY_CLOCK_VALUE) + .process_command(env.rng(), &mut persistent_store, params, DUMMY_CLOCK_VALUE) .is_ok()); for permission in PinPermission::into_enum_iter() { assert_eq!( diff --git a/src/ctap/config_command.rs b/src/ctap/config_command.rs index 6599220..e02fbbb 100644 --- a/src/ctap/config_command.rs +++ b/src/ctap/config_command.rs @@ -19,11 +19,12 @@ use super::data_formats::{ConfigSubCommand, ConfigSubCommandParams, SetMinPinLen use super::response::ResponseData; use super::status_code::Ctap2StatusCode; use super::storage::PersistentStore; +use crate::env::Env; use alloc::vec; /// Processes the subcommand enableEnterpriseAttestation for AuthenticatorConfig. -fn process_enable_enterprise_attestation( - persistent_store: &mut PersistentStore, +fn process_enable_enterprise_attestation( + persistent_store: &mut PersistentStore, ) -> Result { if ENTERPRISE_ATTESTATION_MODE.is_some() { persistent_store.enable_enterprise_attestation()?; @@ -34,16 +35,16 @@ fn process_enable_enterprise_attestation( } /// Processes the subcommand toggleAlwaysUv for AuthenticatorConfig. -fn process_toggle_always_uv( - persistent_store: &mut PersistentStore, +fn process_toggle_always_uv( + persistent_store: &mut PersistentStore, ) -> Result { persistent_store.toggle_always_uv()?; Ok(ResponseData::AuthenticatorConfig) } /// Processes the subcommand setMinPINLength for AuthenticatorConfig. -fn process_set_min_pin_length( - persistent_store: &mut PersistentStore, +fn process_set_min_pin_length( + persistent_store: &mut PersistentStore, params: SetMinPinLengthParams, ) -> Result { let SetMinPinLengthParams { @@ -74,8 +75,8 @@ fn process_set_min_pin_length( } /// Processes the AuthenticatorConfig command. -pub fn process_config( - persistent_store: &mut PersistentStore, +pub fn process_config( + persistent_store: &mut PersistentStore, client_pin: &mut ClientPin, params: AuthenticatorConfigParameters, ) -> Result { @@ -129,13 +130,13 @@ mod test { use crate::ctap::customization::ENFORCE_ALWAYS_UV; use crate::ctap::data_formats::PinUvAuthProtocol; use crate::ctap::pin_protocol::authenticate_pin_uv_auth_token; - use crypto::rng256::ThreadRng256; + use crate::env::test::TestEnv; #[test] fn test_process_enable_enterprise_attestation() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); - let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); + let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); @@ -161,9 +162,9 @@ mod test { #[test] fn test_process_toggle_always_uv() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); - let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); + let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); @@ -197,9 +198,9 @@ mod test { } fn test_helper_process_toggle_always_uv_with_pin(pin_uv_auth_protocol: PinUvAuthProtocol) { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); - let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); + let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol); @@ -268,9 +269,9 @@ mod test { #[test] fn test_process_set_min_pin_length() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); - let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); + let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); @@ -313,9 +314,9 @@ mod test { #[test] fn test_process_set_min_pin_length_rp_ids() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); - let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); + let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); @@ -390,9 +391,9 @@ mod test { #[test] fn test_process_set_min_pin_length_force_pin_change_implicit() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); - let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); + let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); @@ -414,9 +415,9 @@ mod test { #[test] fn test_process_set_min_pin_length_force_pin_change_explicit() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); - let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); + let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); @@ -446,9 +447,9 @@ mod test { #[test] fn test_process_config_vendor_prototype() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); - let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); + let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); diff --git a/src/ctap/credential_management.rs b/src/ctap/credential_management.rs index 52ad6e5..f5e78a7 100644 --- a/src/ctap/credential_management.rs +++ b/src/ctap/credential_management.rs @@ -23,6 +23,7 @@ use super::response::{AuthenticatorCredentialManagementResponse, ResponseData}; use super::status_code::Ctap2StatusCode; use super::storage::PersistentStore; use super::{StatefulCommand, StatefulPermission}; +use crate::env::Env; use alloc::collections::BTreeSet; use alloc::string::String; use alloc::vec; @@ -32,8 +33,8 @@ use crypto::Hash256; use libtock_drivers::timer::ClockValue; /// Generates a set with all existing RP IDs. -fn get_stored_rp_ids( - persistent_store: &PersistentStore, +fn get_stored_rp_ids( + persistent_store: &PersistentStore, ) -> Result, Ctap2StatusCode> { let mut rp_set = BTreeSet::new(); let mut iter_result = Ok(()); @@ -108,8 +109,8 @@ fn enumerate_credentials_response( /// Check if the token permissions have the correct associated RP ID. /// /// Either no RP ID is associated, or the RP ID matches the stored credential. -fn check_rp_id_permissions( - persistent_store: &mut PersistentStore, +fn check_rp_id_permissions( + persistent_store: &mut PersistentStore, client_pin: &mut ClientPin, credential_id: &[u8], ) -> Result<(), Ctap2StatusCode> { @@ -122,8 +123,8 @@ fn check_rp_id_permissions( } /// Processes the subcommand getCredsMetadata for CredentialManagement. -fn process_get_creds_metadata( - persistent_store: &PersistentStore, +fn process_get_creds_metadata( + persistent_store: &PersistentStore, ) -> Result { Ok(AuthenticatorCredentialManagementResponse { existing_resident_credentials_count: Some(persistent_store.count_credentials()? as u64), @@ -135,8 +136,8 @@ fn process_get_creds_metadata( } /// Processes the subcommand enumerateRPsBegin for CredentialManagement. -fn process_enumerate_rps_begin( - persistent_store: &PersistentStore, +fn process_enumerate_rps_begin( + persistent_store: &PersistentStore, stateful_command_permission: &mut StatefulPermission, now: ClockValue, ) -> Result { @@ -155,8 +156,8 @@ fn process_enumerate_rps_begin( } /// Processes the subcommand enumerateRPsGetNextRP for CredentialManagement. -fn process_enumerate_rps_get_next_rp( - persistent_store: &PersistentStore, +fn process_enumerate_rps_get_next_rp( + persistent_store: &PersistentStore, stateful_command_permission: &mut StatefulPermission, ) -> Result { let rp_id_index = stateful_command_permission.next_enumerate_rp()?; @@ -170,8 +171,8 @@ fn process_enumerate_rps_get_next_rp( } /// Processes the subcommand enumerateCredentialsBegin for CredentialManagement. -fn process_enumerate_credentials_begin( - persistent_store: &PersistentStore, +fn process_enumerate_credentials_begin( + persistent_store: &PersistentStore, stateful_command_permission: &mut StatefulPermission, client_pin: &mut ClientPin, sub_command_params: CredentialManagementSubCommandParameters, @@ -207,8 +208,8 @@ fn process_enumerate_credentials_begin( } /// Processes the subcommand enumerateCredentialsGetNextCredential for CredentialManagement. -fn process_enumerate_credentials_get_next_credential( - persistent_store: &PersistentStore, +fn process_enumerate_credentials_get_next_credential( + persistent_store: &PersistentStore, stateful_command_permission: &mut StatefulPermission, ) -> Result { let credential_key = stateful_command_permission.next_enumerate_credential()?; @@ -217,8 +218,8 @@ fn process_enumerate_credentials_get_next_credential( } /// Processes the subcommand deleteCredential for CredentialManagement. -fn process_delete_credential( - persistent_store: &mut PersistentStore, +fn process_delete_credential( + persistent_store: &mut PersistentStore, client_pin: &mut ClientPin, sub_command_params: CredentialManagementSubCommandParameters, ) -> Result<(), Ctap2StatusCode> { @@ -231,8 +232,8 @@ fn process_delete_credential( } /// Processes the subcommand updateUserInformation for CredentialManagement. -fn process_update_user_information( - persistent_store: &mut PersistentStore, +fn process_update_user_information( + persistent_store: &mut PersistentStore, client_pin: &mut ClientPin, sub_command_params: CredentialManagementSubCommandParameters, ) -> Result<(), Ctap2StatusCode> { @@ -248,8 +249,8 @@ fn process_update_user_information( } /// Processes the CredentialManagement command and all its subcommands. -pub fn process_credential_management( - persistent_store: &mut PersistentStore, +pub fn process_credential_management( + persistent_store: &mut PersistentStore, stateful_command_permission: &mut StatefulPermission, client_pin: &mut ClientPin, cred_management_params: AuthenticatorCredentialManagementParameters, diff --git a/src/ctap/ctap1.rs b/src/ctap/ctap1.rs index 857c12b..ff39bf8 100644 --- a/src/ctap/ctap1.rs +++ b/src/ctap/ctap1.rs @@ -178,10 +178,10 @@ impl Ctap1Command { const VENDOR_SPECIFIC_FIRST: u8 = 0x40; const VENDOR_SPECIFIC_LAST: u8 = 0xBF; - pub fn process_command( - env: &mut impl Env, + pub fn process_command( + env: &mut E, message: &[u8], - ctap_state: &mut CtapState, + ctap_state: &mut CtapState, clock_value: ClockValue, ) -> Result, Ctap1StatusCode> { if !ctap_state @@ -243,11 +243,11 @@ impl Ctap1Command { // +------+-------------------+-----------------+------------+--------------------+ // + 0x00 | application (32B) | challenge (32B) | key handle | User pub key (65B) | // +------+-------------------+-----------------+------------+--------------------+ - fn process_register( - env: &mut impl Env, + fn process_register( + env: &mut E, challenge: [u8; 32], application: [u8; 32], - ctap_state: &mut CtapState, + ctap_state: &mut CtapState, ) -> Result, Ctap1StatusCode> { let sk = crypto::ecdsa::SecKey::gensk(env.rng()); let pk = sk.genpk(); @@ -307,13 +307,13 @@ impl Ctap1Command { // +-------------------+---------+--------------+-----------------+ // + application (32B) | UP (1B) | Counter (4B) | challenge (32B) | // +-------------------+---------+--------------+-----------------+ - fn process_authenticate( - env: &mut impl Env, + fn process_authenticate( + env: &mut E, challenge: [u8; 32], application: [u8; 32], key_handle: Vec, flags: Ctap1Flags, - ctap_state: &mut CtapState, + ctap_state: &mut CtapState, ) -> Result, Ctap1StatusCode> { let credential_source = ctap_state .decrypt_credential_source(key_handle, &application) diff --git a/src/ctap/hid/mod.rs b/src/ctap/hid/mod.rs index e6ff8f5..bf6f799 100644 --- a/src/ctap/hid/mod.rs +++ b/src/ctap/hid/mod.rs @@ -146,12 +146,12 @@ 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, + env: &mut E, packet: &HidPacket, clock_value: ClockValue, - ctap_state: &mut CtapState, + ctap_state: &mut CtapState, ) -> HidPacketIterator { // TODO: Send COMMAND_KEEPALIVE every 100ms? match self @@ -442,10 +442,10 @@ mod test { const DUMMY_CLOCK_VALUE: ClockValue = ClockValue::new(0, CLOCK_FREQUENCY_HZ); const DUMMY_TIMESTAMP: Timestamp = Timestamp::from_ms(0); - fn process_messages( - env: &mut impl Env, + fn process_messages( + env: &mut E, ctap_hid: &mut CtapHid, - ctap_state: &mut CtapState, + ctap_state: &mut CtapState, request: Vec, ) -> Option> { let mut result = Vec::new(); @@ -466,10 +466,10 @@ mod test { Some(result) } - fn cid_from_init( - env: &mut impl Env, + fn cid_from_init( + env: &mut E, ctap_hid: &mut CtapHid, - ctap_state: &mut CtapState, + ctap_state: &mut CtapState, ) -> ChannelID { let nonce = vec![0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]; let reply = process_messages( diff --git a/src/ctap/large_blobs.rs b/src/ctap/large_blobs.rs index 9a01b8f..a6cd23a 100644 --- a/src/ctap/large_blobs.rs +++ b/src/ctap/large_blobs.rs @@ -18,6 +18,7 @@ use super::customization::MAX_MSG_SIZE; use super::response::{AuthenticatorLargeBlobsResponse, ResponseData}; use super::status_code::Ctap2StatusCode; use super::storage::PersistentStore; +use crate::env::Env; use alloc::vec; use alloc::vec::Vec; use byteorder::{ByteOrder, LittleEndian}; @@ -44,9 +45,9 @@ impl LargeBlobs { } /// Process the large blob command. - pub fn process_command( + pub fn process_command( &mut self, - persistent_store: &mut PersistentStore, + persistent_store: &mut PersistentStore, client_pin: &mut ClientPin, large_blobs_params: AuthenticatorLargeBlobsParameters, ) -> Result { @@ -137,13 +138,13 @@ mod test { use super::super::data_formats::PinUvAuthProtocol; use super::super::pin_protocol::authenticate_pin_uv_auth_token; use super::*; - use crypto::rng256::ThreadRng256; + use crate::env::test::TestEnv; #[test] fn test_process_command_get_empty() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); - let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); + let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); @@ -173,9 +174,9 @@ mod test { #[test] fn test_process_command_commit_and_get() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); - let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); + let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); @@ -236,9 +237,9 @@ mod test { #[test] fn test_process_command_commit_unexpected_offset() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); - let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); + let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); @@ -283,9 +284,9 @@ mod test { #[test] fn test_process_command_commit_unexpected_length() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); - let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); + let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); @@ -330,9 +331,9 @@ mod test { #[test] fn test_process_command_commit_end_offset_overflow() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); - let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); + let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); @@ -354,9 +355,9 @@ mod test { #[test] fn test_process_command_commit_unexpected_hash() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); - let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); + let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); @@ -383,9 +384,9 @@ mod test { } fn test_helper_process_command_commit_with_pin(pin_uv_auth_protocol: PinUvAuthProtocol) { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); - let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); + let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng()); let pin_uv_auth_token = [0x55; 32]; let mut client_pin = ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol); diff --git a/src/ctap/mod.rs b/src/ctap/mod.rs index dba8ed9..b21efab 100644 --- a/src/ctap/mod.rs +++ b/src/ctap/mod.rs @@ -64,7 +64,7 @@ use self::storage::PersistentStore; use self::timed_permission::TimedPermission; #[cfg(feature = "with_ctap1")] use self::timed_permission::U2fUserPresenceState; -use crate::embedded_flash::{UpgradeLocations, UpgradeStorage}; +use crate::api::upgrade_storage::UpgradeStorage; use crate::env::{Env, UserPresence}; use alloc::boxed::Box; use alloc::string::{String, ToString}; @@ -161,7 +161,7 @@ fn truncate_to_char_boundary(s: &str, mut max: usize) -> &str { /// The upgrade hash is computed over the firmware image and all metadata, /// except the hash itself. fn parse_metadata( - upgrade_locations: &UpgradeLocations, + upgrade_locations: &impl UpgradeStorage, metadata: &[u8], ) -> Result<[u8; 32], Ctap2StatusCode> { const METADATA_LEN: usize = 40; @@ -330,20 +330,21 @@ 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 { - persistent_store: PersistentStore, +pub struct CtapState { + persistent_store: PersistentStore, client_pin: ClientPin, #[cfg(feature = "with_ctap1")] pub(crate) u2f_up_state: U2fUserPresenceState, // The state initializes to Reset and its timeout, and never goes back to Reset. stateful_command_permission: StatefulPermission, large_blobs: LargeBlobs, - upgrade_locations: Option, + // Upgrade support is optional. + upgrade_locations: Option, } -impl CtapState { - pub fn new(env: &mut impl Env, now: ClockValue) -> CtapState { - let persistent_store = PersistentStore::new(env.rng()); +impl CtapState { + pub fn new(env: &mut E, now: ClockValue) -> Self { + let persistent_store = PersistentStore::new(env); let client_pin = ClientPin::new(env.rng()); CtapState { persistent_store, @@ -355,7 +356,7 @@ impl CtapState { ), stateful_command_permission: StatefulPermission::new_reset(now), large_blobs: LargeBlobs::new(), - upgrade_locations: UpgradeLocations::new().ok(), + upgrade_locations: env.upgrade_storage().ok(), } } @@ -369,7 +370,7 @@ impl CtapState { pub fn increment_global_signature_counter( &mut self, - env: &mut impl Env, + env: &mut E, ) -> Result<(), Ctap2StatusCode> { if USE_SIGNATURE_COUNTER { let increment = env.rng().gen_uniform_u32x8()[0] % 8 + 1; @@ -393,7 +394,7 @@ impl CtapState { // compatible with U2F. pub fn encrypt_key_handle( &mut self, - env: &mut impl Env, + env: &mut E, private_key: crypto::ecdsa::SecKey, application: &[u8; 32], ) -> Result, Ctap2StatusCode> { @@ -454,7 +455,7 @@ impl CtapState { pub fn process_command( &mut self, - env: &mut impl Env, + env: &mut E, command_cbor: &[u8], cid: ChannelID, now: ClockValue, @@ -557,7 +558,7 @@ impl CtapState { fn pin_uv_auth_precheck( &mut self, - env: &mut impl Env, + env: &mut E, pin_uv_auth_param: &Option>, pin_uv_auth_protocol: Option, cid: ChannelID, @@ -579,7 +580,7 @@ impl CtapState { fn process_make_credential( &mut self, - env: &mut impl Env, + env: &mut E, make_credential_params: AuthenticatorMakeCredentialParameters, cid: ChannelID, ) -> Result { @@ -837,7 +838,7 @@ impl CtapState { // and returns the correct Get(Next)Assertion response. fn assertion_response( &mut self, - env: &mut impl Env, + env: &mut E, mut credential: PublicKeyCredentialSource, assertion_input: AssertionInput, number_of_credentials: Option, @@ -945,7 +946,7 @@ impl CtapState { fn process_get_assertion( &mut self, - env: &mut impl Env, + env: &mut E, get_assertion_params: AuthenticatorGetAssertionParameters, cid: ChannelID, now: ClockValue, @@ -1071,7 +1072,7 @@ impl CtapState { fn process_get_next_assertion( &mut self, - env: &mut impl Env, + env: &mut E, now: ClockValue, ) -> Result { self.stateful_command_permission @@ -1157,7 +1158,7 @@ impl CtapState { fn process_reset( &mut self, - env: &mut impl Env, + env: &mut E, cid: ChannelID, now: ClockValue, ) -> Result { @@ -1183,7 +1184,7 @@ impl CtapState { fn process_selection( &self, - env: &mut impl Env, + env: &mut E, cid: ChannelID, ) -> Result { env.user_presence().check(cid)?; @@ -1192,7 +1193,7 @@ impl CtapState { fn process_vendor_configure( &mut self, - env: &mut impl Env, + env: &mut E, params: AuthenticatorVendorConfigureParameters, cid: ChannelID, ) -> Result { diff --git a/src/ctap/storage.rs b/src/ctap/storage.rs index b84856f..08e88ff 100644 --- a/src/ctap/storage.rs +++ b/src/ctap/storage.rs @@ -26,7 +26,7 @@ use crate::ctap::data_formats::{ use crate::ctap::key_material; use crate::ctap::status_code::Ctap2StatusCode; use crate::ctap::INITIAL_SIGNATURE_COUNTER; -use crate::embedded_flash::{new_storage, Storage}; +use crate::env::Env; use alloc::string::String; use alloc::vec; use alloc::vec::Vec; @@ -56,22 +56,22 @@ struct PinProperties { } /// CTAP persistent storage. -pub struct PersistentStore { - store: persistent_store::Store, +pub struct PersistentStore { + store: persistent_store::Store, } -impl PersistentStore { +impl PersistentStore { /// Gives access to the persistent store. /// /// # Safety /// /// This should be at most one instance of persistent store per program lifetime. - pub fn new(rng: &mut impl Rng256) -> PersistentStore { - let storage = new_storage().ok().unwrap(); + pub fn new(env: &mut E) -> Self { + let storage = env.storage().ok().unwrap(); let mut store = PersistentStore { store: persistent_store::Store::new(storage).ok().unwrap(), }; - store.init(rng).ok().unwrap(); + store.init(env.rng()).ok().unwrap(); store } @@ -265,7 +265,7 @@ impl PersistentStore { pub fn iter_credentials<'a>( &'a self, result: &'a mut Result<(), Ctap2StatusCode>, - ) -> Result, Ctap2StatusCode> { + ) -> Result, Ctap2StatusCode> { IterCredentials::new(&self.store, result) } @@ -655,9 +655,9 @@ impl From for Ctap2StatusCode { } /// Iterator for credentials. -pub struct IterCredentials<'a> { +pub struct IterCredentials<'a, E: Env> { /// The store being iterated. - store: &'a persistent_store::Store, + store: &'a persistent_store::Store, /// The store iterator. iter: persistent_store::StoreIter<'a>, @@ -669,12 +669,12 @@ pub struct IterCredentials<'a> { result: &'a mut Result<(), Ctap2StatusCode>, } -impl<'a> IterCredentials<'a> { +impl<'a, E: Env> IterCredentials<'a, E> { /// Creates a credential iterator. fn new( - store: &'a persistent_store::Store, + store: &'a persistent_store::Store, result: &'a mut Result<(), Ctap2StatusCode>, - ) -> Result, Ctap2StatusCode> { + ) -> Result { let iter = store.iter()?; Ok(IterCredentials { store, @@ -696,7 +696,7 @@ impl<'a> IterCredentials<'a> { } } -impl<'a> Iterator for IterCredentials<'a> { +impl<'a, E: Env> Iterator for IterCredentials<'a, E> { type Item = (usize, PublicKeyCredentialSource); fn next(&mut self) -> Option<(usize, PublicKeyCredentialSource)> { @@ -752,6 +752,7 @@ fn serialize_min_pin_length_rp_ids(rp_ids: Vec) -> Result, Ctap2 mod test { use super::*; use crate::ctap::data_formats::{PublicKeyCredentialSource, PublicKeyCredentialType}; + use crate::env::test::TestEnv; use crypto::rng256::{Rng256, ThreadRng256}; fn create_credential_source( @@ -778,24 +779,24 @@ mod test { #[test] fn test_store() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); assert_eq!(persistent_store.count_credentials().unwrap(), 0); - let credential_source = create_credential_source(&mut rng, "example.com", vec![]); + let credential_source = create_credential_source(env.rng(), "example.com", vec![]); assert!(persistent_store.store_credential(credential_source).is_ok()); assert!(persistent_store.count_credentials().unwrap() > 0); } #[test] fn test_delete_credential() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); assert_eq!(persistent_store.count_credentials().unwrap(), 0); let mut credential_ids = vec![]; for i in 0..MAX_SUPPORTED_RESIDENT_KEYS { let user_handle = (i as u32).to_ne_bytes().to_vec(); - let credential_source = create_credential_source(&mut rng, "example.com", user_handle); + let credential_source = create_credential_source(env.rng(), "example.com", user_handle); credential_ids.push(credential_source.credential_id.clone()); assert!(persistent_store.store_credential(credential_source).is_ok()); assert_eq!(persistent_store.count_credentials().unwrap(), i + 1); @@ -810,8 +811,8 @@ mod test { #[test] fn test_update_credential() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); let user = PublicKeyCredentialUserEntity { // User ID is ignored. user_id: vec![0x00], @@ -824,7 +825,7 @@ mod test { Err(Ctap2StatusCode::CTAP2_ERR_NO_CREDENTIALS) ); - let credential_source = create_credential_source(&mut rng, "example.com", vec![0x1D]); + let credential_source = create_credential_source(env.rng(), "example.com", vec![0x1D]); let credential_id = credential_source.credential_id.clone(); assert!(persistent_store.store_credential(credential_source).is_ok()); let stored_credential = persistent_store @@ -848,12 +849,12 @@ mod test { #[test] fn test_credential_order() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); - let credential_source = create_credential_source(&mut rng, "example.com", vec![]); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); + let credential_source = create_credential_source(env.rng(), "example.com", vec![]); let current_latest_creation = credential_source.creation_order; assert!(persistent_store.store_credential(credential_source).is_ok()); - let mut credential_source = create_credential_source(&mut rng, "example.com", vec![]); + let mut credential_source = create_credential_source(env.rng(), "example.com", vec![]); credential_source.creation_order = persistent_store.new_creation_order().unwrap(); assert!(credential_source.creation_order > current_latest_creation); let current_latest_creation = credential_source.creation_order; @@ -863,18 +864,18 @@ mod test { #[test] fn test_fill_store() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); assert_eq!(persistent_store.count_credentials().unwrap(), 0); for i in 0..MAX_SUPPORTED_RESIDENT_KEYS { let user_handle = (i as u32).to_ne_bytes().to_vec(); - let credential_source = create_credential_source(&mut rng, "example.com", user_handle); + let credential_source = create_credential_source(env.rng(), "example.com", user_handle); assert!(persistent_store.store_credential(credential_source).is_ok()); assert_eq!(persistent_store.count_credentials().unwrap(), i + 1); } let credential_source = create_credential_source( - &mut rng, + env.rng(), "example.com", vec![MAX_SUPPORTED_RESIDENT_KEYS as u8], ); @@ -890,12 +891,12 @@ mod test { #[test] fn test_overwrite() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); assert_eq!(persistent_store.count_credentials().unwrap(), 0); // These should have different IDs. - let credential_source0 = create_credential_source(&mut rng, "example.com", vec![0x00]); - let credential_source1 = create_credential_source(&mut rng, "example.com", vec![0x00]); + let credential_source0 = create_credential_source(env.rng(), "example.com", vec![0x00]); + let credential_source1 = create_credential_source(env.rng(), "example.com", vec![0x00]); let credential_id0 = credential_source0.credential_id.clone(); let credential_id1 = credential_source1.credential_id.clone(); @@ -915,15 +916,15 @@ mod test { .unwrap() .is_some()); - let mut persistent_store = PersistentStore::new(&mut rng); + let mut persistent_store = PersistentStore::new(&mut env); for i in 0..MAX_SUPPORTED_RESIDENT_KEYS { let user_handle = (i as u32).to_ne_bytes().to_vec(); - let credential_source = create_credential_source(&mut rng, "example.com", user_handle); + let credential_source = create_credential_source(env.rng(), "example.com", user_handle); assert!(persistent_store.store_credential(credential_source).is_ok()); assert_eq!(persistent_store.count_credentials().unwrap(), i + 1); } let credential_source = create_credential_source( - &mut rng, + env.rng(), "example.com", vec![MAX_SUPPORTED_RESIDENT_KEYS as u8], ); @@ -939,12 +940,12 @@ mod test { #[test] fn test_get_credential() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); - let credential_source0 = create_credential_source(&mut rng, "example.com", vec![0x00]); - let credential_source1 = create_credential_source(&mut rng, "example.com", vec![0x01]); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); + let credential_source0 = create_credential_source(env.rng(), "example.com", vec![0x00]); + let credential_source1 = create_credential_source(env.rng(), "example.com", vec![0x01]); let credential_source2 = - create_credential_source(&mut rng, "another.example.com", vec![0x02]); + create_credential_source(env.rng(), "another.example.com", vec![0x02]); let credential_sources = vec![credential_source0, credential_source1, credential_source2]; for credential_source in credential_sources.into_iter() { let cred_id = credential_source.credential_id.clone(); @@ -957,11 +958,11 @@ mod test { #[test] fn test_find() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); assert_eq!(persistent_store.count_credentials().unwrap(), 0); - let credential_source0 = create_credential_source(&mut rng, "example.com", vec![0x00]); - let credential_source1 = create_credential_source(&mut rng, "example.com", vec![0x01]); + let credential_source0 = create_credential_source(env.rng(), "example.com", vec![0x00]); + let credential_source1 = create_credential_source(env.rng(), "example.com", vec![0x01]); let id0 = credential_source0.credential_id.clone(); let key0 = credential_source0.private_key.clone(); assert!(persistent_store @@ -997,13 +998,13 @@ mod test { #[test] fn test_find_with_cred_protect() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); assert_eq!(persistent_store.count_credentials().unwrap(), 0); - let private_key = crypto::ecdsa::SecKey::gensk(&mut rng); + let private_key = crypto::ecdsa::SecKey::gensk(env.rng()); let credential = PublicKeyCredentialSource { key_type: PublicKeyCredentialType::PublicKey, - credential_id: rng.gen_uniform_u8x32().to_vec(), + credential_id: env.rng().gen_uniform_u8x32().to_vec(), private_key, rp_id: String::from("example.com"), user_handle: vec![0x00], @@ -1025,8 +1026,8 @@ mod test { #[test] fn test_master_keys() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); // Master keys stay the same within the same CTAP reset cycle. let master_keys_1 = persistent_store.master_keys().unwrap(); @@ -1038,7 +1039,7 @@ mod test { // same keys. let master_encryption_key = master_keys_1.encryption.to_vec(); let master_hmac_key = master_keys_1.hmac.to_vec(); - persistent_store.reset(&mut rng).unwrap(); + persistent_store.reset(env.rng()).unwrap(); let master_keys_3 = persistent_store.master_keys().unwrap(); assert!(master_keys_3.encryption != master_encryption_key.as_slice()); assert!(master_keys_3.hmac != master_hmac_key.as_slice()); @@ -1046,8 +1047,8 @@ mod test { #[test] fn test_cred_random_secret() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); // CredRandom secrets stay the same within the same CTAP reset cycle. let cred_random_with_uv_1 = persistent_store.cred_random_secret(true).unwrap(); @@ -1059,7 +1060,7 @@ mod test { // CredRandom secrets change after reset. This test may fail if the random generator produces the // same keys. - persistent_store.reset(&mut rng).unwrap(); + persistent_store.reset(env.rng()).unwrap(); let cred_random_with_uv_3 = persistent_store.cred_random_secret(true).unwrap(); let cred_random_without_uv_3 = persistent_store.cred_random_secret(false).unwrap(); assert!(cred_random_with_uv_1 != cred_random_with_uv_3); @@ -1068,15 +1069,15 @@ mod test { #[test] fn test_pin_hash_and_length() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); // Pin hash is initially not set. assert!(persistent_store.pin_hash().unwrap().is_none()); assert!(persistent_store.pin_code_point_length().unwrap().is_none()); // Setting the pin sets the pin hash. - let random_data = rng.gen_uniform_u8x32(); + let random_data = env.rng().gen_uniform_u8x32(); assert_eq!(random_data.len(), 2 * PIN_AUTH_LENGTH); let pin_hash_1 = *array_ref!(random_data, 0, PIN_AUTH_LENGTH); let pin_hash_2 = *array_ref!(random_data, PIN_AUTH_LENGTH, PIN_AUTH_LENGTH); @@ -1096,15 +1097,15 @@ mod test { ); // Resetting the storage resets the pin hash. - persistent_store.reset(&mut rng).unwrap(); + persistent_store.reset(env.rng()).unwrap(); assert!(persistent_store.pin_hash().unwrap().is_none()); assert!(persistent_store.pin_code_point_length().unwrap().is_none()); } #[test] fn test_pin_retries() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); // The pin retries is initially at the maximum. assert_eq!(persistent_store.pin_retries(), Ok(MAX_PIN_RETRIES)); @@ -1126,8 +1127,8 @@ mod test { #[test] fn test_persistent_keys() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); // Make sure the attestation are absent. There is no batch attestation in tests. assert!(persistent_store @@ -1151,7 +1152,7 @@ mod test { assert_eq!(&persistent_store.aaguid().unwrap(), key_material::AAGUID); // The persistent keys stay initialized and preserve their value after a reset. - persistent_store.reset(&mut rng).unwrap(); + persistent_store.reset(env.rng()).unwrap(); assert_eq!( &persistent_store.attestation_private_key().unwrap().unwrap(), &dummy_key @@ -1165,8 +1166,8 @@ mod test { #[test] fn test_min_pin_length() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); // The minimum PIN length is initially at the default. assert_eq!( @@ -1187,8 +1188,8 @@ mod test { #[test] fn test_min_pin_length_rp_ids() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); // The minimum PIN length RP IDs are initially at the default. assert_eq!( @@ -1213,8 +1214,8 @@ mod test { #[test] fn test_max_large_blob_array_size() { - let mut rng = ThreadRng256 {}; - let persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let persistent_store = PersistentStore::new(&mut env); assert!( MAX_LARGE_BLOB_ARRAY_SIZE @@ -1225,8 +1226,8 @@ mod test { #[test] fn test_commit_get_large_blob_array() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); let large_blob_array = vec![0x01, 0x02, 0x03]; assert!(persistent_store @@ -1248,8 +1249,8 @@ mod test { #[test] fn test_commit_get_large_blob_array_overwrite() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); let large_blob_array = vec![0x11; 5]; assert!(persistent_store @@ -1272,8 +1273,8 @@ mod test { #[test] fn test_commit_get_large_blob_array_no_commit() { - let mut rng = ThreadRng256 {}; - let persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let persistent_store = PersistentStore::new(&mut env); let empty_blob_array = vec![ 0x80, 0x76, 0xBE, 0x8B, 0x52, 0x8D, 0x00, 0x75, 0xF7, 0xAA, 0xE9, 0x8D, 0x6F, 0xA5, @@ -1289,8 +1290,8 @@ mod test { #[test] fn test_global_signature_counter() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); let mut counter_value = 1; assert_eq!( @@ -1311,8 +1312,8 @@ mod test { #[test] fn test_force_pin_change() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); assert!(!persistent_store.has_force_pin_change().unwrap()); assert_eq!(persistent_store.force_pin_change(), Ok(())); @@ -1323,20 +1324,20 @@ mod test { #[test] fn test_enterprise_attestation() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); assert!(!persistent_store.enterprise_attestation().unwrap()); assert_eq!(persistent_store.enable_enterprise_attestation(), Ok(())); assert!(persistent_store.enterprise_attestation().unwrap()); - persistent_store.reset(&mut rng).unwrap(); + persistent_store.reset(env.rng()).unwrap(); assert!(!persistent_store.enterprise_attestation().unwrap()); } #[test] fn test_always_uv() { - let mut rng = ThreadRng256 {}; - let mut persistent_store = PersistentStore::new(&mut rng); + let mut env = TestEnv::new(); + let mut persistent_store = PersistentStore::new(&mut env); if ENFORCE_ALWAYS_UV { assert!(persistent_store.has_always_uv().unwrap()); @@ -1355,11 +1356,11 @@ mod test { #[test] fn test_serialize_deserialize_credential() { - 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 credential = PublicKeyCredentialSource { key_type: PublicKeyCredentialType::PublicKey, - credential_id: rng.gen_uniform_u8x32().to_vec(), + credential_id: env.rng().gen_uniform_u8x32().to_vec(), private_key, rp_id: String::from("example.com"), user_handle: vec![0x00], diff --git a/src/embedded_flash/mod.rs b/src/embedded_flash/mod.rs deleted file mode 100644 index c34049a..0000000 --- a/src/embedded_flash/mod.rs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2019-2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#[cfg(feature = "std")] -mod buffer_upgrade; -mod helper; -#[cfg(not(feature = "std"))] -mod syscall; -mod upgrade_storage; - -pub use upgrade_storage::UpgradeStorage; - -/// Definitions for production. -#[cfg(not(feature = "std"))] -mod prod { - use super::syscall::{SyscallStorage, SyscallUpgradeStorage}; - - pub type Storage = SyscallStorage; - - pub fn new_storage() -> persistent_store::StorageResult { - Storage::new() - } - - pub type UpgradeLocations = SyscallUpgradeStorage; -} -#[cfg(not(feature = "std"))] -pub use self::prod::{new_storage, Storage, UpgradeLocations}; - -/// Definitions for testing. -#[cfg(feature = "std")] -mod test { - use super::buffer_upgrade::BufferUpgradeStorage; - - pub type Storage = persistent_store::BufferStorage; - - pub fn new_storage() -> persistent_store::StorageResult { - // Use the Nordic configuration. - const PAGE_SIZE: usize = 0x1000; - const NUM_PAGES: usize = 20; - let store = vec![0xff; NUM_PAGES * PAGE_SIZE].into_boxed_slice(); - let options = persistent_store::BufferOptions { - word_size: 4, - page_size: PAGE_SIZE, - max_word_writes: 2, - max_page_erases: 10000, - strict_mode: true, - }; - Ok(Storage::new(store, options)) - } - - pub type UpgradeLocations = BufferUpgradeStorage; -} -#[cfg(feature = "std")] -pub use self::test::{new_storage, Storage, UpgradeLocations}; diff --git a/src/env/mod.rs b/src/env/mod.rs index 04f849c..dcb8f93 100644 --- a/src/env/mod.rs +++ b/src/env/mod.rs @@ -1,6 +1,8 @@ +use crate::api::upgrade_storage::UpgradeStorage; use crate::ctap::hid::ChannelID; use crate::ctap::status_code::Ctap2StatusCode; use crypto::rng256::Rng256; +use persistent_store::{Storage, StorageResult}; #[cfg(feature = "std")] pub mod test; @@ -17,7 +19,19 @@ pub trait UserPresence { pub trait Env { type Rng: Rng256; type UserPresence: UserPresence; + type Storage: Storage; + type UpgradeStorage: UpgradeStorage; fn rng(&mut self) -> &mut Self::Rng; fn user_presence(&mut self) -> &mut Self::UserPresence; + + /// Returns the unique storage instance. + /// + /// This function is called at most once. Implementation may panic if called more than once. + fn storage(&mut self) -> StorageResult; + + /// Returns the unique upgrade storage instance. + /// + /// This function is called at most once. Implementation may panic if called more than once. + fn upgrade_storage(&mut self) -> StorageResult; } diff --git a/src/env/test.rs b/src/env/test/mod.rs similarity index 57% rename from src/env/test.rs rename to src/env/test/mod.rs index 7d81f4a..02a89c7 100644 --- a/src/env/test.rs +++ b/src/env/test/mod.rs @@ -1,7 +1,11 @@ +use self::upgrade_storage::BufferUpgradeStorage; use crate::ctap::hid::ChannelID; use crate::ctap::status_code::Ctap2StatusCode; use crate::env::{Env, UserPresence}; use crypto::rng256::ThreadRng256; +use persistent_store::{BufferOptions, BufferStorage, StorageResult}; + +mod upgrade_storage; pub struct TestEnv { rng: ThreadRng256, @@ -37,6 +41,8 @@ impl UserPresence for TestUserPresence { impl Env for TestEnv { type Rng = ThreadRng256; type UserPresence = TestUserPresence; + type Storage = BufferStorage; + type UpgradeStorage = BufferUpgradeStorage; fn rng(&mut self) -> &mut Self::Rng { &mut self.rng @@ -45,4 +51,23 @@ impl Env for TestEnv { fn user_presence(&mut self) -> &mut Self::UserPresence { &mut self.user_presence } + + fn storage(&mut self) -> StorageResult { + // Use the Nordic configuration. + const PAGE_SIZE: usize = 0x1000; + const NUM_PAGES: usize = 20; + let store = vec![0xff; NUM_PAGES * PAGE_SIZE].into_boxed_slice(); + let options = BufferOptions { + word_size: 4, + page_size: PAGE_SIZE, + max_word_writes: 2, + max_page_erases: 10000, + strict_mode: true, + }; + Ok(BufferStorage::new(store, options)) + } + + fn upgrade_storage(&mut self) -> StorageResult { + BufferUpgradeStorage::new() + } } diff --git a/src/embedded_flash/buffer_upgrade.rs b/src/env/test/upgrade_storage.rs similarity index 98% rename from src/embedded_flash/buffer_upgrade.rs rename to src/env/test/upgrade_storage.rs index 8f4bc6a..04e3e55 100644 --- a/src/embedded_flash/buffer_upgrade.rs +++ b/src/env/test/upgrade_storage.rs @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::helper::ModRange; -use super::upgrade_storage::UpgradeStorage; +use crate::api::upgrade_storage::helper::ModRange; +use crate::api::upgrade_storage::UpgradeStorage; use alloc::boxed::Box; use persistent_store::{StorageError, StorageResult}; diff --git a/src/env/tock.rs b/src/env/tock/mod.rs similarity index 82% rename from src/env/tock.rs rename to src/env/tock/mod.rs index ea34111..e5973c0 100644 --- a/src/env/tock.rs +++ b/src/env/tock/mod.rs @@ -1,9 +1,11 @@ +use self::storage::{SyscallStorage, SyscallUpgradeStorage}; use crate::ctap::hid::{ChannelID, CtapHid, KeepaliveStatus, ProcessedPacket}; use crate::ctap::status_code::Ctap2StatusCode; use crate::env::{Env, UserPresence}; use core::cell::Cell; #[cfg(feature = "debug_ctap")] use core::fmt::Write; +use core::sync::atomic::{AtomicBool, Ordering}; use crypto::rng256::TockRng256; use libtock_core::result::{CommandError, EALREADY}; use libtock_drivers::buttons::{self, ButtonState}; @@ -12,18 +14,48 @@ use libtock_drivers::console::Console; use libtock_drivers::result::{FlexUnwrap, TockError}; use libtock_drivers::timer::Duration; use libtock_drivers::{led, timer, usb_ctap_hid}; +use persistent_store::StorageResult; + +mod storage; pub struct TockEnv { rng: TockRng256, + storage: bool, + upgrade_storage: bool, } impl TockEnv { - pub fn new() -> Self { - let rng = TockRng256 {}; - TockEnv { rng } + /// Returns the unique instance of the Tock environment. + /// + /// This function returns `Some` the first time it is called. Afterwards, it repeatedly returns + /// `None`. + pub fn new() -> Option { + // Make sure the environment was not already taken. + static TAKEN: AtomicBool = AtomicBool::new(false); + if TAKEN.fetch_or(true, Ordering::SeqCst) { + return None; + } + Some(TockEnv { + rng: TockRng256 {}, + storage: false, + upgrade_storage: false, + }) } } +/// Creates a new storage instance. +/// +/// # Safety +/// +/// It is probably technically memory-safe to hame multiple storage instances at the same time, but +/// for extra precaution we mark the function as unsafe. To ensure correct usage, this function +/// should only be called if the previous storage instance was dropped. +// This function is exposed for example binaries testing the hardware. This could probably be +// cleaned up by having the persistent store return its storage. +pub unsafe fn steal_storage() -> StorageResult { + SyscallStorage::new() +} + impl UserPresence for TockEnv { fn check(&self, cid: ChannelID) -> Result<(), Ctap2StatusCode> { check_user_presence(cid) @@ -33,6 +65,8 @@ impl UserPresence for TockEnv { impl Env for TockEnv { type Rng = TockRng256; type UserPresence = Self; + type Storage = SyscallStorage; + type UpgradeStorage = SyscallUpgradeStorage; fn rng(&mut self) -> &mut Self::Rng { &mut self.rng @@ -41,6 +75,18 @@ impl Env for TockEnv { fn user_presence(&mut self) -> &mut Self::UserPresence { self } + + fn storage(&mut self) -> StorageResult { + assert!(!self.storage); + self.storage = true; + unsafe { steal_storage() } + } + + fn upgrade_storage(&mut self) -> StorageResult { + assert!(!self.upgrade_storage); + self.upgrade_storage = true; + SyscallUpgradeStorage::new() + } } // Returns whether the keepalive was sent, or false if cancelled. diff --git a/src/embedded_flash/syscall.rs b/src/env/tock/storage.rs similarity index 98% rename from src/embedded_flash/syscall.rs rename to src/env/tock/storage.rs index d5dac9c..1d4c6c4 100644 --- a/src/embedded_flash/syscall.rs +++ b/src/env/tock/storage.rs @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::helper::{find_slice, is_aligned, ModRange}; -use super::upgrade_storage::UpgradeStorage; +use crate::api::upgrade_storage::helper::{find_slice, is_aligned, ModRange}; +use crate::api::upgrade_storage::UpgradeStorage; use alloc::vec::Vec; use core::cell::Cell; use libtock_core::{callback, syscalls}; diff --git a/src/lib.rs b/src/lib.rs index e068f26..c3be33d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,19 +24,18 @@ use crate::ctap::CtapState; use crate::env::Env; use libtock_drivers::timer::ClockValue; +pub mod api; // Implementation details must be public for testing (in particular fuzzing). #[cfg(feature = "std")] pub mod ctap; #[cfg(not(feature = "std"))] mod ctap; -// Store example binaries use the flash directly. Eventually, they should access it from env::tock. -pub mod embedded_flash; pub mod env; /// CTAP implementation parameterized by its environment. pub struct Ctap { env: E, - state: CtapState, + state: CtapState, hid: CtapHid, } @@ -50,7 +49,7 @@ impl Ctap { Ctap { env, state, hid } } - pub fn state(&mut self) -> &mut CtapState { + pub fn state(&mut self) -> &mut CtapState { &mut self.state } diff --git a/src/main.rs b/src/main.rs index 04f4c3b..ab3be82 100644 --- a/src/main.rs +++ b/src/main.rs @@ -57,7 +57,7 @@ fn main() { } let boot_time = timer.get_current_clock().flex_unwrap(); - let env = TockEnv::new(); + let env = TockEnv::new().unwrap(); let mut ctap = ctap2::Ctap::new(env, boot_time); let mut led_counter = 0;