From 732523d38021d6d90a725196045e21ce7e13227e Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Fri, 4 Mar 2022 17:14:23 +0100 Subject: [PATCH] Use Store instead of Storage in Env --- src/ctap/client_pin.rs | 246 ++--- src/ctap/config_command.rs | 136 ++- src/ctap/credential_management.rs | 180 ++-- src/ctap/ctap1.rs | 65 +- src/ctap/hid/mod.rs | 18 +- src/ctap/large_blobs.rs | 43 +- src/ctap/mod.rs | 500 +++++----- src/ctap/storage.rs | 1406 ++++++++++++++--------------- src/env/mod.rs | 15 +- src/env/test/mod.rs | 52 +- src/env/tock/mod.rs | 29 +- src/lib.rs | 4 +- 12 files changed, 1202 insertions(+), 1492 deletions(-) diff --git a/src/ctap/client_pin.rs b/src/ctap/client_pin.rs index 2cac1e0..ce3c24f 100644 --- a/src/ctap/client_pin.rs +++ b/src/ctap/client_pin.rs @@ -19,8 +19,8 @@ use super::data_formats::{ use super::pin_protocol::{verify_pin_uv_auth_token, PinProtocol, SharedSecret}; use super::response::{AuthenticatorClientPinResponse, ResponseData}; use super::status_code::Ctap2StatusCode; -use super::storage::PersistentStore; use super::token_state::PinUvAuthTokenState; +use crate::ctap::storage; use crate::env::Env; use alloc::boxed::Box; use alloc::str; @@ -76,13 +76,13 @@ 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( + env: &mut impl Env, shared_secret: &dyn SharedSecret, new_pin_enc: Vec, ) -> Result<(), Ctap2StatusCode> { let pin = decrypt_pin(shared_secret, new_pin_enc)?; - let min_pin_length = persistent_store.min_pin_length()? as usize; + let min_pin_length = storage::min_pin_length(env)? as usize; let pin_length = str::from_utf8(&pin).unwrap_or("").chars().count(); if pin_length < min_pin_length || pin.len() == PIN_PADDED_LENGTH { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_POLICY_VIOLATION); @@ -90,7 +90,7 @@ fn check_and_store_new_pin( let mut pin_hash = [0u8; PIN_AUTH_LENGTH]; pin_hash.copy_from_slice(&Sha256::hash(&pin[..])[..PIN_AUTH_LENGTH]); // The PIN length is always < PIN_PADDED_LENGTH < 256. - persistent_store.set_pin(&pin_hash, pin_length as u8)?; + storage::set_pin(env, &pin_hash, pin_length as u8)?; Ok(()) } @@ -156,28 +156,27 @@ 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, + env: &mut impl Env, pin_uv_auth_protocol: PinUvAuthProtocol, shared_secret: &dyn SharedSecret, pin_hash_enc: Vec, ) -> Result<(), Ctap2StatusCode> { - match persistent_store.pin_hash()? { + match storage::pin_hash(env)? { Some(pin_hash) => { if self.consecutive_pin_mismatches >= 3 { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_BLOCKED); } - persistent_store.decr_pin_retries()?; + storage::decr_pin_retries(env)?; let pin_hash_dec = shared_secret .decrypt(&pin_hash_enc) .map_err(|_| Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)?; if !bool::from(pin_hash.ct_eq(&pin_hash_dec)) { self.get_mut_pin_protocol(pin_uv_auth_protocol) - .regenerate(rng); - if persistent_store.pin_retries()? == 0 { + .regenerate(env.rng()); + if storage::pin_retries(env)? == 0 { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_BLOCKED); } self.consecutive_pin_mismatches += 1; @@ -190,19 +189,19 @@ impl ClientPin { // This status code is not explicitly mentioned in the specification. None => return Err(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED), } - persistent_store.reset_pin_retries()?; + storage::reset_pin_retries(env)?; self.consecutive_pin_mismatches = 0; Ok(()) } - fn process_get_pin_retries( + fn process_get_pin_retries( &self, - persistent_store: &PersistentStore, + env: &mut impl Env, ) -> Result { Ok(AuthenticatorClientPinResponse { key_agreement: None, pin_uv_auth_token: None, - retries: Some(persistent_store.pin_retries()? as u64), + retries: Some(storage::pin_retries(env)? as u64), power_cycle_state: Some(self.consecutive_pin_mismatches >= 3), }) } @@ -223,9 +222,9 @@ impl ClientPin { }) } - fn process_set_pin( + fn process_set_pin( &mut self, - persistent_store: &mut PersistentStore, + env: &mut impl Env, client_pin_params: AuthenticatorClientPinParameters, ) -> Result<(), Ctap2StatusCode> { let AuthenticatorClientPinParameters { @@ -239,21 +238,20 @@ impl ClientPin { let pin_uv_auth_param = ok_or_missing(pin_uv_auth_param)?; let new_pin_enc = ok_or_missing(new_pin_enc)?; - if persistent_store.pin_hash()?.is_some() { + if storage::pin_hash(env)?.is_some() { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID); } let shared_secret = self.get_shared_secret(pin_uv_auth_protocol, key_agreement)?; shared_secret.verify(&new_pin_enc, &pin_uv_auth_param)?; - check_and_store_new_pin(persistent_store, shared_secret.as_ref(), new_pin_enc)?; - persistent_store.reset_pin_retries()?; + check_and_store_new_pin(env, shared_secret.as_ref(), new_pin_enc)?; + storage::reset_pin_retries(env)?; Ok(()) } - fn process_change_pin( + fn process_change_pin( &mut self, - rng: &mut impl Rng256, - persistent_store: &mut PersistentStore, + env: &mut impl Env, client_pin_params: AuthenticatorClientPinParameters, ) -> Result<(), Ctap2StatusCode> { let AuthenticatorClientPinParameters { @@ -269,7 +267,7 @@ impl ClientPin { let new_pin_enc = ok_or_missing(new_pin_enc)?; let pin_hash_enc = ok_or_missing(pin_hash_enc)?; - if persistent_store.pin_retries()? == 0 { + if storage::pin_retries(env)? == 0 { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_BLOCKED); } let shared_secret = self.get_shared_secret(pin_uv_auth_protocol, key_agreement)?; @@ -277,23 +275,21 @@ impl ClientPin { auth_param_data.extend(&pin_hash_enc); shared_secret.verify(&auth_param_data, &pin_uv_auth_param)?; self.verify_pin_hash_enc( - rng, - persistent_store, + env, pin_uv_auth_protocol, shared_secret.as_ref(), pin_hash_enc, )?; - check_and_store_new_pin(persistent_store, shared_secret.as_ref(), new_pin_enc)?; - self.pin_protocol_v1.reset_pin_uv_auth_token(rng); - self.pin_protocol_v2.reset_pin_uv_auth_token(rng); + check_and_store_new_pin(env, shared_secret.as_ref(), new_pin_enc)?; + self.pin_protocol_v1.reset_pin_uv_auth_token(env.rng()); + self.pin_protocol_v2.reset_pin_uv_auth_token(env.rng()); Ok(()) } - fn process_get_pin_token( + fn process_get_pin_token( &mut self, - rng: &mut impl Rng256, - persistent_store: &mut PersistentStore, + env: &mut impl Env, client_pin_params: AuthenticatorClientPinParameters, now: ClockValue, ) -> Result { @@ -311,28 +307,27 @@ impl ClientPin { return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER); } - if persistent_store.pin_retries()? == 0 { + if storage::pin_retries(env)? == 0 { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_BLOCKED); } let shared_secret = self.get_shared_secret(pin_uv_auth_protocol, key_agreement)?; self.verify_pin_hash_enc( - rng, - persistent_store, + env, pin_uv_auth_protocol, shared_secret.as_ref(), pin_hash_enc, )?; - if persistent_store.has_force_pin_change()? { + if storage::has_force_pin_change(env)? { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID); } - self.pin_protocol_v1.reset_pin_uv_auth_token(rng); - self.pin_protocol_v2.reset_pin_uv_auth_token(rng); + self.pin_protocol_v1.reset_pin_uv_auth_token(env.rng()); + self.pin_protocol_v2.reset_pin_uv_auth_token(env.rng()); self.pin_uv_auth_token_state .begin_using_pin_uv_auth_token(now); self.pin_uv_auth_token_state.set_default_permissions(); let pin_uv_auth_token = shared_secret.encrypt( - rng, + env.rng(), self.get_pin_protocol(pin_uv_auth_protocol) .get_pin_uv_auth_token(), )?; @@ -360,10 +355,9 @@ 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, + env: &mut impl Env, mut client_pin_params: AuthenticatorClientPinParameters, now: ClockValue, ) -> Result { @@ -381,7 +375,7 @@ impl ClientPin { return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER); } - let response = self.process_get_pin_token(rng, persistent_store, client_pin_params, now)?; + let response = self.process_get_pin_token(env, client_pin_params, now)?; self.pin_uv_auth_token_state.set_permissions(permissions); self.pin_uv_auth_token_state .set_permissions_rp_id(permissions_rp_id); @@ -390,30 +384,27 @@ impl ClientPin { } /// Processes the authenticatorClientPin command. - pub fn process_command( + pub fn process_command( &mut self, - rng: &mut impl Rng256, - persistent_store: &mut PersistentStore, + env: &mut impl Env, client_pin_params: AuthenticatorClientPinParameters, now: ClockValue, ) -> Result { let response = match client_pin_params.sub_command { - ClientPinSubCommand::GetPinRetries => { - Some(self.process_get_pin_retries(persistent_store)?) - } + ClientPinSubCommand::GetPinRetries => Some(self.process_get_pin_retries(env)?), ClientPinSubCommand::GetKeyAgreement => { Some(self.process_get_key_agreement(client_pin_params)?) } ClientPinSubCommand::SetPin => { - self.process_set_pin(persistent_store, client_pin_params)?; + self.process_set_pin(env, client_pin_params)?; None } ClientPinSubCommand::ChangePin => { - self.process_change_pin(rng, persistent_store, client_pin_params)?; + self.process_change_pin(env, client_pin_params)?; None } ClientPinSubCommand::GetPinToken => { - Some(self.process_get_pin_token(rng, persistent_store, client_pin_params, now)?) + Some(self.process_get_pin_token(env, client_pin_params, now)?) } ClientPinSubCommand::GetPinUvAuthTokenUsingUvWithPermissions => Some( self.process_get_pin_uv_auth_token_using_uv_with_permissions(client_pin_params)?, @@ -421,8 +412,7 @@ impl ClientPin { ClientPinSubCommand::GetUvRetries => Some(self.process_get_uv_retries()?), ClientPinSubCommand::GetPinUvAuthTokenUsingPinWithPermissions => Some( self.process_get_pin_uv_auth_token_using_pin_with_permissions( - rng, - persistent_store, + env, client_pin_params, now, )?, @@ -602,12 +592,12 @@ mod test { 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(env: &mut TestEnv) { let mut pin = [0u8; 64]; pin[..4].copy_from_slice(b"1234"); let mut pin_hash = [0u8; 16]; pin_hash.copy_from_slice(&Sha256::hash(&pin[..])[..16]); - persistent_store.set_pin(&pin_hash, 4).unwrap(); + storage::set_pin(env, &pin_hash, 4).unwrap(); } /// Fails on PINs bigger than 64 bytes. @@ -733,7 +723,6 @@ mod test { fn test_helper_verify_pin_hash_enc(pin_uv_auth_protocol: PinUvAuthProtocol) { 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 @@ -744,7 +733,7 @@ mod test { 0x01, 0xD9, 0x88, 0x40, 0x50, 0xBB, 0xD0, 0x7A, 0x23, 0x1A, 0xEB, 0x69, 0xD8, 0x36, 0xC4, 0x12, ]; - persistent_store.set_pin(&pin_hash, 4).unwrap(); + storage::set_pin(&mut env, &pin_hash, 4).unwrap(); let pin_hash_enc = shared_secret .as_ref() @@ -752,8 +741,7 @@ mod test { .unwrap(); assert_eq!( client_pin.verify_pin_hash_enc( - env.rng(), - &mut persistent_store, + &mut env, pin_uv_auth_protocol, shared_secret.as_ref(), pin_hash_enc @@ -764,8 +752,7 @@ mod test { let pin_hash_enc = vec![0xEE; 16]; assert_eq!( client_pin.verify_pin_hash_enc( - env.rng(), - &mut persistent_store, + &mut env, pin_uv_auth_protocol, shared_secret.as_ref(), pin_hash_enc @@ -780,8 +767,7 @@ mod test { client_pin.consecutive_pin_mismatches = 3; assert_eq!( client_pin.verify_pin_hash_enc( - env.rng(), - &mut persistent_store, + &mut env, pin_uv_auth_protocol, shared_secret.as_ref(), pin_hash_enc @@ -793,8 +779,7 @@ mod test { let pin_hash_enc = vec![0x77; PIN_AUTH_LENGTH - 1]; assert_eq!( client_pin.verify_pin_hash_enc( - env.rng(), - &mut persistent_store, + &mut env, pin_uv_auth_protocol, shared_secret.as_ref(), pin_hash_enc @@ -805,8 +790,7 @@ mod test { let pin_hash_enc = vec![0x77; PIN_AUTH_LENGTH + 1]; assert_eq!( client_pin.verify_pin_hash_enc( - env.rng(), - &mut persistent_store, + &mut env, pin_uv_auth_protocol, shared_secret.as_ref(), pin_hash_enc @@ -831,20 +815,14 @@ mod test { ClientPinSubCommand::GetPinRetries, ); 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, - retries: Some(persistent_store.pin_retries().unwrap() as u64), + retries: Some(storage::pin_retries(&mut env).unwrap() as u64), power_cycle_state: Some(false), }); assert_eq!( - client_pin.process_command( - env.rng(), - &mut persistent_store, - params.clone(), - DUMMY_CLOCK_VALUE - ), + client_pin.process_command(&mut env, params.clone(), DUMMY_CLOCK_VALUE), Ok(ResponseData::AuthenticatorClientPin(expected_response)) ); @@ -852,11 +830,11 @@ mod test { let expected_response = Some(AuthenticatorClientPinResponse { key_agreement: None, pin_uv_auth_token: None, - retries: Some(persistent_store.pin_retries().unwrap() as u64), + retries: Some(storage::pin_retries(&mut env).unwrap() as u64), power_cycle_state: Some(true), }); assert_eq!( - client_pin.process_command(env.rng(), &mut persistent_store, params, DUMMY_CLOCK_VALUE), + client_pin.process_command(&mut env, params, DUMMY_CLOCK_VALUE), Ok(ResponseData::AuthenticatorClientPin(expected_response)) ); } @@ -877,7 +855,6 @@ mod test { ClientPinSubCommand::GetKeyAgreement, ); 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, @@ -885,7 +862,7 @@ mod test { power_cycle_state: None, }); assert_eq!( - client_pin.process_command(env.rng(), &mut persistent_store, params, DUMMY_CLOCK_VALUE), + client_pin.process_command(&mut env, params, DUMMY_CLOCK_VALUE), Ok(ResponseData::AuthenticatorClientPin(expected_response)) ); } @@ -904,9 +881,8 @@ mod test { let (mut client_pin, params) = create_client_pin_and_parameters(pin_uv_auth_protocol, ClientPinSubCommand::SetPin); let mut env = TestEnv::new(); - let mut persistent_store = PersistentStore::new(&mut env); assert_eq!( - client_pin.process_command(env.rng(), &mut persistent_store, params, DUMMY_CLOCK_VALUE), + client_pin.process_command(&mut env, params, DUMMY_CLOCK_VALUE), Ok(ResponseData::AuthenticatorClientPin(None)) ); } @@ -932,40 +908,29 @@ mod test { ) .unwrap(); let mut env = TestEnv::new(); - let mut persistent_store = PersistentStore::new(&mut env); - set_standard_pin(&mut persistent_store); + set_standard_pin(&mut env); let mut auth_param_data = params.new_pin_enc.clone().unwrap(); auth_param_data.extend(params.pin_hash_enc.as_ref().unwrap()); let pin_uv_auth_param = shared_secret.authenticate(&auth_param_data); params.pin_uv_auth_param = Some(pin_uv_auth_param); assert_eq!( - client_pin.process_command( - env.rng(), - &mut persistent_store, - params.clone(), - DUMMY_CLOCK_VALUE - ), + client_pin.process_command(&mut env, params.clone(), DUMMY_CLOCK_VALUE), Ok(ResponseData::AuthenticatorClientPin(None)) ); let mut bad_params = params.clone(); bad_params.pin_hash_enc = Some(vec![0xEE; 16]); assert_eq!( - client_pin.process_command( - env.rng(), - &mut persistent_store, - bad_params, - DUMMY_CLOCK_VALUE - ), + client_pin.process_command(&mut env, bad_params, DUMMY_CLOCK_VALUE), Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID) ); - while persistent_store.pin_retries().unwrap() > 0 { - persistent_store.decr_pin_retries().unwrap(); + while storage::pin_retries(&mut env).unwrap() > 0 { + storage::decr_pin_retries(&mut env).unwrap(); } assert_eq!( - client_pin.process_command(env.rng(), &mut persistent_store, params, DUMMY_CLOCK_VALUE), + client_pin.process_command(&mut env, params, DUMMY_CLOCK_VALUE), Err(Ctap2StatusCode::CTAP2_ERR_PIN_BLOCKED) ); } @@ -993,16 +958,10 @@ mod test { ) .unwrap(); let mut env = TestEnv::new(); - let mut persistent_store = PersistentStore::new(&mut env); - set_standard_pin(&mut persistent_store); + set_standard_pin(&mut env); let response = client_pin - .process_command( - env.rng(), - &mut persistent_store, - params.clone(), - DUMMY_CLOCK_VALUE, - ) + .process_command(&mut env, params.clone(), DUMMY_CLOCK_VALUE) .unwrap(); let encrypted_token = match response { ResponseData::AuthenticatorClientPin(Some(response)) => { @@ -1038,12 +997,7 @@ mod test { let mut bad_params = params; bad_params.pin_hash_enc = Some(vec![0xEE; 16]); assert_eq!( - client_pin.process_command( - env.rng(), - &mut persistent_store, - bad_params, - DUMMY_CLOCK_VALUE - ), + client_pin.process_command(&mut env, bad_params, DUMMY_CLOCK_VALUE), Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID) ); } @@ -1064,12 +1018,11 @@ mod test { ClientPinSubCommand::GetPinToken, ); let mut env = TestEnv::new(); - let mut persistent_store = PersistentStore::new(&mut env); - set_standard_pin(&mut persistent_store); + set_standard_pin(&mut env); - assert_eq!(persistent_store.force_pin_change(), Ok(())); + assert_eq!(storage::force_pin_change(&mut env), Ok(())); assert_eq!( - client_pin.process_command(env.rng(), &mut persistent_store, params, DUMMY_CLOCK_VALUE), + client_pin.process_command(&mut env, params, DUMMY_CLOCK_VALUE), Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID), ); } @@ -1099,16 +1052,10 @@ mod test { ) .unwrap(); let mut env = TestEnv::new(); - let mut persistent_store = PersistentStore::new(&mut env); - set_standard_pin(&mut persistent_store); + set_standard_pin(&mut env); let response = client_pin - .process_command( - env.rng(), - &mut persistent_store, - params.clone(), - DUMMY_CLOCK_VALUE, - ) + .process_command(&mut env, params.clone(), DUMMY_CLOCK_VALUE) .unwrap(); let encrypted_token = match response { ResponseData::AuthenticatorClientPin(Some(response)) => { @@ -1144,36 +1091,21 @@ mod test { let mut bad_params = params.clone(); bad_params.permissions = Some(0x00); assert_eq!( - client_pin.process_command( - env.rng(), - &mut persistent_store, - bad_params, - DUMMY_CLOCK_VALUE - ), + client_pin.process_command(&mut env, bad_params, DUMMY_CLOCK_VALUE), Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER) ); let mut bad_params = params.clone(); bad_params.permissions_rp_id = None; assert_eq!( - client_pin.process_command( - env.rng(), - &mut persistent_store, - bad_params, - DUMMY_CLOCK_VALUE - ), + client_pin.process_command(&mut env, bad_params, DUMMY_CLOCK_VALUE), Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER) ); let mut bad_params = params; bad_params.pin_hash_enc = Some(vec![0xEE; 16]); assert_eq!( - client_pin.process_command( - env.rng(), - &mut persistent_store, - bad_params, - DUMMY_CLOCK_VALUE - ), + client_pin.process_command(&mut env, bad_params, DUMMY_CLOCK_VALUE), Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID) ); } @@ -1196,12 +1128,11 @@ mod test { ClientPinSubCommand::GetPinUvAuthTokenUsingPinWithPermissions, ); let mut env = TestEnv::new(); - let mut persistent_store = PersistentStore::new(&mut env); - set_standard_pin(&mut persistent_store); + set_standard_pin(&mut env); - assert_eq!(persistent_store.force_pin_change(), Ok(())); + assert_eq!(storage::force_pin_change(&mut env), Ok(())); assert_eq!( - client_pin.process_command(env.rng(), &mut persistent_store, params, DUMMY_CLOCK_VALUE), + client_pin.process_command(&mut env, params, DUMMY_CLOCK_VALUE), Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID) ); } @@ -1266,7 +1197,6 @@ mod test { fn test_helper_check_and_store_new_pin(pin_uv_auth_protocol: PinUvAuthProtocol) { 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) @@ -1292,17 +1222,17 @@ mod test { ), ]; for (pin, result) in test_cases { - let old_pin_hash = persistent_store.pin_hash().unwrap(); + let old_pin_hash = storage::pin_hash(&mut env).unwrap(); let new_pin_enc = encrypt_pin(shared_secret.as_ref(), pin); assert_eq!( - check_and_store_new_pin(&mut persistent_store, shared_secret.as_ref(), new_pin_enc), + check_and_store_new_pin(&mut env, shared_secret.as_ref(), new_pin_enc), result ); if result.is_ok() { - assert_ne!(old_pin_hash, persistent_store.pin_hash().unwrap()); + assert_ne!(old_pin_hash, storage::pin_hash(&mut env).unwrap()); } else { - assert_eq!(old_pin_hash, persistent_store.pin_hash().unwrap()); + assert_eq!(old_pin_hash, storage::pin_hash(&mut env).unwrap()); } } } @@ -1683,12 +1613,11 @@ mod test { ClientPinSubCommand::GetPinUvAuthTokenUsingPinWithPermissions, ); let mut env = TestEnv::new(); - let mut persistent_store = PersistentStore::new(&mut env); - set_standard_pin(&mut persistent_store); + set_standard_pin(&mut env); params.permissions = Some(0xFF); assert!(client_pin - .process_command(env.rng(), &mut persistent_store, params, DUMMY_CLOCK_VALUE) + .process_command(&mut env, params, DUMMY_CLOCK_VALUE) .is_ok()); for permission in PinPermission::into_enum_iter() { assert_eq!( @@ -1730,12 +1659,11 @@ mod test { ClientPinSubCommand::GetPinUvAuthTokenUsingPinWithPermissions, ); let mut env = TestEnv::new(); - let mut persistent_store = PersistentStore::new(&mut env); - set_standard_pin(&mut persistent_store); + set_standard_pin(&mut env); params.permissions = Some(0xFF); assert!(client_pin - .process_command(env.rng(), &mut persistent_store, params, DUMMY_CLOCK_VALUE) + .process_command(&mut env, 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 e02fbbb..6cb1948 100644 --- a/src/ctap/config_command.rs +++ b/src/ctap/config_command.rs @@ -18,16 +18,16 @@ use super::customization::ENTERPRISE_ATTESTATION_MODE; use super::data_formats::{ConfigSubCommand, ConfigSubCommandParams, SetMinPinLengthParams}; use super::response::ResponseData; use super::status_code::Ctap2StatusCode; -use super::storage::PersistentStore; +use crate::ctap::storage; 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( + env: &mut impl Env, ) -> Result { if ENTERPRISE_ATTESTATION_MODE.is_some() { - persistent_store.enable_enterprise_attestation()?; + storage::enable_enterprise_attestation(env)?; Ok(ResponseData::AuthenticatorConfig) } else { Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER) @@ -35,16 +35,14 @@ fn process_enable_enterprise_attestation( } /// Processes the subcommand toggleAlwaysUv for AuthenticatorConfig. -fn process_toggle_always_uv( - persistent_store: &mut PersistentStore, -) -> Result { - persistent_store.toggle_always_uv()?; +fn process_toggle_always_uv(env: &mut impl Env) -> Result { + storage::toggle_always_uv(env)?; 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( + env: &mut impl Env, params: SetMinPinLengthParams, ) -> Result { let SetMinPinLengthParams { @@ -52,31 +50,31 @@ fn process_set_min_pin_length( min_pin_length_rp_ids, force_change_pin, } = params; - let store_min_pin_length = persistent_store.min_pin_length()?; + let store_min_pin_length = storage::min_pin_length(env)?; let new_min_pin_length = new_min_pin_length.unwrap_or(store_min_pin_length); if new_min_pin_length < store_min_pin_length { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_POLICY_VIOLATION); } let mut force_change_pin = force_change_pin.unwrap_or(false); - if force_change_pin && persistent_store.pin_hash()?.is_none() { + if force_change_pin && storage::pin_hash(env)?.is_none() { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_NOT_SET); } - if let Some(old_length) = persistent_store.pin_code_point_length()? { + if let Some(old_length) = storage::pin_code_point_length(env)? { force_change_pin |= new_min_pin_length > old_length; } if force_change_pin { - persistent_store.force_pin_change()?; + storage::force_pin_change(env)?; } - persistent_store.set_min_pin_length(new_min_pin_length)?; + storage::set_min_pin_length(env, new_min_pin_length)?; if let Some(min_pin_length_rp_ids) = min_pin_length_rp_ids { - persistent_store.set_min_pin_length_rp_ids(min_pin_length_rp_ids)?; + storage::set_min_pin_length_rp_ids(env, min_pin_length_rp_ids)?; } Ok(ResponseData::AuthenticatorConfig) } /// Processes the AuthenticatorConfig command. -pub fn process_config( - persistent_store: &mut PersistentStore, +pub fn process_config( + env: &mut impl Env, client_pin: &mut ClientPin, params: AuthenticatorConfigParameters, ) -> Result { @@ -87,9 +85,9 @@ pub fn process_config( pin_uv_auth_protocol, } = params; - let enforce_uv = !matches!(sub_command, ConfigSubCommand::ToggleAlwaysUv) - && persistent_store.has_always_uv()?; - if persistent_store.pin_hash()?.is_some() || enforce_uv { + let enforce_uv = + !matches!(sub_command, ConfigSubCommand::ToggleAlwaysUv) && storage::has_always_uv(env)?; + if storage::pin_hash(env)?.is_some() || enforce_uv { let pin_uv_auth_param = pin_uv_auth_param.ok_or(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED)?; let pin_uv_auth_protocol = @@ -109,13 +107,11 @@ pub fn process_config( } match sub_command { - ConfigSubCommand::EnableEnterpriseAttestation => { - process_enable_enterprise_attestation(persistent_store) - } - ConfigSubCommand::ToggleAlwaysUv => process_toggle_always_uv(persistent_store), + ConfigSubCommand::EnableEnterpriseAttestation => process_enable_enterprise_attestation(env), + ConfigSubCommand::ToggleAlwaysUv => process_toggle_always_uv(env), ConfigSubCommand::SetMinPinLength => { if let Some(ConfigSubCommandParams::SetMinPinLength(params)) = sub_command_params { - process_set_min_pin_length(persistent_store, params) + process_set_min_pin_length(env, params) } else { Err(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER) } @@ -135,7 +131,6 @@ mod test { #[test] fn test_process_enable_enterprise_attestation() { 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 = @@ -147,11 +142,11 @@ mod test { pin_uv_auth_param: None, pin_uv_auth_protocol: None, }; - let config_response = process_config(&mut persistent_store, &mut client_pin, config_params); + let config_response = process_config(&mut env, &mut client_pin, config_params); if ENTERPRISE_ATTESTATION_MODE.is_some() { assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig)); - assert_eq!(persistent_store.enterprise_attestation(), Ok(true)); + assert_eq!(storage::enterprise_attestation(&mut env), Ok(true)); } else { assert_eq!( config_response, @@ -163,7 +158,6 @@ mod test { #[test] fn test_process_toggle_always_uv() { 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 = @@ -175,9 +169,9 @@ mod test { pin_uv_auth_param: None, pin_uv_auth_protocol: None, }; - let config_response = process_config(&mut persistent_store, &mut client_pin, config_params); + let config_response = process_config(&mut env, &mut client_pin, config_params); assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig)); - assert!(persistent_store.has_always_uv().unwrap()); + assert!(storage::has_always_uv(&mut env).unwrap()); let config_params = AuthenticatorConfigParameters { sub_command: ConfigSubCommand::ToggleAlwaysUv, @@ -185,7 +179,7 @@ mod test { pin_uv_auth_param: None, pin_uv_auth_protocol: None, }; - let config_response = process_config(&mut persistent_store, &mut client_pin, config_params); + let config_response = process_config(&mut env, &mut client_pin, config_params); if ENFORCE_ALWAYS_UV { assert_eq!( config_response, @@ -193,18 +187,17 @@ mod test { ); } else { assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig)); - assert!(!persistent_store.has_always_uv().unwrap()); + assert!(!storage::has_always_uv(&mut env).unwrap()); } } fn test_helper_process_toggle_always_uv_with_pin(pin_uv_auth_protocol: PinUvAuthProtocol) { 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); - persistent_store.set_pin(&[0x88; 16], 4).unwrap(); + storage::set_pin(&mut env, &[0x88; 16], 4).unwrap(); let mut config_data = vec![0xFF; 32]; config_data.extend(&[0x0D, ConfigSubCommand::ToggleAlwaysUv as u8]); @@ -216,7 +209,7 @@ mod test { pin_uv_auth_param: Some(pin_uv_auth_param.clone()), pin_uv_auth_protocol: Some(pin_uv_auth_protocol), }; - let config_response = process_config(&mut persistent_store, &mut client_pin, config_params); + let config_response = process_config(&mut env, &mut client_pin, config_params); if ENFORCE_ALWAYS_UV { assert_eq!( config_response, @@ -225,7 +218,7 @@ mod test { return; } assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig)); - assert!(persistent_store.has_always_uv().unwrap()); + assert!(storage::has_always_uv(&mut env).unwrap()); let config_params = AuthenticatorConfigParameters { sub_command: ConfigSubCommand::ToggleAlwaysUv, @@ -233,9 +226,9 @@ mod test { pin_uv_auth_param: Some(pin_uv_auth_param), pin_uv_auth_protocol: Some(pin_uv_auth_protocol), }; - let config_response = process_config(&mut persistent_store, &mut client_pin, config_params); + let config_response = process_config(&mut env, &mut client_pin, config_params); assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig)); - assert!(!persistent_store.has_always_uv().unwrap()); + assert!(!storage::has_always_uv(&mut env).unwrap()); } #[test] @@ -270,7 +263,6 @@ mod test { #[test] fn test_process_set_min_pin_length() { 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 = @@ -279,13 +271,13 @@ mod test { // First, increase minimum PIN length from 4 to 6 without PIN auth. let min_pin_length = 6; let config_params = create_min_pin_config_params(min_pin_length, None); - let config_response = process_config(&mut persistent_store, &mut client_pin, config_params); + let config_response = process_config(&mut env, &mut client_pin, config_params); assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig)); - assert_eq!(persistent_store.min_pin_length(), Ok(min_pin_length)); + assert_eq!(storage::min_pin_length(&mut env), Ok(min_pin_length)); // Second, increase minimum PIN length from 6 to 8 with PIN auth. // The stored PIN or its length don't matter since we control the token. - persistent_store.set_pin(&[0x88; 16], 8).unwrap(); + storage::set_pin(&mut env, &[0x88; 16], 8).unwrap(); let min_pin_length = 8; let mut config_params = create_min_pin_config_params(min_pin_length, None); let pin_uv_auth_param = vec![ @@ -293,9 +285,9 @@ mod test { 0xB2, 0xDE, ]; config_params.pin_uv_auth_param = Some(pin_uv_auth_param); - let config_response = process_config(&mut persistent_store, &mut client_pin, config_params); + let config_response = process_config(&mut env, &mut client_pin, config_params); assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig)); - assert_eq!(persistent_store.min_pin_length(), Ok(min_pin_length)); + assert_eq!(storage::min_pin_length(&mut env), Ok(min_pin_length)); // Third, decreasing the minimum PIN length from 8 to 7 fails. let mut config_params = create_min_pin_config_params(7, None); @@ -304,18 +296,17 @@ mod test { 0xA7, 0x71, ]; config_params.pin_uv_auth_param = Some(pin_uv_auth_param); - let config_response = process_config(&mut persistent_store, &mut client_pin, config_params); + let config_response = process_config(&mut env, &mut client_pin, config_params); assert_eq!( config_response, Err(Ctap2StatusCode::CTAP2_ERR_PIN_POLICY_VIOLATION) ); - assert_eq!(persistent_store.min_pin_length(), Ok(min_pin_length)); + assert_eq!(storage::min_pin_length(&mut env), Ok(min_pin_length)); } #[test] fn test_process_set_min_pin_length_rp_ids() { 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 = @@ -326,11 +317,11 @@ mod test { let min_pin_length_rp_ids = vec!["example.com".to_string()]; let config_params = create_min_pin_config_params(min_pin_length, Some(min_pin_length_rp_ids.clone())); - let config_response = process_config(&mut persistent_store, &mut client_pin, config_params); + let config_response = process_config(&mut env, &mut client_pin, config_params); assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig)); - assert_eq!(persistent_store.min_pin_length(), Ok(min_pin_length)); + assert_eq!(storage::min_pin_length(&mut env), Ok(min_pin_length)); assert_eq!( - persistent_store.min_pin_length_rp_ids(), + storage::min_pin_length_rp_ids(&mut env), Ok(min_pin_length_rp_ids) ); @@ -338,7 +329,7 @@ mod test { let min_pin_length = 8; let min_pin_length_rp_ids = vec!["another.example.com".to_string()]; // The stored PIN or its length don't matter since we control the token. - persistent_store.set_pin(&[0x88; 16], 8).unwrap(); + storage::set_pin(&mut env, &[0x88; 16], 8).unwrap(); let mut config_params = create_min_pin_config_params(min_pin_length, Some(min_pin_length_rp_ids.clone())); let pin_uv_auth_param = vec![ @@ -346,11 +337,11 @@ mod test { 0xD6, 0xDA, ]; config_params.pin_uv_auth_param = Some(pin_uv_auth_param.clone()); - let config_response = process_config(&mut persistent_store, &mut client_pin, config_params); + let config_response = process_config(&mut env, &mut client_pin, config_params); assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig)); - assert_eq!(persistent_store.min_pin_length(), Ok(min_pin_length)); + assert_eq!(storage::min_pin_length(&mut env), Ok(min_pin_length)); assert_eq!( - persistent_store.min_pin_length_rp_ids(), + storage::min_pin_length_rp_ids(&mut env), Ok(min_pin_length_rp_ids.clone()) ); @@ -359,14 +350,14 @@ mod test { let mut config_params = create_min_pin_config_params(9, Some(min_pin_length_rp_ids.clone())); config_params.pin_uv_auth_param = Some(pin_uv_auth_param.clone()); - let config_response = process_config(&mut persistent_store, &mut client_pin, config_params); + let config_response = process_config(&mut env, &mut client_pin, config_params); assert_eq!( config_response, Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID) ); - assert_eq!(persistent_store.min_pin_length(), Ok(min_pin_length)); + assert_eq!(storage::min_pin_length(&mut env), Ok(min_pin_length)); assert_eq!( - persistent_store.min_pin_length_rp_ids(), + storage::min_pin_length_rp_ids(&mut env), Ok(min_pin_length_rp_ids.clone()) ); @@ -377,14 +368,14 @@ mod test { Some(vec!["counter.example.com".to_string()]), ); config_params.pin_uv_auth_param = Some(pin_uv_auth_param); - let config_response = process_config(&mut persistent_store, &mut client_pin, config_params); + let config_response = process_config(&mut env, &mut client_pin, config_params); assert_eq!( config_response, Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID) ); - assert_eq!(persistent_store.min_pin_length(), Ok(min_pin_length)); + assert_eq!(storage::min_pin_length(&mut env), Ok(min_pin_length)); assert_eq!( - persistent_store.min_pin_length_rp_ids(), + storage::min_pin_length_rp_ids(&mut env), Ok(min_pin_length_rp_ids) ); } @@ -392,13 +383,12 @@ mod test { #[test] fn test_process_set_min_pin_length_force_pin_change_implicit() { 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); - persistent_store.set_pin(&[0x88; 16], 4).unwrap(); + storage::set_pin(&mut env, &[0x88; 16], 4).unwrap(); // Increase min PIN, force PIN change. let min_pin_length = 6; let mut config_params = create_min_pin_config_params(min_pin_length, None); @@ -407,28 +397,27 @@ mod test { 0xA8, 0xC8, ]); config_params.pin_uv_auth_param = pin_uv_auth_param; - let config_response = process_config(&mut persistent_store, &mut client_pin, config_params); + let config_response = process_config(&mut env, &mut client_pin, config_params); assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig)); - assert_eq!(persistent_store.min_pin_length(), Ok(min_pin_length)); - assert_eq!(persistent_store.has_force_pin_change(), Ok(true)); + assert_eq!(storage::min_pin_length(&mut env), Ok(min_pin_length)); + assert_eq!(storage::has_force_pin_change(&mut env), Ok(true)); } #[test] fn test_process_set_min_pin_length_force_pin_change_explicit() { 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); - persistent_store.set_pin(&[0x88; 16], 4).unwrap(); + storage::set_pin(&mut env, &[0x88; 16], 4).unwrap(); let pin_uv_auth_param = Some(vec![ 0xE3, 0x74, 0xF4, 0x27, 0xBE, 0x7D, 0x40, 0xB5, 0x71, 0xB6, 0xB4, 0x1A, 0xD2, 0xC1, 0x53, 0xD7, ]); let set_min_pin_length_params = SetMinPinLengthParams { - new_min_pin_length: Some(persistent_store.min_pin_length().unwrap()), + new_min_pin_length: Some(storage::min_pin_length(&mut env).unwrap()), min_pin_length_rp_ids: None, force_change_pin: Some(true), }; @@ -440,15 +429,14 @@ mod test { pin_uv_auth_param, pin_uv_auth_protocol: Some(PinUvAuthProtocol::V1), }; - let config_response = process_config(&mut persistent_store, &mut client_pin, config_params); + let config_response = process_config(&mut env, &mut client_pin, config_params); assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig)); - assert_eq!(persistent_store.has_force_pin_change(), Ok(true)); + assert_eq!(storage::has_force_pin_change(&mut env), Ok(true)); } #[test] fn test_process_config_vendor_prototype() { 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 = @@ -460,7 +448,7 @@ mod test { pin_uv_auth_param: None, pin_uv_auth_protocol: None, }; - let config_response = process_config(&mut persistent_store, &mut client_pin, config_params); + let config_response = process_config(&mut env, &mut client_pin, config_params); assert_eq!( config_response, Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER) diff --git a/src/ctap/credential_management.rs b/src/ctap/credential_management.rs index f5e78a7..c5952b2 100644 --- a/src/ctap/credential_management.rs +++ b/src/ctap/credential_management.rs @@ -21,8 +21,8 @@ use super::data_formats::{ }; use super::response::{AuthenticatorCredentialManagementResponse, ResponseData}; use super::status_code::Ctap2StatusCode; -use super::storage::PersistentStore; use super::{StatefulCommand, StatefulPermission}; +use crate::ctap::storage; use crate::env::Env; use alloc::collections::BTreeSet; use alloc::string::String; @@ -33,12 +33,10 @@ use crypto::Hash256; use libtock_drivers::timer::ClockValue; /// Generates a set with all existing RP IDs. -fn get_stored_rp_ids( - persistent_store: &PersistentStore, -) -> Result, Ctap2StatusCode> { +fn get_stored_rp_ids(env: &mut impl Env) -> Result, Ctap2StatusCode> { let mut rp_set = BTreeSet::new(); let mut iter_result = Ok(()); - for (_, credential) in persistent_store.iter_credentials(&mut iter_result)? { + for (_, credential) in storage::iter_credentials(env, &mut iter_result)? { rp_set.insert(credential.rp_id); } iter_result?; @@ -109,8 +107,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( + env: &mut impl Env, client_pin: &mut ClientPin, credential_id: &[u8], ) -> Result<(), Ctap2StatusCode> { @@ -118,30 +116,30 @@ fn check_rp_id_permissions( if client_pin.has_no_rp_id_permission().is_ok() { return Ok(()); } - let (_, credential) = persistent_store.find_credential_item(credential_id)?; + let (_, credential) = storage::find_credential_item(env, credential_id)?; client_pin.has_no_or_rp_id_permission(&credential.rp_id) } /// Processes the subcommand getCredsMetadata for CredentialManagement. -fn process_get_creds_metadata( - persistent_store: &PersistentStore, +fn process_get_creds_metadata( + env: &mut impl Env, ) -> Result { Ok(AuthenticatorCredentialManagementResponse { - existing_resident_credentials_count: Some(persistent_store.count_credentials()? as u64), + existing_resident_credentials_count: Some(storage::count_credentials(env)? as u64), max_possible_remaining_resident_credentials_count: Some( - persistent_store.remaining_credentials()? as u64, + storage::remaining_credentials(env)? as u64, ), ..Default::default() }) } /// Processes the subcommand enumerateRPsBegin for CredentialManagement. -fn process_enumerate_rps_begin( - persistent_store: &PersistentStore, +fn process_enumerate_rps_begin( + env: &mut impl Env, stateful_command_permission: &mut StatefulPermission, now: ClockValue, ) -> Result { - let rp_set = get_stored_rp_ids(persistent_store)?; + let rp_set = get_stored_rp_ids(env)?; let total_rps = rp_set.len(); if total_rps > 1 { @@ -156,12 +154,12 @@ 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( + env: &mut impl Env, stateful_command_permission: &mut StatefulPermission, ) -> Result { let rp_id_index = stateful_command_permission.next_enumerate_rp()?; - let rp_set = get_stored_rp_ids(persistent_store)?; + let rp_set = get_stored_rp_ids(env)?; // A BTreeSet is already sorted. let rp_id = rp_set .into_iter() @@ -171,8 +169,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( + env: &mut impl Env, stateful_command_permission: &mut StatefulPermission, client_pin: &mut ClientPin, sub_command_params: CredentialManagementSubCommandParameters, @@ -183,7 +181,7 @@ fn process_enumerate_credentials_begin( .ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?; client_pin.has_no_or_rp_id_hash_permission(&rp_id_hash[..])?; let mut iter_result = Ok(()); - let iter = persistent_store.iter_credentials(&mut iter_result)?; + let iter = storage::iter_credentials(env, &mut iter_result)?; let mut rp_credentials: Vec = iter .filter_map(|(key, credential)| { let cred_rp_id_hash = Sha256::hash(credential.rp_id.as_bytes()); @@ -199,7 +197,7 @@ fn process_enumerate_credentials_begin( let current_key = rp_credentials .pop() .ok_or(Ctap2StatusCode::CTAP2_ERR_NO_CREDENTIALS)?; - let credential = persistent_store.get_credential(current_key)?; + let credential = storage::get_credential(env, current_key)?; if total_credentials > 1 { stateful_command_permission .set_command(now, StatefulCommand::EnumerateCredentials(rp_credentials)); @@ -208,18 +206,18 @@ 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( + env: &mut impl Env, stateful_command_permission: &mut StatefulPermission, ) -> Result { let credential_key = stateful_command_permission.next_enumerate_credential()?; - let credential = persistent_store.get_credential(credential_key)?; + let credential = storage::get_credential(env, credential_key)?; enumerate_credentials_response(credential, None) } /// Processes the subcommand deleteCredential for CredentialManagement. -fn process_delete_credential( - persistent_store: &mut PersistentStore, +fn process_delete_credential( + env: &mut impl Env, client_pin: &mut ClientPin, sub_command_params: CredentialManagementSubCommandParameters, ) -> Result<(), Ctap2StatusCode> { @@ -227,13 +225,13 @@ fn process_delete_credential( .credential_id .ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)? .key_id; - check_rp_id_permissions(persistent_store, client_pin, &credential_id)?; - persistent_store.delete_credential(&credential_id) + check_rp_id_permissions(env, client_pin, &credential_id)?; + storage::delete_credential(env, &credential_id) } /// Processes the subcommand updateUserInformation for CredentialManagement. -fn process_update_user_information( - persistent_store: &mut PersistentStore, +fn process_update_user_information( + env: &mut impl Env, client_pin: &mut ClientPin, sub_command_params: CredentialManagementSubCommandParameters, ) -> Result<(), Ctap2StatusCode> { @@ -244,13 +242,13 @@ fn process_update_user_information( let user = sub_command_params .user .ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?; - check_rp_id_permissions(persistent_store, client_pin, &credential_id)?; - persistent_store.update_credential(&credential_id, user) + check_rp_id_permissions(env, client_pin, &credential_id)?; + storage::update_credential(env, &credential_id, user) } /// Processes the CredentialManagement command and all its subcommands. -pub fn process_credential_management( - persistent_store: &mut PersistentStore, +pub fn process_credential_management( + env: &mut impl Env, stateful_command_permission: &mut StatefulPermission, client_pin: &mut ClientPin, cred_management_params: AuthenticatorCredentialManagementParameters, @@ -306,37 +304,34 @@ pub fn process_credential_management( let response = match sub_command { CredentialManagementSubCommand::GetCredsMetadata => { client_pin.has_no_rp_id_permission()?; - Some(process_get_creds_metadata(persistent_store)?) + Some(process_get_creds_metadata(env)?) } CredentialManagementSubCommand::EnumerateRpsBegin => { client_pin.has_no_rp_id_permission()?; Some(process_enumerate_rps_begin( - persistent_store, + env, stateful_command_permission, now, )?) } CredentialManagementSubCommand::EnumerateRpsGetNextRp => Some( - process_enumerate_rps_get_next_rp(persistent_store, stateful_command_permission)?, + process_enumerate_rps_get_next_rp(env, stateful_command_permission)?, ), CredentialManagementSubCommand::EnumerateCredentialsBegin => { Some(process_enumerate_credentials_begin( - persistent_store, + env, stateful_command_permission, client_pin, sub_command_params.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?, now, )?) } - CredentialManagementSubCommand::EnumerateCredentialsGetNextCredential => { - Some(process_enumerate_credentials_get_next_credential( - persistent_store, - stateful_command_permission, - )?) - } + CredentialManagementSubCommand::EnumerateCredentialsGetNextCredential => Some( + process_enumerate_credentials_get_next_credential(env, stateful_command_permission)?, + ), CredentialManagementSubCommand::DeleteCredential => { process_delete_credential( - persistent_store, + env, client_pin, sub_command_params.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?, )?; @@ -344,7 +339,7 @@ pub fn process_credential_management( } CredentialManagementSubCommand::UpdateUserInformation => { process_update_user_information( - persistent_store, + env, client_pin, sub_command_params.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?, )?; @@ -396,7 +391,7 @@ mod test { 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(); + storage::set_pin(&mut env, &[0u8; 16], 4).unwrap(); let management_data = vec![CredentialManagementSubCommand::GetCredsMetadata as u8]; let pin_uv_auth_param = authenticate_pin_uv_auth_token( &pin_uv_auth_token, @@ -411,7 +406,7 @@ mod test { pin_uv_auth_param: Some(pin_uv_auth_param.clone()), }; let cred_management_response = process_credential_management( - &mut ctap_state.persistent_store, + &mut env, &mut ctap_state.stateful_command_permission, &mut ctap_state.client_pin, cred_management_params, @@ -427,10 +422,7 @@ mod test { _ => panic!("Invalid response type"), }; - ctap_state - .persistent_store - .store_credential(credential_source) - .unwrap(); + storage::store_credential(&mut env, credential_source).unwrap(); let cred_management_params = AuthenticatorCredentialManagementParameters { sub_command: CredentialManagementSubCommand::GetCredsMetadata, @@ -439,7 +431,7 @@ mod test { pin_uv_auth_param: Some(pin_uv_auth_param), }; let cred_management_response = process_credential_management( - &mut ctap_state.persistent_store, + &mut env, &mut ctap_state.stateful_command_permission, &mut ctap_state.client_pin, cred_management_params, @@ -481,16 +473,10 @@ mod test { let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); ctap_state.client_pin = client_pin; - ctap_state - .persistent_store - .store_credential(credential_source1) - .unwrap(); - ctap_state - .persistent_store - .store_credential(credential_source2) - .unwrap(); + storage::store_credential(&mut env, credential_source1).unwrap(); + storage::store_credential(&mut env, credential_source2).unwrap(); - ctap_state.persistent_store.set_pin(&[0u8; 16], 4).unwrap(); + storage::set_pin(&mut env, &[0u8; 16], 4).unwrap(); let pin_uv_auth_param = Some(vec![ 0x1A, 0xA4, 0x96, 0xDA, 0x62, 0x80, 0x28, 0x13, 0xEB, 0x32, 0xB9, 0xF1, 0xD2, 0xA9, 0xD0, 0xD1, @@ -503,7 +489,7 @@ mod test { pin_uv_auth_param, }; let cred_management_response = process_credential_management( - &mut ctap_state.persistent_store, + &mut env, &mut ctap_state.stateful_command_permission, &mut ctap_state.client_pin, cred_management_params, @@ -527,7 +513,7 @@ mod test { pin_uv_auth_param: None, }; let cred_management_response = process_credential_management( - &mut ctap_state.persistent_store, + &mut env, &mut ctap_state.stateful_command_permission, &mut ctap_state.client_pin, cred_management_params, @@ -552,7 +538,7 @@ mod test { pin_uv_auth_param: None, }; let cred_management_response = process_credential_management( - &mut ctap_state.persistent_store, + &mut env, &mut ctap_state.stateful_command_permission, &mut ctap_state.client_pin, cred_management_params, @@ -580,13 +566,10 @@ mod test { for i in 0..NUM_CREDENTIALS { let mut credential = credential_source.clone(); credential.rp_id = i.to_string(); - ctap_state - .persistent_store - .store_credential(credential) - .unwrap(); + storage::store_credential(&mut env, credential).unwrap(); } - ctap_state.persistent_store.set_pin(&[0u8; 16], 4).unwrap(); + storage::set_pin(&mut env, &[0u8; 16], 4).unwrap(); let pin_uv_auth_param = Some(vec![ 0x1A, 0xA4, 0x96, 0xDA, 0x62, 0x80, 0x28, 0x13, 0xEB, 0x32, 0xB9, 0xF1, 0xD2, 0xA9, 0xD0, 0xD1, @@ -604,7 +587,7 @@ mod test { for _ in 0..NUM_CREDENTIALS { let cred_management_response = process_credential_management( - &mut ctap_state.persistent_store, + &mut env, &mut ctap_state.stateful_command_permission, &mut ctap_state.client_pin, cred_management_params, @@ -634,7 +617,7 @@ mod test { } let cred_management_response = process_credential_management( - &mut ctap_state.persistent_store, + &mut env, &mut ctap_state.stateful_command_permission, &mut ctap_state.client_pin, cred_management_params, @@ -663,16 +646,10 @@ mod test { let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); ctap_state.client_pin = client_pin; - ctap_state - .persistent_store - .store_credential(credential_source1) - .unwrap(); - ctap_state - .persistent_store - .store_credential(credential_source2) - .unwrap(); + storage::store_credential(&mut env, credential_source1).unwrap(); + storage::store_credential(&mut env, credential_source2).unwrap(); - ctap_state.persistent_store.set_pin(&[0u8; 16], 4).unwrap(); + storage::set_pin(&mut env, &[0u8; 16], 4).unwrap(); let pin_uv_auth_param = Some(vec![ 0xF8, 0xB0, 0x3C, 0xC1, 0xD5, 0x58, 0x9C, 0xB7, 0x4D, 0x42, 0xA1, 0x64, 0x14, 0x28, 0x2B, 0x68, @@ -692,7 +669,7 @@ mod test { pin_uv_auth_param, }; let cred_management_response = process_credential_management( - &mut ctap_state.persistent_store, + &mut env, &mut ctap_state.stateful_command_permission, &mut ctap_state.client_pin, cred_management_params, @@ -715,7 +692,7 @@ mod test { pin_uv_auth_param: None, }; let cred_management_response = process_credential_management( - &mut ctap_state.persistent_store, + &mut env, &mut ctap_state.stateful_command_permission, &mut ctap_state.client_pin, cred_management_params, @@ -739,7 +716,7 @@ mod test { pin_uv_auth_param: None, }; let cred_management_response = process_credential_management( - &mut ctap_state.persistent_store, + &mut env, &mut ctap_state.stateful_command_permission, &mut ctap_state.client_pin, cred_management_params, @@ -764,12 +741,9 @@ mod test { let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); ctap_state.client_pin = client_pin; - ctap_state - .persistent_store - .store_credential(credential_source) - .unwrap(); + storage::store_credential(&mut env, credential_source).unwrap(); - ctap_state.persistent_store.set_pin(&[0u8; 16], 4).unwrap(); + storage::set_pin(&mut env, &[0u8; 16], 4).unwrap(); let pin_uv_auth_param = Some(vec![ 0xBD, 0xE3, 0xEF, 0x8A, 0x77, 0x01, 0xB1, 0x69, 0x19, 0xE6, 0x62, 0xB9, 0x9B, 0x89, 0x9C, 0x64, @@ -792,7 +766,7 @@ mod test { pin_uv_auth_param: pin_uv_auth_param.clone(), }; let cred_management_response = process_credential_management( - &mut ctap_state.persistent_store, + &mut env, &mut ctap_state.stateful_command_permission, &mut ctap_state.client_pin, cred_management_params, @@ -810,7 +784,7 @@ mod test { pin_uv_auth_param, }; let cred_management_response = process_credential_management( - &mut ctap_state.persistent_store, + &mut env, &mut ctap_state.stateful_command_permission, &mut ctap_state.client_pin, cred_management_params, @@ -835,12 +809,9 @@ mod test { let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); ctap_state.client_pin = client_pin; - ctap_state - .persistent_store - .store_credential(credential_source) - .unwrap(); + storage::store_credential(&mut env, credential_source).unwrap(); - ctap_state.persistent_store.set_pin(&[0u8; 16], 4).unwrap(); + storage::set_pin(&mut env, &[0u8; 16], 4).unwrap(); let pin_uv_auth_param = Some(vec![ 0xA5, 0x55, 0x8F, 0x03, 0xC3, 0xD3, 0x73, 0x1C, 0x07, 0xDA, 0x1F, 0x8C, 0xC7, 0xBD, 0x9D, 0xB7, @@ -869,7 +840,7 @@ mod test { pin_uv_auth_param, }; let cred_management_response = process_credential_management( - &mut ctap_state.persistent_store, + &mut env, &mut ctap_state.stateful_command_permission, &mut ctap_state.client_pin, cred_management_params, @@ -880,11 +851,10 @@ mod test { Ok(ResponseData::AuthenticatorCredentialManagement(None)) ); - let updated_credential = ctap_state - .persistent_store - .find_credential("example.com", &[0x1D; 32], false) - .unwrap() - .unwrap(); + let updated_credential = + storage::find_credential(&mut env, "example.com", &[0x1D; 32], false) + .unwrap() + .unwrap(); assert_eq!(updated_credential.user_handle, vec![0x01]); assert_eq!(&updated_credential.user_name.unwrap(), "new_name"); assert_eq!( @@ -899,7 +869,7 @@ mod test { 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(); + storage::set_pin(&mut env, &[0u8; 16], 4).unwrap(); let cred_management_params = AuthenticatorCredentialManagementParameters { sub_command: CredentialManagementSubCommand::GetCredsMetadata, @@ -908,7 +878,7 @@ mod test { pin_uv_auth_param: Some(vec![0u8; 16]), }; let cred_management_response = process_credential_management( - &mut ctap_state.persistent_store, + &mut env, &mut ctap_state.stateful_command_permission, &mut ctap_state.client_pin, cred_management_params, diff --git a/src/ctap/ctap1.rs b/src/ctap/ctap1.rs index ff39bf8..5b72e69 100644 --- a/src/ctap/ctap1.rs +++ b/src/ctap/ctap1.rs @@ -14,6 +14,7 @@ use super::apdu::{Apdu, ApduStatusCode}; use super::CtapState; +use crate::ctap::storage; use crate::env::Env; use alloc::vec::Vec; use arrayref::array_ref; @@ -178,14 +179,14 @@ impl Ctap1Command { const VENDOR_SPECIFIC_FIRST: u8 = 0x40; const VENDOR_SPECIFIC_LAST: u8 = 0xBF; - pub fn process_command( - env: &mut E, + pub fn process_command( + env: &mut impl Env, message: &[u8], - ctap_state: &mut CtapState, + ctap_state: &mut CtapState, clock_value: ClockValue, ) -> Result, Ctap1StatusCode> { if !ctap_state - .allows_ctap1() + .allows_ctap1(env) .map_err(|_| Ctap1StatusCode::SW_INTERNAL_EXCEPTION)? { return Err(Ctap1StatusCode::SW_COMMAND_NOT_ALLOWED); @@ -243,11 +244,11 @@ impl Ctap1Command { // +------+-------------------+-----------------+------------+--------------------+ // + 0x00 | application (32B) | challenge (32B) | key handle | User pub key (65B) | // +------+-------------------+-----------------+------------+--------------------+ - fn process_register( - env: &mut E, + fn process_register( + env: &mut impl Env, 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(); @@ -259,14 +260,10 @@ impl Ctap1Command { return Err(Ctap1StatusCode::SW_INTERNAL_EXCEPTION); } - let certificate = ctap_state - .persistent_store - .attestation_certificate() + let certificate = storage::attestation_certificate(env) .map_err(|_| Ctap1StatusCode::SW_MEMERR)? .ok_or(Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?; - let private_key = ctap_state - .persistent_store - .attestation_private_key() + let private_key = storage::attestation_private_key(env) .map_err(|_| Ctap1StatusCode::SW_INTERNAL_EXCEPTION)? .ok_or(Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?; @@ -307,16 +304,16 @@ impl Ctap1Command { // +-------------------+---------+--------------+-----------------+ // + application (32B) | UP (1B) | Counter (4B) | challenge (32B) | // +-------------------+---------+--------------+-----------------+ - fn process_authenticate( - env: &mut E, + fn process_authenticate( + env: &mut impl Env, 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) + .decrypt_credential_source(env, key_handle, &application) .map_err(|_| Ctap1StatusCode::SW_WRONG_DATA)?; if let Some(credential_source) = credential_source { if flags == Ctap1Flags::CheckOnly { @@ -326,7 +323,11 @@ impl Ctap1Command { .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) + .generate_auth_data( + env, + &application, + Ctap1Command::USER_PRESENCE_INDICATOR_BYTE, + ) .map_err(|_| Ctap1StatusCode::SW_WRONG_DATA)?; signature_data.extend(&challenge); let signature = credential_source @@ -400,7 +401,7 @@ mod test { 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(); + storage::toggle_always_uv(&mut env).unwrap(); let application = [0x0A; 32]; let message = create_register_message(&application); @@ -428,10 +429,7 @@ mod test { assert_eq!(response, Err(Ctap1StatusCode::SW_INTERNAL_EXCEPTION)); let fake_key = [0x41u8; key_material::ATTESTATION_PRIVATE_KEY_LENGTH]; - assert!(ctap_state - .persistent_store - .set_attestation_private_key(&fake_key) - .is_ok()); + assert!(storage::set_attestation_private_key(&mut env, &fake_key).is_ok()); ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE); ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE); let response = @@ -440,10 +438,7 @@ mod test { assert_eq!(response, Err(Ctap1StatusCode::SW_INTERNAL_EXCEPTION)); let fake_cert = [0x99u8; 100]; // Arbitrary length - assert!(ctap_state - .persistent_store - .set_attestation_certificate(&fake_cert[..]) - .is_ok()); + assert!(storage::set_attestation_certificate(&mut env, &fake_cert[..]).is_ok()); ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE); ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE); let response = @@ -452,7 +447,11 @@ mod test { assert_eq!(response[0], Ctap1Command::LEGACY_BYTE); assert_eq!(response[66], CREDENTIAL_ID_SIZE as u8); assert!(ctap_state - .decrypt_credential_source(response[67..67 + CREDENTIAL_ID_SIZE].to_vec(), &application) + .decrypt_credential_source( + &mut env, + response[67..67 + CREDENTIAL_ID_SIZE].to_vec(), + &application + ) .unwrap() .is_some()); const CERT_START: usize = 67 + CREDENTIAL_ID_SIZE; @@ -677,10 +676,7 @@ mod test { assert_eq!(response[0], 0x01); check_signature_counter( array_ref!(response, 1, 4), - ctap_state - .persistent_store - .global_signature_counter() - .unwrap(), + storage::global_signature_counter(&mut env).unwrap(), ); } @@ -709,10 +705,7 @@ mod test { assert_eq!(response[0], 0x01); check_signature_counter( array_ref!(response, 1, 4), - ctap_state - .persistent_store - .global_signature_counter() - .unwrap(), + storage::global_signature_counter(&mut env).unwrap(), ); } diff --git a/src/ctap/hid/mod.rs b/src/ctap/hid/mod.rs index bf6f799..c79462b 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 E, + env: &mut impl Env, 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 E, + fn process_messages( + env: &mut TestEnv, 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 E, + fn cid_from_init( + env: &mut TestEnv, 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 a6cd23a..3a0eb62 100644 --- a/src/ctap/large_blobs.rs +++ b/src/ctap/large_blobs.rs @@ -17,7 +17,7 @@ use super::command::AuthenticatorLargeBlobsParameters; use super::customization::MAX_MSG_SIZE; use super::response::{AuthenticatorLargeBlobsResponse, ResponseData}; use super::status_code::Ctap2StatusCode; -use super::storage::PersistentStore; +use crate::ctap::storage; use crate::env::Env; use alloc::vec; use alloc::vec::Vec; @@ -45,9 +45,9 @@ impl LargeBlobs { } /// Process the large blob command. - pub fn process_command( + pub fn process_command( &mut self, - persistent_store: &mut PersistentStore, + env: &mut impl Env, client_pin: &mut ClientPin, large_blobs_params: AuthenticatorLargeBlobsParameters, ) -> Result { @@ -66,7 +66,7 @@ impl LargeBlobs { if get > MAX_FRAGMENT_LENGTH || offset.checked_add(get).is_none() { return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_LENGTH); } - let config = persistent_store.get_large_blob_array(offset, get)?; + let config = storage::get_large_blob_array(env, offset, get)?; return Ok(ResponseData::AuthenticatorLargeBlobs(Some( AuthenticatorLargeBlobsResponse { config }, ))); @@ -85,7 +85,7 @@ impl LargeBlobs { if offset != self.expected_next_offset { return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_SEQ); } - if persistent_store.pin_hash()?.is_some() || persistent_store.has_always_uv()? { + if storage::pin_hash(env)?.is_some() || storage::has_always_uv(env)? { let pin_uv_auth_param = pin_uv_auth_param.ok_or(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED)?; let pin_uv_auth_protocol = @@ -122,7 +122,7 @@ impl LargeBlobs { self.buffer = Vec::new(); return Err(Ctap2StatusCode::CTAP2_ERR_INTEGRITY_FAILURE); } - persistent_store.commit_large_blob_array(&self.buffer)?; + storage::commit_large_blob_array(env, &self.buffer)?; self.buffer = Vec::new(); } return Ok(ResponseData::AuthenticatorLargeBlobs(None)); @@ -143,7 +143,6 @@ mod test { #[test] fn test_process_command_get_empty() { 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 = @@ -163,7 +162,7 @@ mod test { pin_uv_auth_protocol: None, }; let large_blobs_response = - large_blobs.process_command(&mut persistent_store, &mut client_pin, large_blobs_params); + large_blobs.process_command(&mut env, &mut client_pin, large_blobs_params); match large_blobs_response.unwrap() { ResponseData::AuthenticatorLargeBlobs(Some(response)) => { assert_eq!(response.config, large_blob); @@ -175,7 +174,6 @@ mod test { #[test] fn test_process_command_commit_and_get() { 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 = @@ -196,7 +194,7 @@ mod test { pin_uv_auth_protocol: None, }; let large_blobs_response = - large_blobs.process_command(&mut persistent_store, &mut client_pin, large_blobs_params); + large_blobs.process_command(&mut env, &mut client_pin, large_blobs_params); assert_eq!( large_blobs_response, Ok(ResponseData::AuthenticatorLargeBlobs(None)) @@ -211,7 +209,7 @@ mod test { pin_uv_auth_protocol: None, }; let large_blobs_response = - large_blobs.process_command(&mut persistent_store, &mut client_pin, large_blobs_params); + large_blobs.process_command(&mut env, &mut client_pin, large_blobs_params); assert_eq!( large_blobs_response, Ok(ResponseData::AuthenticatorLargeBlobs(None)) @@ -226,7 +224,7 @@ mod test { pin_uv_auth_protocol: None, }; let large_blobs_response = - large_blobs.process_command(&mut persistent_store, &mut client_pin, large_blobs_params); + large_blobs.process_command(&mut env, &mut client_pin, large_blobs_params); match large_blobs_response.unwrap() { ResponseData::AuthenticatorLargeBlobs(Some(response)) => { assert_eq!(response.config, large_blob); @@ -238,7 +236,6 @@ mod test { #[test] fn test_process_command_commit_unexpected_offset() { 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 = @@ -259,7 +256,7 @@ mod test { pin_uv_auth_protocol: None, }; let large_blobs_response = - large_blobs.process_command(&mut persistent_store, &mut client_pin, large_blobs_params); + large_blobs.process_command(&mut env, &mut client_pin, large_blobs_params); assert_eq!( large_blobs_response, Ok(ResponseData::AuthenticatorLargeBlobs(None)) @@ -275,7 +272,7 @@ mod test { pin_uv_auth_protocol: None, }; let large_blobs_response = - large_blobs.process_command(&mut persistent_store, &mut client_pin, large_blobs_params); + large_blobs.process_command(&mut env, &mut client_pin, large_blobs_params); assert_eq!( large_blobs_response, Err(Ctap2StatusCode::CTAP1_ERR_INVALID_SEQ), @@ -285,7 +282,6 @@ mod test { #[test] fn test_process_command_commit_unexpected_length() { 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 = @@ -307,7 +303,7 @@ mod test { pin_uv_auth_protocol: None, }; let large_blobs_response = - large_blobs.process_command(&mut persistent_store, &mut client_pin, large_blobs_params); + large_blobs.process_command(&mut env, &mut client_pin, large_blobs_params); assert_eq!( large_blobs_response, Ok(ResponseData::AuthenticatorLargeBlobs(None)) @@ -322,7 +318,7 @@ mod test { pin_uv_auth_protocol: None, }; let large_blobs_response = - large_blobs.process_command(&mut persistent_store, &mut client_pin, large_blobs_params); + large_blobs.process_command(&mut env, &mut client_pin, large_blobs_params); assert_eq!( large_blobs_response, Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER), @@ -332,7 +328,6 @@ mod test { #[test] fn test_process_command_commit_end_offset_overflow() { 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 = @@ -348,7 +343,7 @@ mod test { pin_uv_auth_protocol: None, }; assert_eq!( - large_blobs.process_command(&mut persistent_store, &mut client_pin, large_blobs_params), + large_blobs.process_command(&mut env, &mut client_pin, large_blobs_params), Err(Ctap2StatusCode::CTAP1_ERR_INVALID_LENGTH), ); } @@ -356,7 +351,6 @@ mod test { #[test] fn test_process_command_commit_unexpected_hash() { 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 = @@ -376,7 +370,7 @@ mod test { pin_uv_auth_protocol: None, }; let large_blobs_response = - large_blobs.process_command(&mut persistent_store, &mut client_pin, large_blobs_params); + large_blobs.process_command(&mut env, &mut client_pin, large_blobs_params); assert_eq!( large_blobs_response, Err(Ctap2StatusCode::CTAP2_ERR_INTEGRITY_FAILURE), @@ -385,7 +379,6 @@ mod test { fn test_helper_process_command_commit_with_pin(pin_uv_auth_protocol: PinUvAuthProtocol) { 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 = @@ -397,7 +390,7 @@ mod test { let mut large_blob = vec![0x1B; DATA_LEN]; large_blob.extend_from_slice(&Sha256::hash(&large_blob[..])[..TRUNCATED_HASH_LEN]); - persistent_store.set_pin(&[0u8; 16], 4).unwrap(); + storage::set_pin(&mut env, &[0u8; 16], 4).unwrap(); let mut large_blob_data = vec![0xFF; 32]; // Command constant and offset bytes. large_blob_data.extend(&[0x0C, 0x00, 0x00, 0x00, 0x00, 0x00]); @@ -417,7 +410,7 @@ mod test { pin_uv_auth_protocol: Some(pin_uv_auth_protocol), }; let large_blobs_response = - large_blobs.process_command(&mut persistent_store, &mut client_pin, large_blobs_params); + large_blobs.process_command(&mut env, &mut client_pin, large_blobs_params); assert_eq!( large_blobs_response, Ok(ResponseData::AuthenticatorLargeBlobs(None)) diff --git a/src/ctap/mod.rs b/src/ctap/mod.rs index b21efab..eed20f9 100644 --- a/src/ctap/mod.rs +++ b/src/ctap/mod.rs @@ -60,7 +60,6 @@ use self::response::{ AuthenticatorVendorUpgradeInfoResponse, ResponseData, }; use self::status_code::Ctap2StatusCode; -use self::storage::PersistentStore; use self::timed_permission::TimedPermission; #[cfg(feature = "with_ctap1")] use self::timed_permission::U2fUserPresenceState; @@ -330,24 +329,20 @@ 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 { 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 support is optional. - upgrade_locations: Option, } -impl CtapState { - pub fn new(env: &mut E, now: ClockValue) -> Self { - let persistent_store = PersistentStore::new(env); +impl CtapState { + pub fn new(env: &mut impl Env, now: ClockValue) -> Self { + storage::init(env).ok().unwrap(); let client_pin = ClientPin::new(env.rng()); CtapState { - persistent_store, client_pin, #[cfg(feature = "with_ctap1")] u2f_up_state: U2fUserPresenceState::new( @@ -356,7 +351,6 @@ impl CtapState { ), stateful_command_permission: StatefulPermission::new_reset(now), large_blobs: LargeBlobs::new(), - upgrade_locations: env.upgrade_storage().ok(), } } @@ -370,12 +364,11 @@ impl CtapState { pub fn increment_global_signature_counter( &mut self, - env: &mut E, + env: &mut impl Env, ) -> Result<(), Ctap2StatusCode> { if USE_SIGNATURE_COUNTER { let increment = env.rng().gen_uniform_u32x8()[0] % 8 + 1; - self.persistent_store - .incr_global_signature_counter(increment)?; + storage::incr_global_signature_counter(env, increment)?; } Ok(()) } @@ -384,8 +377,8 @@ impl CtapState { // If alwaysUv is enabled and the authenticator does not support internal UV, // CTAP1 needs to be disabled. #[cfg(feature = "with_ctap1")] - pub fn allows_ctap1(&self) -> Result { - Ok(!self.persistent_store.has_always_uv()?) + pub fn allows_ctap1(&self, env: &mut impl Env) -> Result { + Ok(!storage::has_always_uv(env)?) } // Encrypts the private key and relying party ID hash into a credential ID. Other @@ -394,11 +387,11 @@ impl CtapState { // compatible with U2F. pub fn encrypt_key_handle( &mut self, - env: &mut E, + env: &mut impl Env, private_key: crypto::ecdsa::SecKey, application: &[u8; 32], ) -> Result, Ctap2StatusCode> { - let master_keys = self.persistent_store.master_keys()?; + let master_keys = storage::master_keys(env)?; let aes_enc_key = crypto::aes256::EncryptionKey::new(&master_keys.encryption); let mut plaintext = [0; 64]; private_key.to_bytes(array_mut_ref!(plaintext, 0, 32)); @@ -415,13 +408,14 @@ impl CtapState { // decrypted relying party ID hash. pub fn decrypt_credential_source( &self, + env: &mut impl Env, credential_id: Vec, rp_id_hash: &[u8], ) -> Result, Ctap2StatusCode> { if credential_id.len() != CREDENTIAL_ID_SIZE { return Ok(None); } - let master_keys = self.persistent_store.master_keys()?; + let master_keys = storage::master_keys(env)?; let payload_size = credential_id.len() - 32; if !verify_hmac_256::( &master_keys.hmac, @@ -455,7 +449,7 @@ impl CtapState { pub fn process_command( &mut self, - env: &mut E, + env: &mut impl Env, command_cbor: &[u8], cid: ChannelID, now: ClockValue, @@ -501,17 +495,14 @@ impl CtapState { Command::AuthenticatorGetNextAssertion => { self.process_get_next_assertion(env, now) } - Command::AuthenticatorGetInfo => self.process_get_info(), - Command::AuthenticatorClientPin(params) => self.client_pin.process_command( - env.rng(), - &mut self.persistent_store, - params, - now, - ), + Command::AuthenticatorGetInfo => self.process_get_info(env), + Command::AuthenticatorClientPin(params) => { + self.client_pin.process_command(env, params, now) + } Command::AuthenticatorReset => self.process_reset(env, cid, now), Command::AuthenticatorCredentialManagement(params) => { process_credential_management( - &mut self.persistent_store, + env, &mut self.stateful_command_permission, &mut self.client_pin, params, @@ -519,22 +510,23 @@ impl CtapState { ) } Command::AuthenticatorSelection => self.process_selection(env, cid), - Command::AuthenticatorLargeBlobs(params) => self.large_blobs.process_command( - &mut self.persistent_store, - &mut self.client_pin, - params, - ), + Command::AuthenticatorLargeBlobs(params) => { + self.large_blobs + .process_command(env, &mut self.client_pin, params) + } Command::AuthenticatorConfig(params) => { - process_config(&mut self.persistent_store, &mut self.client_pin, params) + process_config(env, &mut self.client_pin, params) } // Vendor specific commands Command::AuthenticatorVendorConfigure(params) => { self.process_vendor_configure(env, params, cid) } Command::AuthenticatorVendorUpgrade(params) => { - self.process_vendor_upgrade(params) + self.process_vendor_upgrade(env, params) + } + Command::AuthenticatorVendorUpgradeInfo => { + self.process_vendor_upgrade_info(env) } - Command::AuthenticatorVendorUpgradeInfo => self.process_vendor_upgrade_info(), }; #[cfg(feature = "debug_ctap")] writeln!(&mut Console::new(), "Sending response: {:#?}", response).unwrap(); @@ -558,7 +550,7 @@ impl CtapState { fn pin_uv_auth_precheck( &mut self, - env: &mut E, + env: &mut impl Env, pin_uv_auth_param: &Option>, pin_uv_auth_protocol: Option, cid: ChannelID, @@ -567,7 +559,7 @@ impl CtapState { // This case was added in FIDO 2.1. if auth_param.is_empty() { env.user_presence().check(cid)?; - if self.persistent_store.pin_hash()?.is_none() { + if storage::pin_hash(env)?.is_none() { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_NOT_SET); } else { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID); @@ -580,7 +572,7 @@ impl CtapState { fn process_make_credential( &mut self, - env: &mut E, + env: &mut impl Env, make_credential_params: AuthenticatorMakeCredentialParameters, cid: ChannelID, ) -> Result { @@ -607,7 +599,7 @@ impl CtapState { let ep_att = if let Some(enterprise_attestation) = enterprise_attestation { let authenticator_mode = ENTERPRISE_ATTESTATION_MODE.ok_or(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)?; - if !self.persistent_store.enterprise_attestation()? { + if !storage::enterprise_attestation(env)? { return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER); } match ( @@ -631,7 +623,7 @@ impl CtapState { let mut flags = match pin_uv_auth_param { Some(pin_uv_auth_param) => { // This case is not mentioned in CTAP2.1, so we keep 2.0 logic. - if self.persistent_store.pin_hash()?.is_none() { + if storage::pin_hash(env)?.is_none() { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_NOT_SET); } self.client_pin.verify_pin_uv_auth_token( @@ -652,11 +644,11 @@ impl CtapState { if options.uv { return Err(Ctap2StatusCode::CTAP2_ERR_INVALID_OPTION); } - if self.persistent_store.has_always_uv()? { + if storage::has_always_uv(env)? { return Err(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED); } // Corresponds to makeCredUvNotRqd set to true. - if options.rk && self.persistent_store.pin_hash()?.is_some() { + if options.rk && storage::pin_hash(env)?.is_some() { return Err(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED); } 0x00 @@ -667,12 +659,9 @@ impl CtapState { let rp_id_hash = Sha256::hash(rp_id.as_bytes()); if let Some(exclude_list) = exclude_list { for cred_desc in exclude_list { - if self - .persistent_store - .find_credential(&rp_id, &cred_desc.key_id, !has_uv)? - .is_some() + if storage::find_credential(env, &rp_id, &cred_desc.key_id, !has_uv)?.is_some() || self - .decrypt_credential_source(cred_desc.key_id, &rp_id_hash)? + .decrypt_credential_source(env, cred_desc.key_id, &rp_id_hash)? .is_some() { // Perform this check, so bad actors can't brute force exclude_list @@ -692,11 +681,8 @@ impl CtapState { { cred_protect_policy = DEFAULT_CRED_PROTECT; } - let min_pin_length = extensions.min_pin_length - && self - .persistent_store - .min_pin_length_rp_ids()? - .contains(&rp_id); + let min_pin_length = + extensions.min_pin_length && storage::min_pin_length_rp_ids(env)?.contains(&rp_id); // None for no input, false for invalid input, true for valid input. let has_cred_blob_output = extensions.cred_blob.is_some(); let cred_blob = extensions @@ -736,7 +722,7 @@ impl CtapState { .user_display_name .map(|s| truncate_to_char_boundary(&s, 64).to_string()), cred_protect_policy, - creation_order: self.persistent_store.new_creation_order()?, + creation_order: storage::new_creation_order(env)?, user_name: user .user_name .map(|s| truncate_to_char_boundary(&s, 64).to_string()), @@ -746,14 +732,14 @@ impl CtapState { cred_blob, large_blob_key: large_blob_key.clone(), }; - self.persistent_store.store_credential(credential_source)?; + storage::store_credential(env, credential_source)?; random_id } else { self.encrypt_key_handle(env, sk.clone(), &rp_id_hash)? }; - let mut auth_data = self.generate_auth_data(&rp_id_hash, flags)?; - auth_data.extend(&self.persistent_store.aaguid()?); + let mut auth_data = self.generate_auth_data(env, &rp_id_hash, flags)?; + auth_data.extend(&storage::aaguid(env)?); // The length is fixed to 0x20 or 0x70 and fits one byte. if credential_id.len() > 0xFF { return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); @@ -768,7 +754,7 @@ impl CtapState { None }; let min_pin_length_output = if min_pin_length { - Some(self.persistent_store.min_pin_length()? as u64) + Some(storage::min_pin_length(env)? as u64) } else { None }; @@ -786,15 +772,11 @@ impl CtapState { signature_data.extend(client_data_hash); let (signature, x5c) = if USE_BATCH_ATTESTATION || ep_att { - let attestation_private_key = self - .persistent_store - .attestation_private_key()? + let attestation_private_key = storage::attestation_private_key(env)? .ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?; let attestation_key = crypto::ecdsa::SecKey::from_bytes(&attestation_private_key).unwrap(); - let attestation_certificate = self - .persistent_store - .attestation_certificate()? + let attestation_certificate = storage::attestation_certificate(env)? .ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?; ( attestation_key.sign_rfc6979::(&signature_data), @@ -825,12 +807,13 @@ impl CtapState { // The computation is deterministic, and private_key expected to be unique. fn generate_cred_random( &mut self, + env: &mut impl Env, private_key: &crypto::ecdsa::SecKey, has_uv: bool, ) -> Result<[u8; 32], Ctap2StatusCode> { let mut private_key_bytes = [0u8; 32]; private_key.to_bytes(&mut private_key_bytes); - let key = self.persistent_store.cred_random_secret(has_uv)?; + let key = storage::cred_random_secret(env, has_uv)?; Ok(hmac_256::(&key, &private_key_bytes)) } @@ -838,7 +821,7 @@ impl CtapState { // and returns the correct Get(Next)Assertion response. fn assertion_response( &mut self, - env: &mut E, + env: &mut impl Env, mut credential: PublicKeyCredentialSource, assertion_input: AssertionInput, number_of_credentials: Option, @@ -853,7 +836,8 @@ impl CtapState { // Process extensions. if extensions.hmac_secret.is_some() || extensions.cred_blob { let encrypted_output = if let Some(hmac_secret_input) = extensions.hmac_secret { - let cred_random = self.generate_cred_random(&credential.private_key, has_uv)?; + let cred_random = + self.generate_cred_random(env, &credential.private_key, has_uv)?; Some(self.client_pin.process_hmac_secret( env.rng(), hmac_secret_input, @@ -921,22 +905,20 @@ impl CtapState { // Returns the first applicable credential from the allow list. fn get_any_credential_from_allow_list( &mut self, + env: &mut impl Env, allow_list: Vec, rp_id: &str, rp_id_hash: &[u8], has_uv: bool, ) -> Result, Ctap2StatusCode> { for allowed_credential in allow_list { - let credential = self.persistent_store.find_credential( - rp_id, - &allowed_credential.key_id, - !has_uv, - )?; + let credential = + storage::find_credential(env, rp_id, &allowed_credential.key_id, !has_uv)?; if credential.is_some() { return Ok(credential); } let credential = - self.decrypt_credential_source(allowed_credential.key_id, rp_id_hash)?; + self.decrypt_credential_source(env, allowed_credential.key_id, rp_id_hash)?; if credential.is_some() { return Ok(credential); } @@ -946,7 +928,7 @@ impl CtapState { fn process_get_assertion( &mut self, - env: &mut E, + env: &mut impl Env, get_assertion_params: AuthenticatorGetAssertionParameters, cid: ChannelID, now: ClockValue, @@ -974,7 +956,7 @@ impl CtapState { let mut flags = match pin_uv_auth_param { Some(pin_uv_auth_param) => { // This case is not mentioned in CTAP2.1, so we keep 2.0 logic. - if self.persistent_store.pin_hash()?.is_none() { + if storage::pin_hash(env)?.is_none() { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_NOT_SET); } self.client_pin.verify_pin_uv_auth_token( @@ -995,7 +977,7 @@ impl CtapState { if options.uv { return Err(Ctap2StatusCode::CTAP2_ERR_INVALID_OPTION); } - if options.up && self.persistent_store.has_always_uv()? { + if options.up && storage::has_always_uv(env)? { return Err(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED); } 0x00 @@ -1011,12 +993,18 @@ impl CtapState { let rp_id_hash = Sha256::hash(rp_id.as_bytes()); let (credential, next_credential_keys) = if let Some(allow_list) = allow_list { ( - self.get_any_credential_from_allow_list(allow_list, &rp_id, &rp_id_hash, has_uv)?, + self.get_any_credential_from_allow_list( + env, + allow_list, + &rp_id, + &rp_id_hash, + has_uv, + )?, vec![], ) } else { let mut iter_result = Ok(()); - let iter = self.persistent_store.iter_credentials(&mut iter_result)?; + let iter = storage::iter_credentials(env, &mut iter_result)?; let mut stored_credentials: Vec<(usize, u64)> = iter .filter_map(|(key, credential)| { if credential.rp_id == rp_id && (has_uv || credential.is_discoverable()) { @@ -1034,7 +1022,7 @@ impl CtapState { .collect(); let credential = stored_credentials .pop() - .map(|key| self.persistent_store.get_credential(key)) + .map(|key| storage::get_credential(env, key)) .transpose()?; (credential, stored_credentials) }; @@ -1051,7 +1039,7 @@ impl CtapState { let assertion_input = AssertionInput { client_data_hash, - auth_data: self.generate_auth_data(&rp_id_hash, flags)?, + auth_data: self.generate_auth_data(env, &rp_id_hash, flags)?, extensions, has_uv, }; @@ -1072,7 +1060,7 @@ impl CtapState { fn process_get_next_assertion( &mut self, - env: &mut E, + env: &mut impl Env, now: ClockValue, ) -> Result { self.stateful_command_permission @@ -1080,12 +1068,12 @@ impl CtapState { let (assertion_input, credential_key) = self .stateful_command_permission .next_assertion_credential()?; - let credential = self.persistent_store.get_credential(credential_key)?; + let credential = storage::get_credential(env, credential_key)?; self.assertion_response(env, credential, assertion_input, None) } - fn process_get_info(&self) -> Result { - let has_always_uv = self.persistent_store.has_always_uv()?; + fn process_get_info(&self, env: &mut impl Env) -> Result { + let has_always_uv = storage::has_always_uv(env)?; #[cfg_attr(not(feature = "with_ctap1"), allow(unused_mut))] let mut versions = vec![ String::from(FIDO2_VERSION_STRING), @@ -1099,10 +1087,7 @@ impl CtapState { } let mut options = vec![]; if ENTERPRISE_ATTESTATION_MODE.is_some() { - options.push(( - String::from("ep"), - self.persistent_store.enterprise_attestation()?, - )); + options.push((String::from("ep"), storage::enterprise_attestation(env)?)); } options.append(&mut vec![ (String::from("rk"), true), @@ -1110,10 +1095,7 @@ impl CtapState { (String::from("alwaysUv"), has_always_uv), (String::from("credMgmt"), true), (String::from("authnrCfg"), true), - ( - String::from("clientPin"), - self.persistent_store.pin_hash()?.is_some(), - ), + (String::from("clientPin"), storage::pin_hash(env)?.is_some()), (String::from("largeBlobs"), true), (String::from("pinUvAuthToken"), true), (String::from("setMinPINLength"), true), @@ -1130,7 +1112,7 @@ impl CtapState { String::from("credBlob"), String::from("largeBlobKey"), ]), - aaguid: self.persistent_store.aaguid()?, + aaguid: storage::aaguid(env)?, options: Some(options), max_msg_size: Some(MAX_MSG_SIZE as u64), // The order implies preference. We favor the new V2. @@ -1143,14 +1125,14 @@ impl CtapState { transports: Some(vec![AuthenticatorTransport::Usb]), algorithms: Some(vec![ES256_CRED_PARAM]), max_serialized_large_blob_array: Some(MAX_LARGE_BLOB_ARRAY_SIZE as u64), - force_pin_change: Some(self.persistent_store.has_force_pin_change()?), - min_pin_length: self.persistent_store.min_pin_length()?, + force_pin_change: Some(storage::has_force_pin_change(env)?), + min_pin_length: storage::min_pin_length(env)?, firmware_version: None, max_cred_blob_length: Some(MAX_CRED_BLOB_LENGTH as u64), max_rp_ids_for_set_min_pin_length: Some(MAX_RP_IDS_LENGTH as u64), certifications: None, remaining_discoverable_credentials: Some( - self.persistent_store.remaining_credentials()? as u64, + storage::remaining_credentials(env)? as u64 ), }, )) @@ -1158,7 +1140,7 @@ impl CtapState { fn process_reset( &mut self, - env: &mut E, + env: &mut impl Env, cid: ChannelID, now: ClockValue, ) -> Result { @@ -1170,7 +1152,7 @@ impl CtapState { } env.user_presence().check(cid)?; - self.persistent_store.reset(env.rng())?; + storage::reset(env)?; self.client_pin.reset(env.rng()); #[cfg(feature = "with_ctap1")] { @@ -1184,7 +1166,7 @@ impl CtapState { fn process_selection( &self, - env: &mut E, + env: &mut impl Env, cid: ChannelID, ) -> Result { env.user_presence().check(cid)?; @@ -1193,7 +1175,7 @@ impl CtapState { fn process_vendor_configure( &mut self, - env: &mut E, + env: &mut impl Env, params: AuthenticatorVendorConfigureParameters, cid: ChannelID, ) -> Result { @@ -1202,8 +1184,8 @@ impl CtapState { } // Sanity checks - let current_priv_key = self.persistent_store.attestation_private_key()?; - let current_cert = self.persistent_store.attestation_certificate()?; + let current_priv_key = storage::attestation_private_key(env)?; + let current_cert = storage::attestation_certificate(env)?; let response = match params.attestation_material { // Only reading values. @@ -1231,12 +1213,10 @@ impl CtapState { } } if current_cert.is_none() { - self.persistent_store - .set_attestation_certificate(&data.certificate)?; + storage::set_attestation_certificate(env, &data.certificate)?; } if current_priv_key.is_none() { - self.persistent_store - .set_attestation_private_key(&data.private_key)?; + storage::set_attestation_private_key(env, &data.private_key)?; } AuthenticatorVendorConfigureResponse { cert_programmed: true, @@ -1264,6 +1244,7 @@ impl CtapState { fn process_vendor_upgrade( &mut self, + env: &mut impl Env, params: AuthenticatorVendorUpgradeParameters, ) -> Result { let AuthenticatorVendorUpgradeParameters { @@ -1272,9 +1253,8 @@ impl CtapState { hash, signature, } = params; - let upgrade_locations = self - .upgrade_locations - .as_mut() + let upgrade_locations = env + .upgrade_storage() .ok_or(Ctap2StatusCode::CTAP1_ERR_INVALID_COMMAND)?; let written_slice = if let Some(address) = address { upgrade_locations @@ -1303,10 +1283,12 @@ impl CtapState { Ok(ResponseData::AuthenticatorVendorUpgrade) } - fn process_vendor_upgrade_info(&self) -> Result { - let upgrade_locations = self - .upgrade_locations - .as_ref() + fn process_vendor_upgrade_info( + &self, + env: &mut impl Env, + ) -> Result { + let upgrade_locations = env + .upgrade_storage() .ok_or(Ctap2StatusCode::CTAP1_ERR_INVALID_COMMAND)?; Ok(ResponseData::AuthenticatorVendorUpgradeInfo( AuthenticatorVendorUpgradeInfoResponse { @@ -1317,6 +1299,7 @@ impl CtapState { pub fn generate_auth_data( &self, + env: &mut impl Env, rp_id_hash: &[u8], flag_byte: u8, ) -> Result, Ctap2StatusCode> { @@ -1328,7 +1311,7 @@ impl CtapState { let mut signature_counter = [0u8; 4]; BigEndian::write_u32( &mut signature_counter, - self.persistent_store.global_signature_counter()?, + storage::global_signature_counter(env)?, ); auth_data.extend(&signature_counter); Ok(auth_data) @@ -1430,7 +1413,7 @@ mod test { String::from("credBlob"), String::from("largeBlobKey"), ], - 0x03 => ctap_state.persistent_store.aaguid().unwrap(), + 0x03 => storage::aaguid(&mut env).unwrap(), 0x04 => cbor_map_options! { "ep" => ENTERPRISE_ATTESTATION_MODE.map(|_| false), "rk" => true, @@ -1452,10 +1435,10 @@ mod test { 0x0A => cbor_array![ES256_CRED_PARAM], 0x0B => MAX_LARGE_BLOB_ARRAY_SIZE as u64, 0x0C => false, - 0x0D => ctap_state.persistent_store.min_pin_length().unwrap() as u64, + 0x0D => storage::min_pin_length(&mut env).unwrap() as u64, 0x0F => MAX_CRED_BLOB_LENGTH as u64, 0x10 => MAX_RP_IDS_LENGTH as u64, - 0x14 => ctap_state.persistent_store.remaining_credentials().unwrap() as u64, + 0x14 => storage::remaining_credentials(&mut env).unwrap() as u64, }; let mut response_cbor = vec![0x00]; @@ -1533,7 +1516,7 @@ mod test { check_make_response( make_credential_response, 0x41, - &ctap_state.persistent_store.aaguid().unwrap(), + &storage::aaguid(&mut env).unwrap(), 0x20, &[], ); @@ -1552,7 +1535,7 @@ mod test { check_make_response( make_credential_response, 0x41, - &ctap_state.persistent_store.aaguid().unwrap(), + &storage::aaguid(&mut env).unwrap(), CREDENTIAL_ID_SIZE as u8, &[], ); @@ -1597,10 +1580,7 @@ mod test { cred_blob: None, large_blob_key: None, }; - assert!(ctap_state - .persistent_store - .store_credential(excluded_credential_source) - .is_ok()); + assert!(storage::store_credential(&mut env, excluded_credential_source).is_ok()); let make_credential_response = ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID); @@ -1623,10 +1603,7 @@ mod test { assert!(make_credential_response.is_ok()); let mut iter_result = Ok(()); - let iter = ctap_state - .persistent_store - .iter_credentials(&mut iter_result) - .unwrap(); + let iter = storage::iter_credentials(&mut env, &mut iter_result).unwrap(); // There is only 1 credential, so last is good enough. let (_, stored_credential) = iter.last().unwrap(); iter_result.unwrap(); @@ -1650,10 +1627,7 @@ mod test { assert!(make_credential_response.is_ok()); let mut iter_result = Ok(()); - let iter = ctap_state - .persistent_store - .iter_credentials(&mut iter_result) - .unwrap(); + let iter = storage::iter_credentials(&mut env, &mut iter_result).unwrap(); // There is only 1 credential, so last is good enough. let (_, stored_credential) = iter.last().unwrap(); iter_result.unwrap(); @@ -1688,7 +1662,7 @@ mod test { check_make_response( make_credential_response, 0xC1, - &ctap_state.persistent_store.aaguid().unwrap(), + &storage::aaguid(&mut env).unwrap(), CREDENTIAL_ID_SIZE as u8, &expected_extension_cbor, ); @@ -1714,7 +1688,7 @@ mod test { check_make_response( make_credential_response, 0xC1, - &ctap_state.persistent_store.aaguid().unwrap(), + &storage::aaguid(&mut env).unwrap(), 0x20, &expected_extension_cbor, ); @@ -1737,16 +1711,14 @@ mod test { check_make_response( make_credential_response, 0x41, - &ctap_state.persistent_store.aaguid().unwrap(), + &storage::aaguid(&mut env).unwrap(), 0x20, &[], ); // Second part: The extension is used. assert_eq!( - ctap_state - .persistent_store - .set_min_pin_length_rp_ids(vec!["example.com".to_string()]), + storage::set_min_pin_length_rp_ids(&mut env, vec!["example.com".to_string()]), Ok(()) ); @@ -1765,7 +1737,7 @@ mod test { check_make_response( make_credential_response, 0xC1, - &ctap_state.persistent_store.aaguid().unwrap(), + &storage::aaguid(&mut env).unwrap(), 0x20, &expected_extension_cbor, ); @@ -1790,16 +1762,13 @@ mod test { check_make_response( make_credential_response, 0xC1, - &ctap_state.persistent_store.aaguid().unwrap(), + &storage::aaguid(&mut env).unwrap(), 0x20, &expected_extension_cbor, ); let mut iter_result = Ok(()); - let iter = ctap_state - .persistent_store - .iter_credentials(&mut iter_result) - .unwrap(); + let iter = storage::iter_credentials(&mut env, &mut iter_result).unwrap(); // There is only 1 credential, so last is good enough. let (_, stored_credential) = iter.last().unwrap(); iter_result.unwrap(); @@ -1825,16 +1794,13 @@ mod test { check_make_response( make_credential_response, 0xC1, - &ctap_state.persistent_store.aaguid().unwrap(), + &storage::aaguid(&mut env).unwrap(), 0x20, &expected_extension_cbor, ); let mut iter_result = Ok(()); - let iter = ctap_state - .persistent_store - .iter_credentials(&mut iter_result) - .unwrap(); + let iter = storage::iter_credentials(&mut env, &mut iter_result).unwrap(); // There is only 1 credential, so last is good enough. let (_, stored_credential) = iter.last().unwrap(); iter_result.unwrap(); @@ -1863,10 +1829,7 @@ mod test { assert_eq!(large_blob_key.len(), 32); let mut iter_result = Ok(()); - let iter = ctap_state - .persistent_store - .iter_credentials(&mut iter_result) - .unwrap(); + let iter = storage::iter_credentials(&mut env, &mut iter_result).unwrap(); // There is only 1 credential, so last is good enough. let (_, stored_credential) = iter.last().unwrap(); iter_result.unwrap(); @@ -1884,7 +1847,7 @@ mod test { 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(); + storage::set_pin(&mut env, &[0x88; 16], 4).unwrap(); let client_data_hash = [0xCD]; let pin_uv_auth_param = authenticate_pin_uv_auth_token( @@ -1905,7 +1868,7 @@ mod test { check_make_response( make_credential_response, 0x45, - &ctap_state.persistent_store.aaguid().unwrap(), + &storage::aaguid(&mut env).unwrap(), 0x20, &[], ); @@ -1932,7 +1895,7 @@ mod test { fn test_non_resident_process_make_credential_with_pin() { 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(); + storage::set_pin(&mut env, &[0x88; 16], 4).unwrap(); let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.options.rk = false; @@ -1942,7 +1905,7 @@ mod test { check_make_response( make_credential_response, 0x41, - &ctap_state.persistent_store.aaguid().unwrap(), + &storage::aaguid(&mut env).unwrap(), 0x70, &[], ); @@ -1952,7 +1915,7 @@ mod test { fn test_resident_process_make_credential_with_pin() { 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(); + storage::set_pin(&mut env, &[0x88; 16], 4).unwrap(); let make_credential_params = create_minimal_make_credential_parameters(); let make_credential_response = @@ -1968,7 +1931,7 @@ mod test { let mut env = TestEnv::new(); let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); - ctap_state.persistent_store.toggle_always_uv().unwrap(); + storage::toggle_always_uv(&mut env).unwrap(); let make_credential_params = create_minimal_make_credential_parameters(); let make_credential_response = ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL_ID); @@ -1977,7 +1940,7 @@ mod test { Err(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED) ); - ctap_state.persistent_store.set_pin(&[0x88; 16], 4).unwrap(); + storage::set_pin(&mut env, &[0x88; 16], 4).unwrap(); let mut make_credential_params = create_minimal_make_credential_parameters(); make_credential_params.pin_uv_auth_param = Some(vec![0xA4; 16]); make_credential_params.pin_uv_auth_protocol = Some(PinUvAuthProtocol::V1); @@ -2114,10 +2077,7 @@ mod test { DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE, ); - let signature_counter = ctap_state - .persistent_store - .global_signature_counter() - .unwrap(); + let signature_counter = storage::global_signature_counter(&mut env).unwrap(); check_assertion_response(get_assertion_response, vec![0x1D], signature_counter, None); } @@ -2192,7 +2152,7 @@ mod test { let credential_id = match make_credential_response.unwrap() { ResponseData::AuthenticatorMakeCredential(make_credential_response) => { let auth_data = make_credential_response.auth_data; - let offset = 37 + ctap_state.persistent_store.aaguid().unwrap().len(); + let offset = 37 + storage::aaguid(&mut env).unwrap().len(); assert_eq!(auth_data[offset], 0x00); assert_eq!(auth_data[offset + 1] as usize, CREDENTIAL_ID_SIZE); auth_data[offset + 2..offset + 2 + CREDENTIAL_ID_SIZE].to_vec() @@ -2210,12 +2170,10 @@ mod test { permissions: None, permissions_rp_id: None, }; - let key_agreement_response = ctap_state.client_pin.process_command( - env.rng(), - &mut ctap_state.persistent_store, - client_pin_params, - DUMMY_CLOCK_VALUE, - ); + let key_agreement_response = + ctap_state + .client_pin + .process_command(&mut env, client_pin_params, DUMMY_CLOCK_VALUE); let get_assertion_params = get_assertion_hmac_secret_params( key_agreement_key, key_agreement_response.unwrap(), @@ -2268,12 +2226,10 @@ mod test { permissions: None, permissions_rp_id: None, }; - let key_agreement_response = ctap_state.client_pin.process_command( - env.rng(), - &mut ctap_state.persistent_store, - client_pin_params, - DUMMY_CLOCK_VALUE, - ); + let key_agreement_response = + ctap_state + .client_pin + .process_command(&mut env, client_pin_params, DUMMY_CLOCK_VALUE); let get_assertion_params = get_assertion_hmac_secret_params( key_agreement_key, key_agreement_response.unwrap(), @@ -2327,10 +2283,7 @@ mod test { cred_blob: None, large_blob_key: None, }; - assert!(ctap_state - .persistent_store - .store_credential(credential) - .is_ok()); + assert!(storage::store_credential(&mut env, credential).is_ok()); let get_assertion_params = AuthenticatorGetAssertionParameters { rp_id: String::from("example.com"), @@ -2373,10 +2326,7 @@ mod test { DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE, ); - let signature_counter = ctap_state - .persistent_store - .global_signature_counter() - .unwrap(); + let signature_counter = storage::global_signature_counter(&mut env).unwrap(); check_assertion_response(get_assertion_response, vec![0x1D], signature_counter, None); let credential = PublicKeyCredentialSource { @@ -2393,10 +2343,7 @@ mod test { cred_blob: None, large_blob_key: None, }; - assert!(ctap_state - .persistent_store - .store_credential(credential) - .is_ok()); + assert!(storage::store_credential(&mut env, credential).is_ok()); let get_assertion_params = AuthenticatorGetAssertionParameters { rp_id: String::from("example.com"), @@ -2443,10 +2390,7 @@ mod test { cred_blob: Some(vec![0xCB]), large_blob_key: None, }; - assert!(ctap_state - .persistent_store - .store_credential(credential) - .is_ok()); + assert!(storage::store_credential(&mut env, credential).is_ok()); let extensions = GetAssertionExtensions { cred_blob: true, @@ -2470,10 +2414,7 @@ mod test { DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE, ); - let signature_counter = ctap_state - .persistent_store - .global_signature_counter() - .unwrap(); + let signature_counter = storage::global_signature_counter(&mut env).unwrap(); let expected_extension_cbor = [ 0xA1, 0x68, 0x63, 0x72, 0x65, 0x64, 0x42, 0x6C, 0x6F, 0x62, 0x41, 0xCB, ]; @@ -2507,10 +2448,7 @@ mod test { cred_blob: None, large_blob_key: Some(vec![0x1C; 32]), }; - assert!(ctap_state - .persistent_store - .store_credential(credential) - .is_ok()); + assert!(storage::store_credential(&mut env, credential).is_ok()); let extensions = GetAssertionExtensions { large_blob_key: Some(true), @@ -2579,7 +2517,7 @@ mod test { ctap_state.client_pin = client_pin; // The PIN length is outside of the test scope and most likely incorrect. - ctap_state.persistent_store.set_pin(&[0u8; 16], 4).unwrap(); + storage::set_pin(&mut env, &[0u8; 16], 4).unwrap(); let client_data_hash = vec![0xCD]; let pin_uv_auth_param = authenticate_pin_uv_auth_token( &pin_uv_auth_token, @@ -2605,10 +2543,7 @@ mod test { DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE, ); - let signature_counter = ctap_state - .persistent_store - .global_signature_counter() - .unwrap(); + let signature_counter = storage::global_signature_counter(&mut env).unwrap(); check_assertion_response_with_user( get_assertion_response, user2, @@ -2695,10 +2630,7 @@ mod test { DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE, ); - let signature_counter = ctap_state - .persistent_store - .global_signature_counter() - .unwrap(); + let signature_counter = storage::global_signature_counter(&mut env).unwrap(); check_assertion_response( get_assertion_response, vec![0x03], @@ -2809,17 +2741,14 @@ mod test { cred_blob: None, large_blob_key: None, }; - assert!(ctap_state - .persistent_store - .store_credential(credential_source) - .is_ok()); - assert!(ctap_state.persistent_store.count_credentials().unwrap() > 0); + assert!(storage::store_credential(&mut env, credential_source).is_ok()); + assert!(storage::count_credentials(&mut env).unwrap() > 0); let reset_reponse = ctap_state.process_command(&mut env, &[0x07], DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE); let expected_response = vec![0x00]; assert_eq!(reset_reponse, expected_response); - assert!(ctap_state.persistent_store.count_credentials().unwrap() == 0); + assert!(storage::count_credentials(&mut env).unwrap() == 0); } #[test] @@ -2890,7 +2819,7 @@ mod test { .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) + .decrypt_credential_source(&mut env, encrypted_id, &rp_id_hash) .unwrap() .unwrap(); @@ -2912,7 +2841,7 @@ mod test { let mut modified_id = encrypted_id.clone(); modified_id[i] ^= 0x01; assert!(ctap_state - .decrypt_credential_source(modified_id, &rp_id_hash) + .decrypt_credential_source(&mut env, modified_id, &rp_id_hash) .unwrap() .is_none()); } @@ -2923,19 +2852,13 @@ mod test { let mut env = TestEnv::new(); let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); - let mut last_counter = ctap_state - .persistent_store - .global_signature_counter() - .unwrap(); + let mut last_counter = storage::global_signature_counter(&mut env).unwrap(); assert!(last_counter > 0); for _ in 0..100 { assert!(ctap_state .increment_global_signature_counter(&mut env) .is_ok()); - let next_counter = ctap_state - .persistent_store - .global_signature_counter() - .unwrap(); + let next_counter = storage::global_signature_counter(&mut env).unwrap(); assert!(next_counter > last_counter); last_counter = next_counter; } @@ -2989,19 +2912,11 @@ mod test { )) ); assert_eq!( - ctap_state - .persistent_store - .attestation_certificate() - .unwrap() - .unwrap(), + storage::attestation_certificate(&mut env).unwrap().unwrap(), dummy_cert ); assert_eq!( - ctap_state - .persistent_store - .attestation_private_key() - .unwrap() - .unwrap(), + storage::attestation_private_key(&mut env).unwrap().unwrap(), dummy_key ); @@ -3028,19 +2943,11 @@ mod test { )) ); assert_eq!( - ctap_state - .persistent_store - .attestation_certificate() - .unwrap() - .unwrap(), + storage::attestation_certificate(&mut env).unwrap().unwrap(), dummy_cert ); assert_eq!( - ctap_state - .persistent_store - .attestation_private_key() - .unwrap() - .unwrap(), + storage::attestation_private_key(&mut env).unwrap().unwrap(), dummy_key ); @@ -3067,11 +2974,10 @@ mod test { #[test] fn test_parse_metadata() { 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)] - let mut upgrade_locations = ctap_state.upgrade_locations.as_mut().unwrap(); + let mut upgrade_locations = env.upgrade_storage().unwrap(); // Partition of 0x40000 bytes and 8 bytes metadata are hashed. let hashed_data = vec![0xFF; 0x40000 + 8]; @@ -3171,7 +3077,7 @@ mod test { let data = vec![0xFF; 0x1000]; let hash = Sha256::hash(&data).to_vec(); - let upgrade_locations = ctap_state.upgrade_locations.as_ref().unwrap(); + let upgrade_locations = env.upgrade_storage().unwrap(); let partition_length = upgrade_locations.partition_length(); let mut signed_over_data = upgrade_locations .read_partition(0, partition_length) @@ -3192,66 +3098,84 @@ mod test { }; // Write to partition and metadata. - let response = ctap_state.process_vendor_upgrade(AuthenticatorVendorUpgradeParameters { - address: Some(0x20000), - data: data.clone(), - hash: hash.clone(), - signature: None, - }); + let response = ctap_state.process_vendor_upgrade( + &mut env, + AuthenticatorVendorUpgradeParameters { + address: Some(0x20000), + data: data.clone(), + hash: hash.clone(), + signature: None, + }, + ); assert_eq!(response, Ok(ResponseData::AuthenticatorVendorUpgrade)); // We can't inject a public key for our known private key, so the last upgrade step fails. // verify_signature is separately tested for that reason. - let response = ctap_state.process_vendor_upgrade(AuthenticatorVendorUpgradeParameters { - address: None, - data: metadata.clone(), - hash: metadata_hash.clone(), - signature: Some(cose_signature.clone()), - }); + let response = ctap_state.process_vendor_upgrade( + &mut env, + AuthenticatorVendorUpgradeParameters { + address: None, + data: metadata.clone(), + hash: metadata_hash.clone(), + signature: Some(cose_signature.clone()), + }, + ); assert_eq!(response, Err(Ctap2StatusCode::CTAP2_ERR_INTEGRITY_FAILURE)); // Write metadata of a wrong size. - let response = ctap_state.process_vendor_upgrade(AuthenticatorVendorUpgradeParameters { - address: None, - data: metadata[..METADATA_LEN - 1].to_vec(), - hash: metadata_hash, - signature: Some(cose_signature), - }); + let response = ctap_state.process_vendor_upgrade( + &mut env, + AuthenticatorVendorUpgradeParameters { + address: None, + data: metadata[..METADATA_LEN - 1].to_vec(), + hash: metadata_hash, + signature: Some(cose_signature), + }, + ); assert_eq!(response, Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)); // Write outside of the partition. - let response = ctap_state.process_vendor_upgrade(AuthenticatorVendorUpgradeParameters { - address: Some(0x40000), - data: data.clone(), - hash, - signature: None, - }); + let response = ctap_state.process_vendor_upgrade( + &mut env, + AuthenticatorVendorUpgradeParameters { + address: Some(0x40000), + data: data.clone(), + hash, + signature: None, + }, + ); assert_eq!(response, Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)); // Write a bad hash. - let response = ctap_state.process_vendor_upgrade(AuthenticatorVendorUpgradeParameters { - address: Some(0x20000), - data, - hash: [0xEE; 32].to_vec(), - signature: None, - }); + let response = ctap_state.process_vendor_upgrade( + &mut env, + AuthenticatorVendorUpgradeParameters { + address: Some(0x20000), + data, + hash: [0xEE; 32].to_vec(), + signature: None, + }, + ); assert_eq!(response, Err(Ctap2StatusCode::CTAP2_ERR_INTEGRITY_FAILURE)); } #[test] fn test_vendor_upgrade_no_second_partition() { let mut env = TestEnv::new(); + env.disable_upgrade_storage(); let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); - ctap_state.upgrade_locations = None; let data = vec![0xFF; 0x1000]; let hash = Sha256::hash(&data).to_vec(); - let response = ctap_state.process_vendor_upgrade(AuthenticatorVendorUpgradeParameters { - address: Some(0), - data, - hash, - signature: None, - }); + let response = ctap_state.process_vendor_upgrade( + &mut env, + AuthenticatorVendorUpgradeParameters { + address: Some(0), + data, + hash, + signature: None, + }, + ); assert_eq!(response, Err(Ctap2StatusCode::CTAP1_ERR_INVALID_COMMAND)); } @@ -3259,13 +3183,9 @@ mod test { fn test_vendor_upgrade_info() { let mut env = TestEnv::new(); let ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE); - let partition_address = ctap_state - .upgrade_locations - .as_ref() - .unwrap() - .partition_address(); + let partition_address = env.upgrade_storage().unwrap().partition_address(); - let upgrade_info_reponse = ctap_state.process_vendor_upgrade_info(); + let upgrade_info_reponse = ctap_state.process_vendor_upgrade_info(&mut env); assert_eq!( upgrade_info_reponse, Ok(ResponseData::AuthenticatorVendorUpgradeInfo( diff --git a/src/ctap/storage.rs b/src/ctap/storage.rs index 08e88ff..371e38f 100644 --- a/src/ctap/storage.rs +++ b/src/ctap/storage.rs @@ -55,582 +55,566 @@ struct PinProperties { code_point_length: u8, } -/// CTAP persistent storage. -pub struct PersistentStore { - store: persistent_store::Store, +/// Initializes the store by creating missing objects. +pub fn init(env: &mut impl Env) -> Result<(), Ctap2StatusCode> { + // Generate and store the master keys if they are missing. + if env.store().find_handle(key::MASTER_KEYS)?.is_none() { + let master_encryption_key = env.rng().gen_uniform_u8x32(); + let master_hmac_key = env.rng().gen_uniform_u8x32(); + let mut master_keys = Vec::with_capacity(64); + master_keys.extend_from_slice(&master_encryption_key); + master_keys.extend_from_slice(&master_hmac_key); + env.store().insert(key::MASTER_KEYS, &master_keys)?; + } + + // Generate and store the CredRandom secrets if they are missing. + if env.store().find_handle(key::CRED_RANDOM_SECRET)?.is_none() { + let cred_random_with_uv = env.rng().gen_uniform_u8x32(); + let cred_random_without_uv = env.rng().gen_uniform_u8x32(); + let mut cred_random = Vec::with_capacity(64); + cred_random.extend_from_slice(&cred_random_without_uv); + cred_random.extend_from_slice(&cred_random_with_uv); + env.store().insert(key::CRED_RANDOM_SECRET, &cred_random)?; + } + + if env.store().find_handle(key::AAGUID)?.is_none() { + set_aaguid(env, key_material::AAGUID)?; + } + Ok(()) } -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(env: &mut E) -> Self { - let storage = env.storage().ok().unwrap(); - let mut store = PersistentStore { - store: persistent_store::Store::new(storage).ok().unwrap(), - }; - store.init(env.rng()).ok().unwrap(); - store +/// Returns the credential at the given key. +/// +/// # Errors +/// +/// Returns `CTAP2_ERR_VENDOR_INTERNAL_ERROR` if the key does not hold a valid credential. +pub fn get_credential( + env: &mut impl Env, + key: usize, +) -> Result { + let min_key = key::CREDENTIALS.start; + if key < min_key || key >= min_key + MAX_SUPPORTED_RESIDENT_KEYS { + return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); } + let credential_entry = env + .store() + .find(key)? + .ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?; + deserialize_credential(&credential_entry) + .ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR) +} - /// Initializes the store by creating missing objects. - fn init(&mut self, rng: &mut impl Rng256) -> Result<(), Ctap2StatusCode> { - // Generate and store the master keys if they are missing. - if self.store.find_handle(key::MASTER_KEYS)?.is_none() { - let master_encryption_key = rng.gen_uniform_u8x32(); - let master_hmac_key = rng.gen_uniform_u8x32(); - let mut master_keys = Vec::with_capacity(64); - master_keys.extend_from_slice(&master_encryption_key); - master_keys.extend_from_slice(&master_hmac_key); - self.store.insert(key::MASTER_KEYS, &master_keys)?; - } - - // Generate and store the CredRandom secrets if they are missing. - if self.store.find_handle(key::CRED_RANDOM_SECRET)?.is_none() { - let cred_random_with_uv = rng.gen_uniform_u8x32(); - let cred_random_without_uv = rng.gen_uniform_u8x32(); - let mut cred_random = Vec::with_capacity(64); - cred_random.extend_from_slice(&cred_random_without_uv); - cred_random.extend_from_slice(&cred_random_with_uv); - self.store.insert(key::CRED_RANDOM_SECRET, &cred_random)?; - } - - if self.store.find_handle(key::AAGUID)?.is_none() { - self.set_aaguid(key_material::AAGUID)?; - } - Ok(()) +/// Finds the key and value for a given credential ID. +/// +/// # Errors +/// +/// Returns `CTAP2_ERR_NO_CREDENTIALS` if the credential is not found. +pub fn find_credential_item( + env: &mut impl Env, + credential_id: &[u8], +) -> Result<(usize, PublicKeyCredentialSource), Ctap2StatusCode> { + let mut iter_result = Ok(()); + let iter = iter_credentials(env, &mut iter_result)?; + let mut credentials: Vec<(usize, PublicKeyCredentialSource)> = iter + .filter(|(_, credential)| credential.credential_id == credential_id) + .collect(); + iter_result?; + if credentials.len() > 1 { + return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); } + credentials + .pop() + .ok_or(Ctap2StatusCode::CTAP2_ERR_NO_CREDENTIALS) +} - /// Returns the credential at the given key. - /// - /// # Errors - /// - /// Returns `CTAP2_ERR_VENDOR_INTERNAL_ERROR` if the key does not hold a valid credential. - pub fn get_credential(&self, key: usize) -> Result { - let min_key = key::CREDENTIALS.start; - if key < min_key || key >= min_key + MAX_SUPPORTED_RESIDENT_KEYS { +/// Returns the first matching credential. +/// +/// Returns `None` if no credentials are matched or if `check_cred_protect` is set and the first +/// matched credential requires user verification. +pub fn find_credential( + env: &mut impl Env, + rp_id: &str, + credential_id: &[u8], + check_cred_protect: bool, +) -> Result, Ctap2StatusCode> { + let credential = match find_credential_item(env, credential_id) { + Err(Ctap2StatusCode::CTAP2_ERR_NO_CREDENTIALS) => return Ok(None), + Err(e) => return Err(e), + Ok((_key, credential)) => credential, + }; + let is_protected = credential.cred_protect_policy + == Some(CredentialProtectionPolicy::UserVerificationRequired); + if credential.rp_id != rp_id || (check_cred_protect && is_protected) { + return Ok(None); + } + Ok(Some(credential)) +} + +/// Stores or updates a credential. +/// +/// If a credential with the same RP id and user handle already exists, it is replaced. +pub fn store_credential( + env: &mut impl Env, + new_credential: PublicKeyCredentialSource, +) -> Result<(), Ctap2StatusCode> { + // Holds the key of the existing credential if this is an update. + let mut old_key = None; + let min_key = key::CREDENTIALS.start; + // Holds whether a key is used (indices are shifted by min_key). + let mut keys = vec![false; MAX_SUPPORTED_RESIDENT_KEYS]; + let mut iter_result = Ok(()); + let iter = iter_credentials(env, &mut iter_result)?; + for (key, credential) in iter { + if key < min_key || key - min_key >= MAX_SUPPORTED_RESIDENT_KEYS || keys[key - min_key] { return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); } - let credential_entry = self - .store - .find(key)? - .ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?; - deserialize_credential(&credential_entry) - .ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR) - } - - /// Finds the key and value for a given credential ID. - /// - /// # Errors - /// - /// Returns `CTAP2_ERR_NO_CREDENTIALS` if the credential is not found. - pub fn find_credential_item( - &self, - credential_id: &[u8], - ) -> Result<(usize, PublicKeyCredentialSource), Ctap2StatusCode> { - let mut iter_result = Ok(()); - let iter = self.iter_credentials(&mut iter_result)?; - let mut credentials: Vec<(usize, PublicKeyCredentialSource)> = iter - .filter(|(_, credential)| credential.credential_id == credential_id) - .collect(); - iter_result?; - if credentials.len() > 1 { - return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); - } - credentials - .pop() - .ok_or(Ctap2StatusCode::CTAP2_ERR_NO_CREDENTIALS) - } - - /// Returns the first matching credential. - /// - /// Returns `None` if no credentials are matched or if `check_cred_protect` is set and the first - /// matched credential requires user verification. - pub fn find_credential( - &self, - rp_id: &str, - credential_id: &[u8], - check_cred_protect: bool, - ) -> Result, Ctap2StatusCode> { - let credential = match self.find_credential_item(credential_id) { - Err(Ctap2StatusCode::CTAP2_ERR_NO_CREDENTIALS) => return Ok(None), - Err(e) => return Err(e), - Ok((_key, credential)) => credential, - }; - let is_protected = credential.cred_protect_policy - == Some(CredentialProtectionPolicy::UserVerificationRequired); - if credential.rp_id != rp_id || (check_cred_protect && is_protected) { - return Ok(None); - } - Ok(Some(credential)) - } - - /// Stores or updates a credential. - /// - /// If a credential with the same RP id and user handle already exists, it is replaced. - pub fn store_credential( - &mut self, - new_credential: PublicKeyCredentialSource, - ) -> Result<(), Ctap2StatusCode> { - // Holds the key of the existing credential if this is an update. - let mut old_key = None; - let min_key = key::CREDENTIALS.start; - // Holds whether a key is used (indices are shifted by min_key). - let mut keys = vec![false; MAX_SUPPORTED_RESIDENT_KEYS]; - let mut iter_result = Ok(()); - let iter = self.iter_credentials(&mut iter_result)?; - for (key, credential) in iter { - if key < min_key || key - min_key >= MAX_SUPPORTED_RESIDENT_KEYS || keys[key - min_key] - { + keys[key - min_key] = true; + if credential.rp_id == new_credential.rp_id + && credential.user_handle == new_credential.user_handle + { + if old_key.is_some() { return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); } - keys[key - min_key] = true; - if credential.rp_id == new_credential.rp_id - && credential.user_handle == new_credential.user_handle - { - if old_key.is_some() { - return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); - } - old_key = Some(key); - } - } - iter_result?; - if old_key.is_none() && keys.iter().filter(|&&x| x).count() >= MAX_SUPPORTED_RESIDENT_KEYS { - return Err(Ctap2StatusCode::CTAP2_ERR_KEY_STORE_FULL); - } - let key = match old_key { - // This is a new credential being added, we need to allocate a free key. We choose the - // first available key. - None => key::CREDENTIALS - .take(MAX_SUPPORTED_RESIDENT_KEYS) - .find(|key| !keys[key - min_key]) - .ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?, - // This is an existing credential being updated, we reuse its key. - Some(x) => x, - }; - let value = serialize_credential(new_credential)?; - self.store.insert(key, &value)?; - Ok(()) - } - - /// Deletes a credential. - /// - /// # Errors - /// - /// Returns `CTAP2_ERR_NO_CREDENTIALS` if the credential is not found. - pub fn delete_credential(&mut self, credential_id: &[u8]) -> Result<(), Ctap2StatusCode> { - let (key, _) = self.find_credential_item(credential_id)?; - Ok(self.store.remove(key)?) - } - - /// Updates a credential's user information. - /// - /// # Errors - /// - /// Returns `CTAP2_ERR_NO_CREDENTIALS` if the credential is not found. - pub fn update_credential( - &mut self, - credential_id: &[u8], - user: PublicKeyCredentialUserEntity, - ) -> Result<(), Ctap2StatusCode> { - let (key, mut credential) = self.find_credential_item(credential_id)?; - credential.user_name = user.user_name; - credential.user_display_name = user.user_display_name; - credential.user_icon = user.user_icon; - let value = serialize_credential(credential)?; - Ok(self.store.insert(key, &value)?) - } - - /// Returns the number of credentials. - pub fn count_credentials(&self) -> Result { - let mut count = 0; - for handle in self.store.iter()? { - count += key::CREDENTIALS.contains(&handle?.get_key()) as usize; - } - Ok(count) - } - - /// Returns the estimated number of credentials that can still be stored. - pub fn remaining_credentials(&self) -> Result { - MAX_SUPPORTED_RESIDENT_KEYS - .checked_sub(self.count_credentials()?) - .ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR) - } - - /// Iterates through the credentials. - /// - /// If an error is encountered during iteration, it is written to `result`. - pub fn iter_credentials<'a>( - &'a self, - result: &'a mut Result<(), Ctap2StatusCode>, - ) -> Result, Ctap2StatusCode> { - IterCredentials::new(&self.store, result) - } - - /// Returns the next creation order. - pub fn new_creation_order(&self) -> Result { - let mut iter_result = Ok(()); - let iter = self.iter_credentials(&mut iter_result)?; - let max = iter.map(|(_, credential)| credential.creation_order).max(); - iter_result?; - Ok(max.unwrap_or(0).wrapping_add(1)) - } - - /// Returns the global signature counter. - pub fn global_signature_counter(&self) -> Result { - match self.store.find(key::GLOBAL_SIGNATURE_COUNTER)? { - None => Ok(INITIAL_SIGNATURE_COUNTER), - Some(value) if value.len() == 4 => Ok(u32::from_ne_bytes(*array_ref!(&value, 0, 4))), - Some(_) => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR), + old_key = Some(key); } } - - /// Increments the global signature counter. - pub fn incr_global_signature_counter(&mut self, increment: u32) -> Result<(), Ctap2StatusCode> { - let old_value = self.global_signature_counter()?; - // In hopes that servers handle the wrapping gracefully. - let new_value = old_value.wrapping_add(increment); - self.store - .insert(key::GLOBAL_SIGNATURE_COUNTER, &new_value.to_ne_bytes())?; - Ok(()) + iter_result?; + if old_key.is_none() && keys.iter().filter(|&&x| x).count() >= MAX_SUPPORTED_RESIDENT_KEYS { + return Err(Ctap2StatusCode::CTAP2_ERR_KEY_STORE_FULL); } + let key = match old_key { + // This is a new credential being added, we need to allocate a free key. We choose the + // first available key. + None => key::CREDENTIALS + .take(MAX_SUPPORTED_RESIDENT_KEYS) + .find(|key| !keys[key - min_key]) + .ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?, + // This is an existing credential being updated, we reuse its key. + Some(x) => x, + }; + let value = serialize_credential(new_credential)?; + env.store().insert(key, &value)?; + Ok(()) +} - /// Returns the master keys. - pub fn master_keys(&self) -> Result { - let master_keys = self - .store - .find(key::MASTER_KEYS)? - .ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?; - if master_keys.len() != 64 { - return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); - } - Ok(MasterKeys { - encryption: *array_ref![master_keys, 0, 32], - hmac: *array_ref![master_keys, 32, 32], - }) +/// Deletes a credential. +/// +/// # Errors +/// +/// Returns `CTAP2_ERR_NO_CREDENTIALS` if the credential is not found. +pub fn delete_credential(env: &mut impl Env, credential_id: &[u8]) -> Result<(), Ctap2StatusCode> { + let (key, _) = find_credential_item(env, credential_id)?; + Ok(env.store().remove(key)?) +} + +/// Updates a credential's user information. +/// +/// # Errors +/// +/// Returns `CTAP2_ERR_NO_CREDENTIALS` if the credential is not found. +pub fn update_credential( + env: &mut impl Env, + credential_id: &[u8], + user: PublicKeyCredentialUserEntity, +) -> Result<(), Ctap2StatusCode> { + let (key, mut credential) = find_credential_item(env, credential_id)?; + credential.user_name = user.user_name; + credential.user_display_name = user.user_display_name; + credential.user_icon = user.user_icon; + let value = serialize_credential(credential)?; + Ok(env.store().insert(key, &value)?) +} + +/// Returns the number of credentials. +pub fn count_credentials(env: &mut impl Env) -> Result { + let mut count = 0; + for handle in env.store().iter()? { + count += key::CREDENTIALS.contains(&handle?.get_key()) as usize; } + Ok(count) +} - /// Returns the CredRandom secret. - pub fn cred_random_secret(&self, has_uv: bool) -> Result<[u8; 32], Ctap2StatusCode> { - let cred_random_secret = self - .store - .find(key::CRED_RANDOM_SECRET)? - .ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?; - if cred_random_secret.len() != 64 { - return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); - } - let offset = if has_uv { 32 } else { 0 }; - Ok(*array_ref![cred_random_secret, offset, 32]) +/// Returns the estimated number of credentials that can still be stored. +pub fn remaining_credentials(env: &mut impl Env) -> Result { + MAX_SUPPORTED_RESIDENT_KEYS + .checked_sub(count_credentials(env)?) + .ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR) +} + +/// Iterates through the credentials. +/// +/// If an error is encountered during iteration, it is written to `result`. +pub fn iter_credentials<'a, E: Env>( + env: &'a mut E, + result: &'a mut Result<(), Ctap2StatusCode>, +) -> Result, Ctap2StatusCode> { + IterCredentials::new(env.store(), result) +} + +/// Returns the next creation order. +pub fn new_creation_order(env: &mut impl Env) -> Result { + let mut iter_result = Ok(()); + let iter = iter_credentials(env, &mut iter_result)?; + let max = iter.map(|(_, credential)| credential.creation_order).max(); + iter_result?; + Ok(max.unwrap_or(0).wrapping_add(1)) +} + +/// Returns the global signature counter. +pub fn global_signature_counter(env: &mut impl Env) -> Result { + match env.store().find(key::GLOBAL_SIGNATURE_COUNTER)? { + None => Ok(INITIAL_SIGNATURE_COUNTER), + Some(value) if value.len() == 4 => Ok(u32::from_ne_bytes(*array_ref!(&value, 0, 4))), + Some(_) => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR), } +} - /// Reads the PIN properties and wraps them into PinProperties. - fn pin_properties(&self) -> Result, Ctap2StatusCode> { - let pin_properties = match self.store.find(key::PIN_PROPERTIES)? { - None => return Ok(None), - Some(pin_properties) => pin_properties, - }; - const PROPERTIES_LENGTH: usize = PIN_AUTH_LENGTH + 1; - match pin_properties.len() { - PROPERTIES_LENGTH => Ok(Some(PinProperties { - hash: *array_ref![pin_properties, 1, PIN_AUTH_LENGTH], - code_point_length: pin_properties[0], - })), - _ => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR), +/// Increments the global signature counter. +pub fn incr_global_signature_counter( + env: &mut impl Env, + increment: u32, +) -> Result<(), Ctap2StatusCode> { + let old_value = global_signature_counter(env)?; + // In hopes that servers handle the wrapping gracefully. + let new_value = old_value.wrapping_add(increment); + env.store() + .insert(key::GLOBAL_SIGNATURE_COUNTER, &new_value.to_ne_bytes())?; + Ok(()) +} + +/// Returns the master keys. +pub fn master_keys(env: &mut impl Env) -> Result { + let master_keys = env + .store() + .find(key::MASTER_KEYS)? + .ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?; + if master_keys.len() != 64 { + return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); + } + Ok(MasterKeys { + encryption: *array_ref![master_keys, 0, 32], + hmac: *array_ref![master_keys, 32, 32], + }) +} + +/// Returns the CredRandom secret. +pub fn cred_random_secret(env: &mut impl Env, has_uv: bool) -> Result<[u8; 32], Ctap2StatusCode> { + let cred_random_secret = env + .store() + .find(key::CRED_RANDOM_SECRET)? + .ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?; + if cred_random_secret.len() != 64 { + return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); + } + let offset = if has_uv { 32 } else { 0 }; + Ok(*array_ref![cred_random_secret, offset, 32]) +} + +/// Reads the PIN properties and wraps them into PinProperties. +fn pin_properties(env: &mut impl Env) -> Result, Ctap2StatusCode> { + let pin_properties = match env.store().find(key::PIN_PROPERTIES)? { + None => return Ok(None), + Some(pin_properties) => pin_properties, + }; + const PROPERTIES_LENGTH: usize = PIN_AUTH_LENGTH + 1; + match pin_properties.len() { + PROPERTIES_LENGTH => Ok(Some(PinProperties { + hash: *array_ref![pin_properties, 1, PIN_AUTH_LENGTH], + code_point_length: pin_properties[0], + })), + _ => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR), + } +} + +/// Returns the PIN hash if defined. +pub fn pin_hash(env: &mut impl Env) -> Result, Ctap2StatusCode> { + Ok(pin_properties(env)?.map(|p| p.hash)) +} + +/// Returns the length of the currently set PIN if defined. +pub fn pin_code_point_length(env: &mut impl Env) -> Result, Ctap2StatusCode> { + Ok(pin_properties(env)?.map(|p| p.code_point_length)) +} + +/// Sets the PIN hash and length. +/// +/// If it was already defined, it is updated. +pub fn set_pin( + env: &mut impl Env, + pin_hash: &[u8; PIN_AUTH_LENGTH], + pin_code_point_length: u8, +) -> Result<(), Ctap2StatusCode> { + let mut pin_properties = [0; 1 + PIN_AUTH_LENGTH]; + pin_properties[0] = pin_code_point_length; + pin_properties[1..].clone_from_slice(pin_hash); + Ok(env.store().transaction(&[ + StoreUpdate::Insert { + key: key::PIN_PROPERTIES, + value: &pin_properties[..], + }, + StoreUpdate::Remove { + key: key::FORCE_PIN_CHANGE, + }, + ])?) +} + +/// Returns the number of remaining PIN retries. +pub fn pin_retries(env: &mut impl Env) -> Result { + match env.store().find(key::PIN_RETRIES)? { + None => Ok(MAX_PIN_RETRIES), + Some(value) if value.len() == 1 => Ok(value[0]), + _ => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR), + } +} + +/// Decrements the number of remaining PIN retries. +pub fn decr_pin_retries(env: &mut impl Env) -> Result<(), Ctap2StatusCode> { + let old_value = pin_retries(env)?; + let new_value = old_value.saturating_sub(1); + if new_value != old_value { + env.store().insert(key::PIN_RETRIES, &[new_value])?; + } + Ok(()) +} + +/// Resets the number of remaining PIN retries. +pub fn reset_pin_retries(env: &mut impl Env) -> Result<(), Ctap2StatusCode> { + Ok(env.store().remove(key::PIN_RETRIES)?) +} + +/// Returns the minimum PIN length. +pub fn min_pin_length(env: &mut impl Env) -> Result { + match env.store().find(key::MIN_PIN_LENGTH)? { + None => Ok(DEFAULT_MIN_PIN_LENGTH), + Some(value) if value.len() == 1 => Ok(value[0]), + _ => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR), + } +} + +/// Sets the minimum PIN length. +pub fn set_min_pin_length(env: &mut impl Env, min_pin_length: u8) -> Result<(), Ctap2StatusCode> { + Ok(env.store().insert(key::MIN_PIN_LENGTH, &[min_pin_length])?) +} + +/// Returns the list of RP IDs that are used to check if reading the minimum PIN length is +/// allowed. +pub fn min_pin_length_rp_ids(env: &mut impl Env) -> Result, Ctap2StatusCode> { + let rp_ids = env.store().find(key::MIN_PIN_LENGTH_RP_IDS)?.map_or_else( + || { + Some( + DEFAULT_MIN_PIN_LENGTH_RP_IDS + .iter() + .map(|&s| String::from(s)) + .collect(), + ) + }, + |value| deserialize_min_pin_length_rp_ids(&value), + ); + debug_assert!(rp_ids.is_some()); + Ok(rp_ids.unwrap_or_default()) +} + +/// Sets the list of RP IDs that are used to check if reading the minimum PIN length is allowed. +pub fn set_min_pin_length_rp_ids( + env: &mut impl Env, + min_pin_length_rp_ids: Vec, +) -> Result<(), Ctap2StatusCode> { + let mut min_pin_length_rp_ids = min_pin_length_rp_ids; + for &rp_id in DEFAULT_MIN_PIN_LENGTH_RP_IDS.iter() { + let rp_id = String::from(rp_id); + if !min_pin_length_rp_ids.contains(&rp_id) { + min_pin_length_rp_ids.push(rp_id); } } - - /// Returns the PIN hash if defined. - pub fn pin_hash(&self) -> Result, Ctap2StatusCode> { - Ok(self.pin_properties()?.map(|p| p.hash)) + if min_pin_length_rp_ids.len() > MAX_RP_IDS_LENGTH { + return Err(Ctap2StatusCode::CTAP2_ERR_KEY_STORE_FULL); } + Ok(env.store().insert( + key::MIN_PIN_LENGTH_RP_IDS, + &serialize_min_pin_length_rp_ids(min_pin_length_rp_ids)?, + )?) +} - /// Returns the length of the currently set PIN if defined. - pub fn pin_code_point_length(&self) -> Result, Ctap2StatusCode> { - Ok(self.pin_properties()?.map(|p| p.code_point_length)) +/// Reads the byte vector stored as the serialized large blobs array. +/// +/// If too few bytes exist at that offset, return the maximum number +/// available. This includes cases of offset being beyond the stored array. +/// +/// If no large blob is committed to the store, get responds as if an empty +/// CBOR array (0x80) was written, together with the 16 byte prefix of its +/// SHA256, to a total length of 17 byte (which is the shortest legitimate +/// large blob entry possible). +pub fn get_large_blob_array( + env: &mut impl Env, + offset: usize, + byte_count: usize, +) -> Result, Ctap2StatusCode> { + let byte_range = offset..offset + byte_count; + let output = fragment::read_range(env.store(), &key::LARGE_BLOB_SHARDS, byte_range)?; + Ok(output.unwrap_or_else(|| { + const EMPTY_LARGE_BLOB: [u8; 17] = [ + 0x80, 0x76, 0xBE, 0x8B, 0x52, 0x8D, 0x00, 0x75, 0xF7, 0xAA, 0xE9, 0x8D, 0x6F, 0xA5, + 0x7A, 0x6D, 0x3C, + ]; + let last_index = cmp::min(EMPTY_LARGE_BLOB.len(), offset + byte_count); + EMPTY_LARGE_BLOB + .get(offset..last_index) + .unwrap_or_default() + .to_vec() + })) +} + +/// Sets a byte vector as the serialized large blobs array. +pub fn commit_large_blob_array( + env: &mut impl Env, + large_blob_array: &[u8], +) -> Result<(), Ctap2StatusCode> { + // This input should have been caught at caller level. + if large_blob_array.len() > MAX_LARGE_BLOB_ARRAY_SIZE { + return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); } + Ok(fragment::write( + env.store(), + &key::LARGE_BLOB_SHARDS, + large_blob_array, + )?) +} - /// Sets the PIN hash and length. - /// - /// If it was already defined, it is updated. - pub fn set_pin( - &mut self, - pin_hash: &[u8; PIN_AUTH_LENGTH], - pin_code_point_length: u8, - ) -> Result<(), Ctap2StatusCode> { - let mut pin_properties = [0; 1 + PIN_AUTH_LENGTH]; - pin_properties[0] = pin_code_point_length; - pin_properties[1..].clone_from_slice(pin_hash); - Ok(self.store.transaction(&[ - StoreUpdate::Insert { - key: key::PIN_PROPERTIES, - value: &pin_properties[..], - }, - StoreUpdate::Remove { - key: key::FORCE_PIN_CHANGE, - }, - ])?) - } - - /// Returns the number of remaining PIN retries. - pub fn pin_retries(&self) -> Result { - match self.store.find(key::PIN_RETRIES)? { - None => Ok(MAX_PIN_RETRIES), - Some(value) if value.len() == 1 => Ok(value[0]), - _ => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR), +/// Returns the attestation private key if defined. +pub fn attestation_private_key( + env: &mut impl Env, +) -> Result, Ctap2StatusCode> { + match env.store().find(key::ATTESTATION_PRIVATE_KEY)? { + None => Ok(None), + Some(key) if key.len() == key_material::ATTESTATION_PRIVATE_KEY_LENGTH => { + Ok(Some(*array_ref![ + key, + 0, + key_material::ATTESTATION_PRIVATE_KEY_LENGTH + ])) } + Some(_) => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR), } +} - /// Decrements the number of remaining PIN retries. - pub fn decr_pin_retries(&mut self) -> Result<(), Ctap2StatusCode> { - let old_value = self.pin_retries()?; - let new_value = old_value.saturating_sub(1); - if new_value != old_value { - self.store.insert(key::PIN_RETRIES, &[new_value])?; - } - Ok(()) +/// Sets the attestation private key. +/// +/// If it is already defined, it is overwritten. +pub fn set_attestation_private_key( + env: &mut impl Env, + attestation_private_key: &[u8; key_material::ATTESTATION_PRIVATE_KEY_LENGTH], +) -> Result<(), Ctap2StatusCode> { + match env.store().find(key::ATTESTATION_PRIVATE_KEY)? { + None => Ok(env + .store() + .insert(key::ATTESTATION_PRIVATE_KEY, attestation_private_key)?), + Some(_) => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR), } +} - /// Resets the number of remaining PIN retries. - pub fn reset_pin_retries(&mut self) -> Result<(), Ctap2StatusCode> { - Ok(self.store.remove(key::PIN_RETRIES)?) +/// Returns the attestation certificate if defined. +pub fn attestation_certificate(env: &mut impl Env) -> Result>, Ctap2StatusCode> { + Ok(env.store().find(key::ATTESTATION_CERTIFICATE)?) +} + +/// Sets the attestation certificate. +/// +/// If it is already defined, it is overwritten. +pub fn set_attestation_certificate( + env: &mut impl Env, + attestation_certificate: &[u8], +) -> Result<(), Ctap2StatusCode> { + match env.store().find(key::ATTESTATION_CERTIFICATE)? { + None => Ok(env + .store() + .insert(key::ATTESTATION_CERTIFICATE, attestation_certificate)?), + Some(_) => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR), } +} - /// Returns the minimum PIN length. - pub fn min_pin_length(&self) -> Result { - match self.store.find(key::MIN_PIN_LENGTH)? { - None => Ok(DEFAULT_MIN_PIN_LENGTH), - Some(value) if value.len() == 1 => Ok(value[0]), - _ => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR), - } +/// Returns the AAGUID. +pub fn aaguid(env: &mut impl Env) -> Result<[u8; key_material::AAGUID_LENGTH], Ctap2StatusCode> { + let aaguid = env + .store() + .find(key::AAGUID)? + .ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?; + if aaguid.len() != key_material::AAGUID_LENGTH { + return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); } + Ok(*array_ref![aaguid, 0, key_material::AAGUID_LENGTH]) +} - /// Sets the minimum PIN length. - pub fn set_min_pin_length(&mut self, min_pin_length: u8) -> Result<(), Ctap2StatusCode> { - Ok(self.store.insert(key::MIN_PIN_LENGTH, &[min_pin_length])?) +/// Sets the AAGUID. +/// +/// If it is already defined, it is overwritten. +pub fn set_aaguid( + env: &mut impl Env, + aaguid: &[u8; key_material::AAGUID_LENGTH], +) -> Result<(), Ctap2StatusCode> { + Ok(env.store().insert(key::AAGUID, aaguid)?) +} + +/// Resets the store as for a CTAP reset. +/// +/// In particular persistent entries are not reset. +pub fn reset(env: &mut impl Env) -> Result<(), Ctap2StatusCode> { + env.store().clear(key::NUM_PERSISTENT_KEYS)?; + init(env)?; + Ok(()) +} + +/// Returns whether the PIN needs to be changed before its next usage. +pub fn has_force_pin_change(env: &mut impl Env) -> Result { + match env.store().find(key::FORCE_PIN_CHANGE)? { + None => Ok(false), + Some(value) if value.is_empty() => Ok(true), + _ => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR), } +} - /// Returns the list of RP IDs that are used to check if reading the minimum PIN length is - /// allowed. - pub fn min_pin_length_rp_ids(&self) -> Result, Ctap2StatusCode> { - let rp_ids = self.store.find(key::MIN_PIN_LENGTH_RP_IDS)?.map_or_else( - || { - Some( - DEFAULT_MIN_PIN_LENGTH_RP_IDS - .iter() - .map(|&s| String::from(s)) - .collect(), - ) - }, - |value| deserialize_min_pin_length_rp_ids(&value), - ); - debug_assert!(rp_ids.is_some()); - Ok(rp_ids.unwrap_or_default()) +/// Marks the PIN as outdated with respect to the new PIN policy. +pub fn force_pin_change(env: &mut impl Env) -> Result<(), Ctap2StatusCode> { + Ok(env.store().insert(key::FORCE_PIN_CHANGE, &[])?) +} + +/// Returns whether enterprise attestation is enabled. +pub fn enterprise_attestation(env: &mut impl Env) -> Result { + match env.store().find(key::ENTERPRISE_ATTESTATION)? { + None => Ok(false), + Some(value) if value.is_empty() => Ok(true), + _ => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR), } +} - /// Sets the list of RP IDs that are used to check if reading the minimum PIN length is allowed. - pub fn set_min_pin_length_rp_ids( - &mut self, - min_pin_length_rp_ids: Vec, - ) -> Result<(), Ctap2StatusCode> { - let mut min_pin_length_rp_ids = min_pin_length_rp_ids; - for &rp_id in DEFAULT_MIN_PIN_LENGTH_RP_IDS.iter() { - let rp_id = String::from(rp_id); - if !min_pin_length_rp_ids.contains(&rp_id) { - min_pin_length_rp_ids.push(rp_id); - } - } - if min_pin_length_rp_ids.len() > MAX_RP_IDS_LENGTH { - return Err(Ctap2StatusCode::CTAP2_ERR_KEY_STORE_FULL); - } - Ok(self.store.insert( - key::MIN_PIN_LENGTH_RP_IDS, - &serialize_min_pin_length_rp_ids(min_pin_length_rp_ids)?, - )?) +/// Marks enterprise attestation as enabled. +pub fn enable_enterprise_attestation(env: &mut impl Env) -> Result<(), Ctap2StatusCode> { + if !enterprise_attestation(env)? { + env.store().insert(key::ENTERPRISE_ATTESTATION, &[])?; } + Ok(()) +} - /// Reads the byte vector stored as the serialized large blobs array. - /// - /// If too few bytes exist at that offset, return the maximum number - /// available. This includes cases of offset being beyond the stored array. - /// - /// If no large blob is committed to the store, get responds as if an empty - /// CBOR array (0x80) was written, together with the 16 byte prefix of its - /// SHA256, to a total length of 17 byte (which is the shortest legitimate - /// large blob entry possible). - pub fn get_large_blob_array( - &self, - offset: usize, - byte_count: usize, - ) -> Result, Ctap2StatusCode> { - let byte_range = offset..offset + byte_count; - let output = fragment::read_range(&self.store, &key::LARGE_BLOB_SHARDS, byte_range)?; - Ok(output.unwrap_or_else(|| { - const EMPTY_LARGE_BLOB: [u8; 17] = [ - 0x80, 0x76, 0xBE, 0x8B, 0x52, 0x8D, 0x00, 0x75, 0xF7, 0xAA, 0xE9, 0x8D, 0x6F, 0xA5, - 0x7A, 0x6D, 0x3C, - ]; - let last_index = cmp::min(EMPTY_LARGE_BLOB.len(), offset + byte_count); - EMPTY_LARGE_BLOB - .get(offset..last_index) - .unwrap_or_default() - .to_vec() - })) +/// Returns whether alwaysUv is enabled. +pub fn has_always_uv(env: &mut impl Env) -> Result { + if ENFORCE_ALWAYS_UV { + return Ok(true); } - - /// Sets a byte vector as the serialized large blobs array. - pub fn commit_large_blob_array( - &mut self, - large_blob_array: &[u8], - ) -> Result<(), Ctap2StatusCode> { - // This input should have been caught at caller level. - if large_blob_array.len() > MAX_LARGE_BLOB_ARRAY_SIZE { - return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); - } - Ok(fragment::write( - &mut self.store, - &key::LARGE_BLOB_SHARDS, - large_blob_array, - )?) + match env.store().find(key::ALWAYS_UV)? { + None => Ok(false), + Some(value) if value.is_empty() => Ok(true), + _ => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR), } +} - /// Returns the attestation private key if defined. - pub fn attestation_private_key( - &self, - ) -> Result, Ctap2StatusCode> { - match self.store.find(key::ATTESTATION_PRIVATE_KEY)? { - None => Ok(None), - Some(key) if key.len() == key_material::ATTESTATION_PRIVATE_KEY_LENGTH => { - Ok(Some(*array_ref![ - key, - 0, - key_material::ATTESTATION_PRIVATE_KEY_LENGTH - ])) - } - Some(_) => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR), - } +/// Enables alwaysUv, when disabled, and vice versa. +pub fn toggle_always_uv(env: &mut impl Env) -> Result<(), Ctap2StatusCode> { + if ENFORCE_ALWAYS_UV { + return Err(Ctap2StatusCode::CTAP2_ERR_OPERATION_DENIED); } - - /// Sets the attestation private key. - /// - /// If it is already defined, it is overwritten. - pub fn set_attestation_private_key( - &mut self, - attestation_private_key: &[u8; key_material::ATTESTATION_PRIVATE_KEY_LENGTH], - ) -> Result<(), Ctap2StatusCode> { - match self.store.find(key::ATTESTATION_PRIVATE_KEY)? { - None => Ok(self - .store - .insert(key::ATTESTATION_PRIVATE_KEY, attestation_private_key)?), - Some(_) => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR), - } - } - - /// Returns the attestation certificate if defined. - pub fn attestation_certificate(&self) -> Result>, Ctap2StatusCode> { - Ok(self.store.find(key::ATTESTATION_CERTIFICATE)?) - } - - /// Sets the attestation certificate. - /// - /// If it is already defined, it is overwritten. - pub fn set_attestation_certificate( - &mut self, - attestation_certificate: &[u8], - ) -> Result<(), Ctap2StatusCode> { - match self.store.find(key::ATTESTATION_CERTIFICATE)? { - None => Ok(self - .store - .insert(key::ATTESTATION_CERTIFICATE, attestation_certificate)?), - Some(_) => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR), - } - } - - /// Returns the AAGUID. - pub fn aaguid(&self) -> Result<[u8; key_material::AAGUID_LENGTH], Ctap2StatusCode> { - let aaguid = self - .store - .find(key::AAGUID)? - .ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?; - if aaguid.len() != key_material::AAGUID_LENGTH { - return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); - } - Ok(*array_ref![aaguid, 0, key_material::AAGUID_LENGTH]) - } - - /// Sets the AAGUID. - /// - /// If it is already defined, it is overwritten. - pub fn set_aaguid( - &mut self, - aaguid: &[u8; key_material::AAGUID_LENGTH], - ) -> Result<(), Ctap2StatusCode> { - Ok(self.store.insert(key::AAGUID, aaguid)?) - } - - /// Resets the store as for a CTAP reset. - /// - /// In particular persistent entries are not reset. - pub fn reset(&mut self, rng: &mut impl Rng256) -> Result<(), Ctap2StatusCode> { - self.store.clear(key::NUM_PERSISTENT_KEYS)?; - self.init(rng)?; - Ok(()) - } - - /// Returns whether the PIN needs to be changed before its next usage. - pub fn has_force_pin_change(&self) -> Result { - match self.store.find(key::FORCE_PIN_CHANGE)? { - None => Ok(false), - Some(value) if value.is_empty() => Ok(true), - _ => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR), - } - } - - /// Marks the PIN as outdated with respect to the new PIN policy. - pub fn force_pin_change(&mut self) -> Result<(), Ctap2StatusCode> { - Ok(self.store.insert(key::FORCE_PIN_CHANGE, &[])?) - } - - /// Returns whether enterprise attestation is enabled. - pub fn enterprise_attestation(&self) -> Result { - match self.store.find(key::ENTERPRISE_ATTESTATION)? { - None => Ok(false), - Some(value) if value.is_empty() => Ok(true), - _ => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR), - } - } - - /// Marks enterprise attestation as enabled. - pub fn enable_enterprise_attestation(&mut self) -> Result<(), Ctap2StatusCode> { - if !self.enterprise_attestation()? { - self.store.insert(key::ENTERPRISE_ATTESTATION, &[])?; - } - Ok(()) - } - - /// Returns whether alwaysUv is enabled. - pub fn has_always_uv(&self) -> Result { - if ENFORCE_ALWAYS_UV { - return Ok(true); - } - match self.store.find(key::ALWAYS_UV)? { - None => Ok(false), - Some(value) if value.is_empty() => Ok(true), - _ => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR), - } - } - - /// Enables alwaysUv, when disabled, and vice versa. - pub fn toggle_always_uv(&mut self) -> Result<(), Ctap2StatusCode> { - if ENFORCE_ALWAYS_UV { - return Err(Ctap2StatusCode::CTAP2_ERR_OPERATION_DENIED); - } - if self.has_always_uv()? { - Ok(self.store.remove(key::ALWAYS_UV)?) - } else { - Ok(self.store.insert(key::ALWAYS_UV, &[])?) - } + if has_always_uv(env)? { + Ok(env.store().remove(key::ALWAYS_UV)?) + } else { + Ok(env.store().insert(key::ALWAYS_UV, &[])?) } } @@ -780,39 +764,36 @@ mod test { #[test] fn test_store() { let mut env = TestEnv::new(); - let mut persistent_store = PersistentStore::new(&mut env); - assert_eq!(persistent_store.count_credentials().unwrap(), 0); + assert_eq!(count_credentials(&mut env).unwrap(), 0); 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); + assert!(store_credential(&mut env, credential_source).is_ok()); + assert!(count_credentials(&mut env).unwrap() > 0); } #[test] fn test_delete_credential() { let mut env = TestEnv::new(); - let mut persistent_store = PersistentStore::new(&mut env); - assert_eq!(persistent_store.count_credentials().unwrap(), 0); + assert_eq!(count_credentials(&mut env).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(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); + assert!(store_credential(&mut env, credential_source).is_ok()); + assert_eq!(count_credentials(&mut env).unwrap(), i + 1); } - let mut count = persistent_store.count_credentials().unwrap(); + let mut count = count_credentials(&mut env).unwrap(); for credential_id in credential_ids { - assert!(persistent_store.delete_credential(&credential_id).is_ok()); + assert!(delete_credential(&mut env, &credential_id).is_ok()); count -= 1; - assert_eq!(persistent_store.count_credentials().unwrap(), count); + assert_eq!(count_credentials(&mut env).unwrap(), count); } } #[test] fn test_update_credential() { let mut env = TestEnv::new(); - let mut persistent_store = PersistentStore::new(&mut env); let user = PublicKeyCredentialUserEntity { // User ID is ignored. user_id: vec![0x00], @@ -821,25 +802,21 @@ mod test { user_icon: Some("icon".to_string()), }; assert_eq!( - persistent_store.update_credential(&[0x1D], user.clone()), + update_credential(&mut env, &[0x1D], user.clone()), Err(Ctap2StatusCode::CTAP2_ERR_NO_CREDENTIALS) ); 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 - .find_credential("example.com", &credential_id, false) + assert!(store_credential(&mut env, credential_source).is_ok()); + let stored_credential = find_credential(&mut env, "example.com", &credential_id, false) .unwrap() .unwrap(); assert_eq!(stored_credential.user_name, None); assert_eq!(stored_credential.user_display_name, None); assert_eq!(stored_credential.user_icon, None); - assert!(persistent_store - .update_credential(&credential_id, user.clone()) - .is_ok()); - let stored_credential = persistent_store - .find_credential("example.com", &credential_id, false) + assert!(update_credential(&mut env, &credential_id, user.clone()).is_ok()); + let stored_credential = find_credential(&mut env, "example.com", &credential_id, false) .unwrap() .unwrap(); assert_eq!(stored_credential.user_name, user.user_name); @@ -850,29 +827,27 @@ mod test { #[test] fn test_credential_order() { 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()); + assert!(store_credential(&mut env, credential_source).is_ok()); let mut credential_source = create_credential_source(env.rng(), "example.com", vec![]); - credential_source.creation_order = persistent_store.new_creation_order().unwrap(); + credential_source.creation_order = new_creation_order(&mut env).unwrap(); assert!(credential_source.creation_order > current_latest_creation); let current_latest_creation = credential_source.creation_order; - assert!(persistent_store.store_credential(credential_source).is_ok()); - assert!(persistent_store.new_creation_order().unwrap() > current_latest_creation); + assert!(store_credential(&mut env, credential_source).is_ok()); + assert!(new_creation_order(&mut env).unwrap() > current_latest_creation); } #[test] fn test_fill_store() { let mut env = TestEnv::new(); - let mut persistent_store = PersistentStore::new(&mut env); - assert_eq!(persistent_store.count_credentials().unwrap(), 0); + assert_eq!(count_credentials(&mut env).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(env.rng(), "example.com", user_handle); - assert!(persistent_store.store_credential(credential_source).is_ok()); - assert_eq!(persistent_store.count_credentials().unwrap(), i + 1); + assert!(store_credential(&mut env, credential_source).is_ok()); + assert_eq!(count_credentials(&mut env).unwrap(), i + 1); } let credential_source = create_credential_source( env.rng(), @@ -880,11 +855,11 @@ mod test { vec![MAX_SUPPORTED_RESIDENT_KEYS as u8], ); assert_eq!( - persistent_store.store_credential(credential_source), + store_credential(&mut env, credential_source), Err(Ctap2StatusCode::CTAP2_ERR_KEY_STORE_FULL) ); assert_eq!( - persistent_store.count_credentials().unwrap(), + count_credentials(&mut env).unwrap(), MAX_SUPPORTED_RESIDENT_KEYS ); } @@ -892,36 +867,35 @@ mod test { #[test] fn test_overwrite() { let mut env = TestEnv::new(); - let mut persistent_store = PersistentStore::new(&mut env); - assert_eq!(persistent_store.count_credentials().unwrap(), 0); + init(&mut env).unwrap(); + + assert_eq!(count_credentials(&mut env).unwrap(), 0); // These should have different IDs. 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(); - assert!(persistent_store - .store_credential(credential_source0) - .is_ok()); - assert!(persistent_store - .store_credential(credential_source1) - .is_ok()); - assert_eq!(persistent_store.count_credentials().unwrap(), 1); - assert!(persistent_store - .find_credential("example.com", &credential_id0, false) - .unwrap() - .is_none()); - assert!(persistent_store - .find_credential("example.com", &credential_id1, false) - .unwrap() - .is_some()); + assert!(store_credential(&mut env, credential_source0).is_ok()); + assert!(store_credential(&mut env, credential_source1).is_ok()); + assert_eq!(count_credentials(&mut env).unwrap(), 1); + assert!( + find_credential(&mut env, "example.com", &credential_id0, false) + .unwrap() + .is_none() + ); + assert!( + find_credential(&mut env, "example.com", &credential_id1, false) + .unwrap() + .is_some() + ); - let mut persistent_store = PersistentStore::new(&mut env); + reset(&mut env).unwrap(); 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(env.rng(), "example.com", user_handle); - assert!(persistent_store.store_credential(credential_source).is_ok()); - assert_eq!(persistent_store.count_credentials().unwrap(), i + 1); + assert!(store_credential(&mut env, credential_source).is_ok()); + assert_eq!(count_credentials(&mut env).unwrap(), i + 1); } let credential_source = create_credential_source( env.rng(), @@ -929,11 +903,11 @@ mod test { vec![MAX_SUPPORTED_RESIDENT_KEYS as u8], ); assert_eq!( - persistent_store.store_credential(credential_source), + store_credential(&mut env, credential_source), Err(Ctap2StatusCode::CTAP2_ERR_KEY_STORE_FULL) ); assert_eq!( - persistent_store.count_credentials().unwrap(), + count_credentials(&mut env).unwrap(), MAX_SUPPORTED_RESIDENT_KEYS ); } @@ -941,7 +915,6 @@ mod test { #[test] fn test_get_credential() { 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 = @@ -949,9 +922,9 @@ mod test { 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(); - assert!(persistent_store.store_credential(credential_source).is_ok()); - let (key, _) = persistent_store.find_credential_item(&cred_id).unwrap(); - let cred = persistent_store.get_credential(key).unwrap(); + assert!(store_credential(&mut env, credential_source).is_ok()); + let (key, _) = find_credential_item(&mut env, &cred_id).unwrap(); + let cred = get_credential(&mut env, key).unwrap(); assert_eq!(&cred_id, &cred.credential_id); } } @@ -959,26 +932,17 @@ mod test { #[test] fn test_find() { let mut env = TestEnv::new(); - let mut persistent_store = PersistentStore::new(&mut env); - assert_eq!(persistent_store.count_credentials().unwrap(), 0); + assert_eq!(count_credentials(&mut env).unwrap(), 0); 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 - .store_credential(credential_source0) - .is_ok()); - assert!(persistent_store - .store_credential(credential_source1) - .is_ok()); + assert!(store_credential(&mut env, credential_source0).is_ok()); + assert!(store_credential(&mut env, credential_source1).is_ok()); - let no_credential = persistent_store - .find_credential("another.example.com", &id0, false) - .unwrap(); + let no_credential = find_credential(&mut env, "another.example.com", &id0, false).unwrap(); assert_eq!(no_credential, None); - let found_credential = persistent_store - .find_credential("example.com", &id0, false) - .unwrap(); + let found_credential = find_credential(&mut env, "example.com", &id0, false).unwrap(); let expected_credential = PublicKeyCredentialSource { key_type: PublicKeyCredentialType::PublicKey, credential_id: id0, @@ -999,8 +963,7 @@ mod test { #[test] fn test_find_with_cred_protect() { let mut env = TestEnv::new(); - let mut persistent_store = PersistentStore::new(&mut env); - assert_eq!(persistent_store.count_credentials().unwrap(), 0); + assert_eq!(count_credentials(&mut env).unwrap(), 0); let private_key = crypto::ecdsa::SecKey::gensk(env.rng()); let credential = PublicKeyCredentialSource { key_type: PublicKeyCredentialType::PublicKey, @@ -1016,22 +979,20 @@ mod test { cred_blob: None, large_blob_key: None, }; - assert!(persistent_store.store_credential(credential).is_ok()); + assert!(store_credential(&mut env, credential).is_ok()); - let no_credential = persistent_store - .find_credential("example.com", &[0x00], true) - .unwrap(); + let no_credential = find_credential(&mut env, "example.com", &[0x00], true).unwrap(); assert_eq!(no_credential, None); } #[test] fn test_master_keys() { let mut env = TestEnv::new(); - let mut persistent_store = PersistentStore::new(&mut env); + init(&mut env).unwrap(); // Master keys stay the same within the same CTAP reset cycle. - let master_keys_1 = persistent_store.master_keys().unwrap(); - let master_keys_2 = persistent_store.master_keys().unwrap(); + let master_keys_1 = master_keys(&mut env).unwrap(); + let master_keys_2 = master_keys(&mut env).unwrap(); assert_eq!(master_keys_2.encryption, master_keys_1.encryption); assert_eq!(master_keys_2.hmac, master_keys_1.hmac); @@ -1039,8 +1000,8 @@ 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(env.rng()).unwrap(); - let master_keys_3 = persistent_store.master_keys().unwrap(); + reset(&mut env).unwrap(); + let master_keys_3 = master_keys(&mut env).unwrap(); assert!(master_keys_3.encryption != master_encryption_key.as_slice()); assert!(master_keys_3.hmac != master_hmac_key.as_slice()); } @@ -1048,21 +1009,21 @@ mod test { #[test] fn test_cred_random_secret() { let mut env = TestEnv::new(); - let mut persistent_store = PersistentStore::new(&mut env); + init(&mut env).unwrap(); // CredRandom secrets stay the same within the same CTAP reset cycle. - let cred_random_with_uv_1 = persistent_store.cred_random_secret(true).unwrap(); - let cred_random_without_uv_1 = persistent_store.cred_random_secret(false).unwrap(); - let cred_random_with_uv_2 = persistent_store.cred_random_secret(true).unwrap(); - let cred_random_without_uv_2 = persistent_store.cred_random_secret(false).unwrap(); + let cred_random_with_uv_1 = cred_random_secret(&mut env, true).unwrap(); + let cred_random_without_uv_1 = cred_random_secret(&mut env, false).unwrap(); + let cred_random_with_uv_2 = cred_random_secret(&mut env, true).unwrap(); + let cred_random_without_uv_2 = cred_random_secret(&mut env, false).unwrap(); assert_eq!(cred_random_with_uv_1, cred_random_with_uv_2); assert_eq!(cred_random_without_uv_1, cred_random_without_uv_2); // CredRandom secrets change after reset. This test may fail if the random generator produces the // same keys. - 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(); + reset(&mut env).unwrap(); + let cred_random_with_uv_3 = cred_random_secret(&mut env, true).unwrap(); + let cred_random_without_uv_3 = cred_random_secret(&mut env, false).unwrap(); assert!(cred_random_with_uv_1 != cred_random_with_uv_3); assert!(cred_random_without_uv_1 != cred_random_without_uv_3); } @@ -1070,11 +1031,10 @@ mod test { #[test] fn test_pin_hash_and_length() { 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()); + assert!(pin_hash(&mut env).unwrap().is_none()); + assert!(pin_code_point_length(&mut env).unwrap().is_none()); // Setting the pin sets the pin hash. let random_data = env.rng().gen_uniform_u8x32(); @@ -1083,143 +1043,112 @@ mod test { let pin_hash_2 = *array_ref!(random_data, PIN_AUTH_LENGTH, PIN_AUTH_LENGTH); let pin_length_1 = 4; let pin_length_2 = 63; - persistent_store.set_pin(&pin_hash_1, pin_length_1).unwrap(); - assert_eq!(persistent_store.pin_hash().unwrap(), Some(pin_hash_1)); - assert_eq!( - persistent_store.pin_code_point_length().unwrap(), - Some(pin_length_1) - ); - persistent_store.set_pin(&pin_hash_2, pin_length_2).unwrap(); - assert_eq!(persistent_store.pin_hash().unwrap(), Some(pin_hash_2)); - assert_eq!( - persistent_store.pin_code_point_length().unwrap(), - Some(pin_length_2) - ); + set_pin(&mut env, &pin_hash_1, pin_length_1).unwrap(); + assert_eq!(pin_hash(&mut env).unwrap(), Some(pin_hash_1)); + assert_eq!(pin_code_point_length(&mut env).unwrap(), Some(pin_length_1)); + set_pin(&mut env, &pin_hash_2, pin_length_2).unwrap(); + assert_eq!(pin_hash(&mut env).unwrap(), Some(pin_hash_2)); + assert_eq!(pin_code_point_length(&mut env).unwrap(), Some(pin_length_2)); // Resetting the storage resets the pin hash. - persistent_store.reset(env.rng()).unwrap(); - assert!(persistent_store.pin_hash().unwrap().is_none()); - assert!(persistent_store.pin_code_point_length().unwrap().is_none()); + reset(&mut env).unwrap(); + assert!(pin_hash(&mut env).unwrap().is_none()); + assert!(pin_code_point_length(&mut env).unwrap().is_none()); } #[test] fn test_pin_retries() { 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)); + assert_eq!(pin_retries(&mut env), Ok(MAX_PIN_RETRIES)); // Decrementing the pin retries decrements the pin retries. - for pin_retries in (0..MAX_PIN_RETRIES).rev() { - persistent_store.decr_pin_retries().unwrap(); - assert_eq!(persistent_store.pin_retries(), Ok(pin_retries)); + for retries in (0..MAX_PIN_RETRIES).rev() { + decr_pin_retries(&mut env).unwrap(); + assert_eq!(pin_retries(&mut env), Ok(retries)); } // Decrementing the pin retries after zero does not modify the pin retries. - persistent_store.decr_pin_retries().unwrap(); - assert_eq!(persistent_store.pin_retries(), Ok(0)); + decr_pin_retries(&mut env).unwrap(); + assert_eq!(pin_retries(&mut env), Ok(0)); // Resetting the pin retries resets the pin retries. - persistent_store.reset_pin_retries().unwrap(); - assert_eq!(persistent_store.pin_retries(), Ok(MAX_PIN_RETRIES)); + reset_pin_retries(&mut env).unwrap(); + assert_eq!(pin_retries(&mut env), Ok(MAX_PIN_RETRIES)); } #[test] fn test_persistent_keys() { let mut env = TestEnv::new(); - let mut persistent_store = PersistentStore::new(&mut env); + init(&mut env).unwrap(); // Make sure the attestation are absent. There is no batch attestation in tests. - assert!(persistent_store - .attestation_private_key() - .unwrap() - .is_none()); - assert!(persistent_store - .attestation_certificate() - .unwrap() - .is_none()); + assert!(attestation_private_key(&mut env,).unwrap().is_none()); + assert!(attestation_certificate(&mut env,).unwrap().is_none()); // Make sure the persistent keys are initialized to dummy values. let dummy_key = [0x41u8; key_material::ATTESTATION_PRIVATE_KEY_LENGTH]; let dummy_cert = [0xddu8; 20]; - persistent_store - .set_attestation_private_key(&dummy_key) - .unwrap(); - persistent_store - .set_attestation_certificate(&dummy_cert) - .unwrap(); - assert_eq!(&persistent_store.aaguid().unwrap(), key_material::AAGUID); + set_attestation_private_key(&mut env, &dummy_key).unwrap(); + set_attestation_certificate(&mut env, &dummy_cert).unwrap(); + assert_eq!(&aaguid(&mut env).unwrap(), key_material::AAGUID); // The persistent keys stay initialized and preserve their value after a reset. - persistent_store.reset(env.rng()).unwrap(); + reset(&mut env).unwrap(); assert_eq!( - &persistent_store.attestation_private_key().unwrap().unwrap(), + &attestation_private_key(&mut env).unwrap().unwrap(), &dummy_key ); assert_eq!( - persistent_store.attestation_certificate().unwrap().unwrap(), + attestation_certificate(&mut env).unwrap().unwrap(), &dummy_cert ); - assert_eq!(&persistent_store.aaguid().unwrap(), key_material::AAGUID); + assert_eq!(&aaguid(&mut env).unwrap(), key_material::AAGUID); } #[test] fn test_min_pin_length() { let mut env = TestEnv::new(); - let mut persistent_store = PersistentStore::new(&mut env); // The minimum PIN length is initially at the default. - assert_eq!( - persistent_store.min_pin_length().unwrap(), - DEFAULT_MIN_PIN_LENGTH - ); + assert_eq!(min_pin_length(&mut env).unwrap(), DEFAULT_MIN_PIN_LENGTH); // Changes by the setter are reflected by the getter.. let new_min_pin_length = 8; - persistent_store - .set_min_pin_length(new_min_pin_length) - .unwrap(); - assert_eq!( - persistent_store.min_pin_length().unwrap(), - new_min_pin_length - ); + set_min_pin_length(&mut env, new_min_pin_length).unwrap(); + assert_eq!(min_pin_length(&mut env).unwrap(), new_min_pin_length); } #[test] fn test_min_pin_length_rp_ids() { 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!( - persistent_store.min_pin_length_rp_ids().unwrap(), + min_pin_length_rp_ids(&mut env).unwrap(), DEFAULT_MIN_PIN_LENGTH_RP_IDS ); // Changes by the setter are reflected by the getter. let mut rp_ids = vec![String::from("example.com")]; - assert_eq!( - persistent_store.set_min_pin_length_rp_ids(rp_ids.clone()), - Ok(()) - ); + assert_eq!(set_min_pin_length_rp_ids(&mut env, rp_ids.clone()), Ok(())); for &rp_id in DEFAULT_MIN_PIN_LENGTH_RP_IDS.iter() { let rp_id = String::from(rp_id); if !rp_ids.contains(&rp_id) { rp_ids.push(rp_id); } } - assert_eq!(persistent_store.min_pin_length_rp_ids().unwrap(), rp_ids); + assert_eq!(min_pin_length_rp_ids(&mut env).unwrap(), rp_ids); } #[test] fn test_max_large_blob_array_size() { let mut env = TestEnv::new(); - let persistent_store = PersistentStore::new(&mut env); assert!( MAX_LARGE_BLOB_ARRAY_SIZE - <= persistent_store.store.max_value_length() + <= env.store().max_value_length() * (key::LARGE_BLOB_SHARDS.end - key::LARGE_BLOB_SHARDS.start) ); } @@ -1227,46 +1156,38 @@ mod test { #[test] fn test_commit_get_large_blob_array() { let mut env = TestEnv::new(); - let mut persistent_store = PersistentStore::new(&mut env); let large_blob_array = vec![0x01, 0x02, 0x03]; - assert!(persistent_store - .commit_large_blob_array(&large_blob_array) - .is_ok()); - let restored_large_blob_array = persistent_store.get_large_blob_array(0, 1).unwrap(); + assert!(commit_large_blob_array(&mut env, &large_blob_array).is_ok()); + let restored_large_blob_array = get_large_blob_array(&mut env, 0, 1).unwrap(); assert_eq!(vec![0x01], restored_large_blob_array); - let restored_large_blob_array = persistent_store.get_large_blob_array(1, 1).unwrap(); + let restored_large_blob_array = get_large_blob_array(&mut env, 1, 1).unwrap(); assert_eq!(vec![0x02], restored_large_blob_array); - let restored_large_blob_array = persistent_store.get_large_blob_array(2, 1).unwrap(); + let restored_large_blob_array = get_large_blob_array(&mut env, 2, 1).unwrap(); assert_eq!(vec![0x03], restored_large_blob_array); - let restored_large_blob_array = persistent_store.get_large_blob_array(2, 2).unwrap(); + let restored_large_blob_array = get_large_blob_array(&mut env, 2, 2).unwrap(); assert_eq!(vec![0x03], restored_large_blob_array); - let restored_large_blob_array = persistent_store.get_large_blob_array(3, 1).unwrap(); + let restored_large_blob_array = get_large_blob_array(&mut env, 3, 1).unwrap(); assert_eq!(Vec::::new(), restored_large_blob_array); - let restored_large_blob_array = persistent_store.get_large_blob_array(4, 1).unwrap(); + let restored_large_blob_array = get_large_blob_array(&mut env, 4, 1).unwrap(); assert_eq!(Vec::::new(), restored_large_blob_array); } #[test] fn test_commit_get_large_blob_array_overwrite() { let mut env = TestEnv::new(); - let mut persistent_store = PersistentStore::new(&mut env); let large_blob_array = vec![0x11; 5]; - assert!(persistent_store - .commit_large_blob_array(&large_blob_array) - .is_ok()); + assert!(commit_large_blob_array(&mut env, &large_blob_array).is_ok()); let large_blob_array = vec![0x22; 4]; - assert!(persistent_store - .commit_large_blob_array(&large_blob_array) - .is_ok()); - let restored_large_blob_array = persistent_store.get_large_blob_array(0, 5).unwrap(); + assert!(commit_large_blob_array(&mut env, &large_blob_array).is_ok()); + let restored_large_blob_array = get_large_blob_array(&mut env, 0, 5).unwrap(); assert_eq!(large_blob_array, restored_large_blob_array); - let restored_large_blob_array = persistent_store.get_large_blob_array(4, 1).unwrap(); + let restored_large_blob_array = get_large_blob_array(&mut env, 4, 1).unwrap(); assert_eq!(Vec::::new(), restored_large_blob_array); - assert!(persistent_store.commit_large_blob_array(&[]).is_ok()); - let restored_large_blob_array = persistent_store.get_large_blob_array(0, 20).unwrap(); + assert!(commit_large_blob_array(&mut env, &[]).is_ok()); + let restored_large_blob_array = get_large_blob_array(&mut env, 0, 20).unwrap(); // Committing an empty array resets to the default blob of 17 byte. assert_eq!(restored_large_blob_array.len(), 17); } @@ -1274,83 +1195,70 @@ mod test { #[test] fn test_commit_get_large_blob_array_no_commit() { 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, 0x7A, 0x6D, 0x3C, ]; - let restored_large_blob_array = persistent_store.get_large_blob_array(0, 17).unwrap(); + let restored_large_blob_array = get_large_blob_array(&mut env, 0, 17).unwrap(); assert_eq!(empty_blob_array, restored_large_blob_array); - let restored_large_blob_array = persistent_store.get_large_blob_array(0, 1).unwrap(); + let restored_large_blob_array = get_large_blob_array(&mut env, 0, 1).unwrap(); assert_eq!(vec![0x80], restored_large_blob_array); - let restored_large_blob_array = persistent_store.get_large_blob_array(16, 1).unwrap(); + let restored_large_blob_array = get_large_blob_array(&mut env, 16, 1).unwrap(); assert_eq!(vec![0x3C], restored_large_blob_array); } #[test] fn test_global_signature_counter() { let mut env = TestEnv::new(); - let mut persistent_store = PersistentStore::new(&mut env); let mut counter_value = 1; - assert_eq!( - persistent_store.global_signature_counter().unwrap(), - counter_value - ); + assert_eq!(global_signature_counter(&mut env).unwrap(), counter_value); for increment in 1..10 { - assert!(persistent_store - .incr_global_signature_counter(increment) - .is_ok()); + assert!(incr_global_signature_counter(&mut env, increment).is_ok()); counter_value += increment; - assert_eq!( - persistent_store.global_signature_counter().unwrap(), - counter_value - ); + assert_eq!(global_signature_counter(&mut env).unwrap(), counter_value); } } #[test] fn test_force_pin_change() { 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(())); - assert!(persistent_store.has_force_pin_change().unwrap()); - assert_eq!(persistent_store.set_pin(&[0x88; 16], 8), Ok(())); - assert!(!persistent_store.has_force_pin_change().unwrap()); + assert!(!has_force_pin_change(&mut env).unwrap()); + assert_eq!(force_pin_change(&mut env), Ok(())); + assert!(has_force_pin_change(&mut env).unwrap()); + assert_eq!(set_pin(&mut env, &[0x88; 16], 8), Ok(())); + assert!(!has_force_pin_change(&mut env).unwrap()); } #[test] fn test_enterprise_attestation() { 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(env.rng()).unwrap(); - assert!(!persistent_store.enterprise_attestation().unwrap()); + assert!(!enterprise_attestation(&mut env).unwrap()); + assert_eq!(enable_enterprise_attestation(&mut env), Ok(())); + assert!(enterprise_attestation(&mut env).unwrap()); + reset(&mut env).unwrap(); + assert!(!enterprise_attestation(&mut env).unwrap()); } #[test] fn test_always_uv() { let mut env = TestEnv::new(); - let mut persistent_store = PersistentStore::new(&mut env); if ENFORCE_ALWAYS_UV { - assert!(persistent_store.has_always_uv().unwrap()); + assert!(has_always_uv(&mut env).unwrap()); assert_eq!( - persistent_store.toggle_always_uv(), + toggle_always_uv(&mut env), Err(Ctap2StatusCode::CTAP2_ERR_OPERATION_DENIED) ); } else { - assert!(!persistent_store.has_always_uv().unwrap()); - assert_eq!(persistent_store.toggle_always_uv(), Ok(())); - assert!(persistent_store.has_always_uv().unwrap()); - assert_eq!(persistent_store.toggle_always_uv(), Ok(())); - assert!(!persistent_store.has_always_uv().unwrap()); + assert!(!has_always_uv(&mut env).unwrap()); + assert_eq!(toggle_always_uv(&mut env), Ok(())); + assert!(has_always_uv(&mut env).unwrap()); + assert_eq!(toggle_always_uv(&mut env), Ok(())); + assert!(!has_always_uv(&mut env).unwrap()); } } diff --git a/src/env/mod.rs b/src/env/mod.rs index dcb8f93..ba8f75b 100644 --- a/src/env/mod.rs +++ b/src/env/mod.rs @@ -2,7 +2,7 @@ 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}; +use persistent_store::{Storage, Store}; #[cfg(feature = "std")] pub mod test; @@ -24,14 +24,11 @@ pub trait Env { fn rng(&mut self) -> &mut Self::Rng; fn user_presence(&mut self) -> &mut Self::UserPresence; + fn store(&mut self) -> &mut Store; - /// Returns the unique storage instance. + /// Returns the upgrade 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; + /// Upgrade storage is optional, so implementations may return `None`. However, implementations + /// should either always return `None` or always return `Some`. + fn upgrade_storage(&mut self) -> Option<&mut Self::UpgradeStorage>; } diff --git a/src/env/test/mod.rs b/src/env/test/mod.rs index 02a89c7..cf1f506 100644 --- a/src/env/test/mod.rs +++ b/src/env/test/mod.rs @@ -3,26 +3,55 @@ 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}; +use persistent_store::{BufferOptions, BufferStorage, Store}; mod upgrade_storage; pub struct TestEnv { rng: ThreadRng256, user_presence: TestUserPresence, + store: Store, + upgrade_storage: Option, } pub struct TestUserPresence { check: Box Result<(), Ctap2StatusCode>>, } +fn new_storage() -> BufferStorage { + // 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, + }; + BufferStorage::new(store, options) +} + impl TestEnv { pub fn new() -> Self { let rng = ThreadRng256 {}; let user_presence = TestUserPresence { check: Box::new(|_| Ok(())), }; - TestEnv { rng, user_presence } + let storage = new_storage(); + let store = Store::new(storage).ok().unwrap(); + let upgrade_storage = Some(BufferUpgradeStorage::new().unwrap()); + TestEnv { + rng, + user_presence, + store, + upgrade_storage, + } + } + + pub fn disable_upgrade_storage(&mut self) { + self.upgrade_storage = None; } } @@ -52,22 +81,11 @@ impl Env for TestEnv { &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 store(&mut self) -> &mut Store { + &mut self.store } - fn upgrade_storage(&mut self) -> StorageResult { - BufferUpgradeStorage::new() + fn upgrade_storage(&mut self) -> Option<&mut Self::UpgradeStorage> { + self.upgrade_storage.as_mut() } } diff --git a/src/env/tock/mod.rs b/src/env/tock/mod.rs index a8decfd..5c32822 100644 --- a/src/env/tock/mod.rs +++ b/src/env/tock/mod.rs @@ -14,14 +14,14 @@ 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; +use persistent_store::{StorageResult, Store}; mod storage; pub struct TockEnv { rng: TockRng256, - storage: bool, - upgrade_storage: bool, + store: Store, + upgrade_storage: SyscallUpgradeStorage, } impl TockEnv { @@ -34,10 +34,13 @@ impl TockEnv { // Make sure the environment was not already taken. static TAKEN: AtomicBool = AtomicBool::new(false); assert!(!TAKEN.fetch_or(true, Ordering::SeqCst)); + let storage = unsafe { steal_storage() }.unwrap(); + let store = Store::new(storage).ok().unwrap(); + let upgrade_storage = SyscallUpgradeStorage::new().unwrap(); TockEnv { rng: TockRng256 {}, - storage: false, - upgrade_storage: false, + store, + upgrade_storage, } } } @@ -75,23 +78,15 @@ impl Env for TockEnv { self } - fn storage(&mut self) -> StorageResult { - assert_once(&mut self.storage); - unsafe { steal_storage() } + fn store(&mut self) -> &mut Store { + &mut self.store } - fn upgrade_storage(&mut self) -> StorageResult { - assert_once(&mut self.upgrade_storage); - SyscallUpgradeStorage::new() + fn upgrade_storage(&mut self) -> Option<&mut Self::UpgradeStorage> { + Some(&mut self.upgrade_storage) } } -/// Asserts a boolean is false and sets it to true. -fn assert_once(b: &mut bool) { - assert!(!*b); - *b = true; -} - // Returns whether the keepalive was sent, or false if cancelled. fn send_keepalive_up_needed( cid: ChannelID, diff --git a/src/lib.rs b/src/lib.rs index c3be33d..26573c2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,7 +35,7 @@ pub mod env; /// CTAP implementation parameterized by its environment. pub struct Ctap { env: E, - state: CtapState, + state: CtapState, hid: CtapHid, } @@ -49,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 }