Merge branch 'firmware_protection' into env_console

This commit is contained in:
Julien Cretin
2022-03-08 15:02:34 +01:00
28 changed files with 2280 additions and 1883 deletions

View File

@@ -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<E: Env>(
persistent_store: &mut PersistentStore<E>,
fn check_and_store_new_pin(
env: &mut impl Env,
shared_secret: &dyn SharedSecret,
new_pin_enc: Vec<u8>,
) -> 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<E: Env>(
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<E: Env>(
fn verify_pin_hash_enc(
&mut self,
rng: &mut impl Rng256,
persistent_store: &mut PersistentStore<E>,
env: &mut impl Env,
pin_uv_auth_protocol: PinUvAuthProtocol,
shared_secret: &dyn SharedSecret,
pin_hash_enc: Vec<u8>,
) -> 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<E: Env>(
fn process_get_pin_retries(
&self,
persistent_store: &PersistentStore<E>,
env: &mut impl Env,
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
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<E: Env>(
fn process_set_pin(
&mut self,
persistent_store: &mut PersistentStore<E>,
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<E: Env>(
fn process_change_pin(
&mut self,
rng: &mut impl Rng256,
persistent_store: &mut PersistentStore<E>,
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<E: Env>(
fn process_get_pin_token(
&mut self,
rng: &mut impl Rng256,
persistent_store: &mut PersistentStore<E>,
env: &mut impl Env,
client_pin_params: AuthenticatorClientPinParameters,
now: ClockValue,
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
@@ -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<E: Env>(
fn process_get_pin_uv_auth_token_using_pin_with_permissions(
&mut self,
rng: &mut impl Rng256,
persistent_store: &mut PersistentStore<E>,
env: &mut impl Env,
mut client_pin_params: AuthenticatorClientPinParameters,
now: ClockValue,
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
@@ -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<E: Env>(
pub fn process_command(
&mut self,
rng: &mut impl Rng256,
persistent_store: &mut PersistentStore<E>,
env: &mut impl Env,
client_pin_params: AuthenticatorClientPinParameters,
now: ClockValue,
) -> Result<ResponseData, Ctap2StatusCode> {
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<E: Env>(persistent_store: &mut PersistentStore<E>) {
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!(

View File

@@ -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<E: Env>(
persistent_store: &mut PersistentStore<E>,
fn process_enable_enterprise_attestation(
env: &mut impl Env,
) -> Result<ResponseData, Ctap2StatusCode> {
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<E: Env>(
}
/// Processes the subcommand toggleAlwaysUv for AuthenticatorConfig.
fn process_toggle_always_uv<E: Env>(
persistent_store: &mut PersistentStore<E>,
) -> Result<ResponseData, Ctap2StatusCode> {
persistent_store.toggle_always_uv()?;
fn process_toggle_always_uv(env: &mut impl Env) -> Result<ResponseData, Ctap2StatusCode> {
storage::toggle_always_uv(env)?;
Ok(ResponseData::AuthenticatorConfig)
}
/// Processes the subcommand setMinPINLength for AuthenticatorConfig.
fn process_set_min_pin_length<E: Env>(
persistent_store: &mut PersistentStore<E>,
fn process_set_min_pin_length(
env: &mut impl Env,
params: SetMinPinLengthParams,
) -> Result<ResponseData, Ctap2StatusCode> {
let SetMinPinLengthParams {
@@ -52,31 +50,31 @@ fn process_set_min_pin_length<E: Env>(
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<E: Env>(
persistent_store: &mut PersistentStore<E>,
pub fn process_config(
env: &mut impl Env,
client_pin: &mut ClientPin,
params: AuthenticatorConfigParameters,
) -> Result<ResponseData, Ctap2StatusCode> {
@@ -87,9 +85,9 @@ pub fn process_config<E: Env>(
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<E: Env>(
}
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)

View File

@@ -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<E: Env>(
persistent_store: &PersistentStore<E>,
) -> Result<BTreeSet<String>, Ctap2StatusCode> {
fn get_stored_rp_ids(env: &mut impl Env) -> Result<BTreeSet<String>, 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<E: Env>(
persistent_store: &mut PersistentStore<E>,
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<E: Env>(
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<E: Env>(
persistent_store: &PersistentStore<E>,
fn process_get_creds_metadata(
env: &mut impl Env,
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
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<E: Env>(
persistent_store: &PersistentStore<E>,
fn process_enumerate_rps_begin(
env: &mut impl Env,
stateful_command_permission: &mut StatefulPermission,
now: ClockValue,
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
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<E: Env>(
}
/// Processes the subcommand enumerateRPsGetNextRP for CredentialManagement.
fn process_enumerate_rps_get_next_rp<E: Env>(
persistent_store: &PersistentStore<E>,
fn process_enumerate_rps_get_next_rp(
env: &mut impl Env,
stateful_command_permission: &mut StatefulPermission,
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
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<E: Env>(
}
/// Processes the subcommand enumerateCredentialsBegin for CredentialManagement.
fn process_enumerate_credentials_begin<E: Env>(
persistent_store: &PersistentStore<E>,
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<E: Env>(
.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<usize> = 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<E: Env>(
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<E: Env>(
}
/// Processes the subcommand enumerateCredentialsGetNextCredential for CredentialManagement.
fn process_enumerate_credentials_get_next_credential<E: Env>(
persistent_store: &PersistentStore<E>,
fn process_enumerate_credentials_get_next_credential(
env: &mut impl Env,
stateful_command_permission: &mut StatefulPermission,
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
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<E: Env>(
persistent_store: &mut PersistentStore<E>,
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<E: Env>(
.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<E: Env>(
persistent_store: &mut PersistentStore<E>,
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<E: Env>(
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<E: Env>(
persistent_store: &mut PersistentStore<E>,
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<E: Env>(
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<E: Env>(
}
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,

View File

@@ -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<E: Env>(
env: &mut E,
pub fn process_command(
env: &mut impl Env,
message: &[u8],
ctap_state: &mut CtapState<E>,
ctap_state: &mut CtapState,
clock_value: ClockValue,
) -> Result<Vec<u8>, 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<E: Env>(
env: &mut E,
fn process_register(
env: &mut impl Env,
challenge: [u8; 32],
application: [u8; 32],
ctap_state: &mut CtapState<E>,
ctap_state: &mut CtapState,
) -> Result<Vec<u8>, 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<E: Env>(
env: &mut E,
fn process_authenticate(
env: &mut impl Env,
challenge: [u8; 32],
application: [u8; 32],
key_handle: Vec<u8>,
flags: Ctap1Flags,
ctap_state: &mut CtapState<E>,
ctap_state: &mut CtapState,
) -> Result<Vec<u8>, 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(),
);
}

View File

@@ -28,13 +28,50 @@ use alloc::vec::Vec;
use arrayref::{array_ref, array_refs};
use libtock_drivers::timer::{ClockValue, Duration, Timestamp};
// CTAP specification (version 20190130) section 8.1
// TODO: Channel allocation, section 8.1.3?
// TODO: Transaction timeout, section 8.1.5.2
pub type HidPacket = [u8; 64];
pub type ChannelID = [u8; 4];
/// CTAPHID commands
///
/// See section 11.2.9. of FIDO 2.1 (2021-06-15).
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CtapHidCommand {
Ping = 0x01,
Msg = 0x03,
// Lock is optional and may be used in the future.
Lock = 0x04,
Init = 0x06,
Wink = 0x08,
Cbor = 0x10,
Cancel = 0x11,
Keepalive = 0x3B,
Error = 0x3F,
// VendorFirst and VendorLast describe a range, and are not commands themselves.
_VendorFirst = 0x40,
_VendorLast = 0x7F,
}
impl From<u8> for CtapHidCommand {
fn from(cmd: u8) -> Self {
match cmd {
x if x == CtapHidCommand::Ping as u8 => CtapHidCommand::Ping,
x if x == CtapHidCommand::Msg as u8 => CtapHidCommand::Msg,
x if x == CtapHidCommand::Lock as u8 => CtapHidCommand::Lock,
x if x == CtapHidCommand::Init as u8 => CtapHidCommand::Init,
x if x == CtapHidCommand::Wink as u8 => CtapHidCommand::Wink,
x if x == CtapHidCommand::Cbor as u8 => CtapHidCommand::Cbor,
x if x == CtapHidCommand::Cancel as u8 => CtapHidCommand::Cancel,
x if x == CtapHidCommand::Keepalive as u8 => CtapHidCommand::Keepalive,
// This includes the actual error code 0x3F. Error is not used for incoming packets in
// the specification, so we can safely reuse it for unknown bytes.
_ => CtapHidCommand::Error,
}
}
}
/// Describes the structure of a parsed HID packet.
///
/// A packet is either an Init or a Continuation packet.
pub enum ProcessedPacket<'a> {
InitPacket {
cmd: u8,
@@ -47,72 +84,79 @@ pub enum ProcessedPacket<'a> {
},
}
// An assembled CTAPHID command.
/// An assembled CTAPHID command.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Message {
// Channel ID.
pub cid: ChannelID,
// Command.
pub cmd: u8,
pub cmd: CtapHidCommand,
// Bytes of the message.
pub payload: Vec<u8>,
}
/// A keepalive packet reports the reason why a command does not finish.
#[allow(dead_code)]
pub enum KeepaliveStatus {
Processing = 0x01,
UpNeeded = 0x02,
}
/// Holds all state for receiving and sending HID packets.
///
/// This includes
/// - state from not fully processed messages,
/// - all allocated channels,
/// - information about requested winks.
///
/// The wink information can be polled to decide to i.e. blink LEDs.
///
/// To process a packet and receive the response, you can call `process_hid_packet`.
/// If you want more control, you can also do the processing in steps:
///
/// 1. `HidPacket` -> `Option<Message>`
/// 2. `Option<Message>` -> `Message`
/// 3. `Message` -> `Message`
/// 4. `Message` -> `HidPacketIterator`
///
/// These steps correspond to:
///
/// 1. `parse_packet` assembles the message and preprocesses all pure HID commands and errors.
/// 2. If you didn't receive any message or preprocessing discarded it, stop.
/// 3. `process_message` handles all protocol interactions.
/// 4. `split_message` creates packets out of the response message.
pub struct CtapHid {
assembler: MessageAssembler,
// The specification (version 20190130) only requires unique CIDs ; the allocation algorithm is
// vendor specific.
// The specification only requires unique CIDs, the allocation algorithm is vendor specific.
// We allocate them incrementally, that is all `cid` such that 1 <= cid <= allocated_cids are
// allocated.
// In packets, the ID encoding is Big Endian to match what is used throughout CTAP (with the
// u32::to/from_be_bytes methods).
// TODO(kaczmarczyck) We might want to limit or timeout open channels.
allocated_cids: usize,
pub(crate) wink_permission: TimedPermission,
}
#[allow(dead_code)]
pub enum KeepaliveStatus {
Processing,
UpNeeded,
}
#[allow(dead_code)]
// TODO(kaczmarczyck) disable the warning in the end
impl CtapHid {
// CTAP specification (version 20190130) section 8.1.3
// We implement CTAP 2.1 from 2021-06-15. Please see section
// 11.2. USB Human Interface Device (USB HID)
const CHANNEL_RESERVED: ChannelID = [0, 0, 0, 0];
const CHANNEL_BROADCAST: ChannelID = [0xFF, 0xFF, 0xFF, 0xFF];
const TYPE_INIT_BIT: u8 = 0x80;
const PACKET_TYPE_MASK: u8 = 0x80;
// CTAP specification (version 20190130) section 8.1.9
const COMMAND_PING: u8 = 0x01;
const COMMAND_MSG: u8 = 0x03;
const COMMAND_INIT: u8 = 0x06;
const COMMAND_CBOR: u8 = 0x10;
pub const COMMAND_CANCEL: u8 = 0x11;
const COMMAND_KEEPALIVE: u8 = 0x3B;
const COMMAND_ERROR: u8 = 0x3F;
// TODO: optional lock command
const COMMAND_LOCK: u8 = 0x04;
const COMMAND_WINK: u8 = 0x08;
const COMMAND_VENDOR_FIRST: u8 = 0x40;
const COMMAND_VENDOR_LAST: u8 = 0x7F;
// CTAP specification (version 20190130) section 8.1.9.1.6
const ERR_INVALID_CMD: u8 = 0x01;
const ERR_INVALID_PAR: u8 = 0x02;
const _ERR_INVALID_PAR: u8 = 0x02;
const ERR_INVALID_LEN: u8 = 0x03;
const ERR_INVALID_SEQ: u8 = 0x04;
const ERR_MSG_TIMEOUT: u8 = 0x05;
const ERR_CHANNEL_BUSY: u8 = 0x06;
const ERR_LOCK_REQUIRED: u8 = 0x0A;
const _ERR_LOCK_REQUIRED: u8 = 0x0A;
const ERR_INVALID_CHANNEL: u8 = 0x0B;
const ERR_OTHER: u8 = 0x7F;
const _ERR_OTHER: u8 = 0x7F;
// CTAP specification (version 20190130) section 8.1.9.1.3
// See section 11.2.9.1.3. CTAPHID_INIT (0x06).
const PROTOCOL_VERSION: u8 = 2;
// The device version number is vendor-defined.
const DEVICE_VERSION_MAJOR: u8 = 1;
const DEVICE_VERSION_MINOR: u8 = 0;
@@ -120,6 +164,7 @@ impl CtapHid {
const CAPABILITY_WINK: u8 = 0x01;
const CAPABILITY_CBOR: u8 = 0x04;
#[cfg(not(feature = "with_ctap1"))]
const CAPABILITY_NMSG: u8 = 0x08;
// Capabilitites currently supported by this device.
#[cfg(feature = "with_ctap1")]
@@ -132,6 +177,7 @@ impl CtapHid {
const TIMEOUT_DURATION: Duration<isize> = Duration::from_ms(100);
const WINK_TIMEOUT_DURATION: Duration<isize> = Duration::from_ms(5000);
/// Creates a new idle HID state.
pub fn new() -> CtapHid {
CtapHid {
assembler: MessageAssembler::new(),
@@ -140,197 +186,73 @@ 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<E: Env>(
/// Parses a packet, and preprocesses some messages and errors.
///
/// The preprocessed commands are:
/// - INIT
/// - CANCEL
/// - ERROR
/// - Unknown and unexpected commands like KEEPALIVE
/// - LOCK is not implemented and currently treated like an unknown command
///
/// Commands that may still be processed:
/// - PING
/// - MSG
/// - WINK
/// - CBOR
///
/// You may ignore PING, it's behaving correctly by default (input == output).
/// Ignoring the others is incorrect behavior. You have to at least replace them with an error
/// message:
/// `CtapHid::error_message(message.cid, CtapHid::ERR_INVALID_CMD)`
pub fn parse_packet(
&mut self,
env: &mut E,
env: &mut impl Env,
packet: &HidPacket,
clock_value: ClockValue,
ctap_state: &mut CtapState<E>,
) -> HidPacketIterator {
// TODO: Send COMMAND_KEEPALIVE every 100ms?
) -> Option<Message> {
match self
.assembler
.parse_packet(packet, Timestamp::<isize>::from_clock_value(clock_value))
{
Ok(Some(message)) => {
debug_ctap!(env, "Received message: {:02x?}", message);
let cid = message.cid;
if !self.has_valid_channel(&message) {
debug_ctap!(env, "Invalid channel: {:02x?}", cid);
return CtapHid::error_message(env, cid, CtapHid::ERR_INVALID_CHANNEL);
}
// If another command arrives, stop winking to prevent accidential button touches.
self.wink_permission = TimedPermission::waiting();
match message.cmd {
// CTAP specification (version 20190130) section 8.1.9.1.1
CtapHid::COMMAND_MSG => {
// If we don't have CTAP1 backward compatibilty, this command is invalid.
#[cfg(not(feature = "with_ctap1"))]
return CtapHid::error_message(env, cid, CtapHid::ERR_INVALID_CMD);
#[cfg(feature = "with_ctap1")]
match ctap1::Ctap1Command::process_command(
env,
&message.payload,
ctap_state,
clock_value,
) {
Ok(payload) => CtapHid::ctap1_success_message(env, cid, &payload),
Err(ctap1_status_code) => {
CtapHid::ctap1_error_message(env, cid, ctap1_status_code)
}
}
}
// CTAP specification (version 20190130) section 8.1.9.1.2
CtapHid::COMMAND_CBOR => {
// CTAP specification (version 20190130) section 8.1.5.1
// Each transaction is atomic, so we process the command directly here and
// don't handle any other packet in the meantime.
// TODO: Send keep-alive packets in the meantime.
let response =
ctap_state.process_command(env, &message.payload, cid, clock_value);
if let Some(iterator) = CtapHid::split_message(
env,
Message {
cid,
cmd: CtapHid::COMMAND_CBOR,
payload: response,
},
) {
iterator
} else {
// Handle the case of a payload > 7609 bytes.
// Although this shouldn't happen if the FIDO2 commands are implemented
// correctly, we reply with a vendor specific code instead of silently
// ignoring the error.
//
// The error payload that we send instead is 1 <= 7609 bytes, so it is
// safe to unwrap() the result.
CtapHid::split_message(
env,
Message {
cid,
cmd: CtapHid::COMMAND_CBOR,
payload: vec![
Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR as u8,
],
},
)
.unwrap()
}
}
// CTAP specification (version 20190130) section 8.1.9.1.3
CtapHid::COMMAND_INIT => {
if message.payload.len() != 8 {
return CtapHid::error_message(env, cid, CtapHid::ERR_INVALID_LEN);
}
let new_cid = if cid == CtapHid::CHANNEL_BROADCAST {
// TODO: Prevent allocating 2^32 channels.
self.allocated_cids += 1;
(self.allocated_cids as u32).to_be_bytes()
} else {
// Sync the channel and discard the current transaction.
cid
};
let mut payload = vec![0; 17];
payload[..8].copy_from_slice(&message.payload);
payload[8..12].copy_from_slice(&new_cid);
payload[12] = CtapHid::PROTOCOL_VERSION;
payload[13] = CtapHid::DEVICE_VERSION_MAJOR;
payload[14] = CtapHid::DEVICE_VERSION_MINOR;
payload[15] = CtapHid::DEVICE_VERSION_BUILD;
payload[16] = CtapHid::CAPABILITIES;
// This unwrap is safe because the payload length is 17 <= 7609 bytes.
CtapHid::split_message(
env,
Message {
cid,
cmd: CtapHid::COMMAND_INIT,
payload,
},
)
.unwrap()
}
// CTAP specification (version 20190130) section 8.1.9.1.4
CtapHid::COMMAND_PING => {
// Pong the same message.
// This unwrap is safe because if we could parse the incoming message, it's
// payload length must be <= 7609 bytes.
CtapHid::split_message(env, message).unwrap()
}
// CTAP specification (version 20190130) section 8.1.9.1.5
CtapHid::COMMAND_CANCEL => {
// Authenticators MUST NOT reply to this message.
// CANCEL is handled during user presence checks in main.
HidPacketIterator::none()
}
// Optional commands
// CTAP specification (version 20190130) section 8.1.9.2.1
CtapHid::COMMAND_WINK => {
if !message.payload.is_empty() {
return CtapHid::error_message(env, cid, CtapHid::ERR_INVALID_LEN);
}
self.wink_permission =
TimedPermission::granted(clock_value, CtapHid::WINK_TIMEOUT_DURATION);
CtapHid::split_message(
env,
Message {
cid,
cmd: CtapHid::COMMAND_WINK,
payload: vec![],
},
)
.unwrap()
}
// CTAP specification (version 20190130) section 8.1.9.2.2
// TODO: implement LOCK
_ => {
// Unknown or unsupported command.
CtapHid::error_message(env, cid, CtapHid::ERR_INVALID_CMD)
}
}
self.preprocess_message(message)
}
Ok(None) => {
// Waiting for more packets to assemble the message, nothing to send for now.
HidPacketIterator::none()
None
}
Err((cid, error)) => {
if !self.is_allocated_channel(cid)
&& error != receive::Error::UnexpectedContinuation
{
CtapHid::error_message(env, cid, CtapHid::ERR_INVALID_CHANNEL)
Some(CtapHid::error_message(cid, CtapHid::ERR_INVALID_CHANNEL))
} else {
match error {
receive::Error::UnexpectedChannel => {
CtapHid::error_message(env, cid, CtapHid::ERR_CHANNEL_BUSY)
Some(CtapHid::error_message(cid, CtapHid::ERR_CHANNEL_BUSY))
}
receive::Error::UnexpectedInit => {
// TODO: Should we send another error code in this case?
// Technically, we were expecting a sequence number and got another
// byte, although the command/seqnum bit has higher-level semantics
// than sequence numbers.
CtapHid::error_message(env, cid, CtapHid::ERR_INVALID_SEQ)
Some(CtapHid::error_message(cid, CtapHid::ERR_INVALID_SEQ))
}
receive::Error::UnexpectedContinuation => {
// CTAP specification (version 20190130) section 8.1.5.4
// Spurious continuation packets will be ignored.
HidPacketIterator::none()
None
}
receive::Error::UnexpectedSeq => {
CtapHid::error_message(env, cid, CtapHid::ERR_INVALID_SEQ)
Some(CtapHid::error_message(cid, CtapHid::ERR_INVALID_SEQ))
}
receive::Error::UnexpectedLen => {
CtapHid::error_message(env, cid, CtapHid::ERR_INVALID_LEN)
Some(CtapHid::error_message(cid, CtapHid::ERR_INVALID_LEN))
}
receive::Error::Timeout => {
CtapHid::error_message(env, cid, CtapHid::ERR_MSG_TIMEOUT)
Some(CtapHid::error_message(cid, CtapHid::ERR_MSG_TIMEOUT))
}
}
}
@@ -338,10 +260,151 @@ impl CtapHid {
}
}
/// Processes HID-only commands of a message and returns an outgoing message if necessary.
///
/// The preprocessed commands are:
/// - INIT
/// - CANCEL
/// - ERROR
/// - Unknown and unexpected commands like KEEPALIVE
/// - LOCK is not implemented and currently treated like an unknown command
fn preprocess_message(&mut self, message: Message) -> Option<Message> {
let cid = message.cid;
if !self.has_valid_channel(&message) {
return Some(CtapHid::error_message(cid, CtapHid::ERR_INVALID_CHANNEL));
}
match message.cmd {
CtapHidCommand::Msg => Some(message),
CtapHidCommand::Cbor => Some(message),
// CTAP 2.1 from 2021-06-15, section 11.2.9.1.3.
CtapHidCommand::Init => {
if message.payload.len() != 8 {
return Some(CtapHid::error_message(cid, CtapHid::ERR_INVALID_LEN));
}
let new_cid = if cid == CtapHid::CHANNEL_BROADCAST {
// TODO: Prevent allocating 2^32 channels.
self.allocated_cids += 1;
(self.allocated_cids as u32).to_be_bytes()
} else {
// Sync the channel and discard the current transaction.
cid
};
let mut payload = vec![0; 17];
payload[..8].copy_from_slice(&message.payload);
payload[8..12].copy_from_slice(&new_cid);
payload[12] = CtapHid::PROTOCOL_VERSION;
payload[13] = CtapHid::DEVICE_VERSION_MAJOR;
payload[14] = CtapHid::DEVICE_VERSION_MINOR;
payload[15] = CtapHid::DEVICE_VERSION_BUILD;
payload[16] = CtapHid::CAPABILITIES;
Some(Message {
cid,
cmd: CtapHidCommand::Init,
payload,
})
}
// CTAP 2.1 from 2021-06-15, section 11.2.9.1.4.
CtapHidCommand::Ping => {
// Pong the same message.
Some(message)
}
// CTAP 2.1 from 2021-06-15, section 11.2.9.1.5.
CtapHidCommand::Cancel => {
// Authenticators MUST NOT reply to this message.
// CANCEL is handled during user presence checks in main.
None
}
CtapHidCommand::Wink => Some(message),
_ => {
// Unknown or unsupported command.
Some(CtapHid::error_message(cid, CtapHid::ERR_INVALID_CMD))
}
}
}
/// Processes a message's commands that affect the protocol outside HID.
pub fn process_message(
&mut self,
env: &mut impl Env,
message: Message,
clock_value: ClockValue,
ctap_state: &mut CtapState,
) -> Message {
// If another command arrives, stop winking to prevent accidential button touches.
self.wink_permission = TimedPermission::waiting();
let cid = message.cid;
match message.cmd {
// CTAP 2.1 from 2021-06-15, section 11.2.9.1.1.
CtapHidCommand::Msg => {
// If we don't have CTAP1 backward compatibilty, this command is invalid.
#[cfg(not(feature = "with_ctap1"))]
return CtapHid::error_message(cid, CtapHid::ERR_INVALID_CMD);
#[cfg(feature = "with_ctap1")]
match ctap1::Ctap1Command::process_command(
env,
&message.payload,
ctap_state,
clock_value,
) {
Ok(payload) => CtapHid::ctap1_success_message(cid, &payload),
Err(ctap1_status_code) => CtapHid::ctap1_error_message(cid, ctap1_status_code),
}
}
// CTAP 2.1 from 2021-06-15, section 11.2.9.1.2.
CtapHidCommand::Cbor => {
// Each transaction is atomic, so we process the command directly here and
// don't handle any other packet in the meantime.
// TODO: Send "Processing" type keep-alive packets in the meantime.
let response = ctap_state.process_command(env, &message.payload, cid, clock_value);
Message {
cid,
cmd: CtapHidCommand::Cbor,
payload: response,
}
}
// CTAP 2.1 from 2021-06-15, section 11.2.9.2.1.
CtapHidCommand::Wink => {
if message.payload.is_empty() {
self.wink_permission =
TimedPermission::granted(clock_value, CtapHid::WINK_TIMEOUT_DURATION);
// The response is empty like the request.
message
} else {
CtapHid::error_message(cid, CtapHid::ERR_INVALID_LEN)
}
}
// All other commands have already been processed, keep them as is.
_ => message,
}
}
/// Processes an incoming USB HID packet, and returns an iterator for all outgoing packets.
pub fn process_hid_packet(
&mut self,
env: &mut impl Env,
packet: &HidPacket,
clock_value: ClockValue,
ctap_state: &mut CtapState,
) -> HidPacketIterator {
if let Some(message) = self.parse_packet(env, packet, clock_value) {
let processed_message = self.process_message(env, message, clock_value, ctap_state);
debug_ctap!(env, "Sending message: {:02x?}", processed_message);
CtapHid::split_message(processed_message)
} else {
HidPacketIterator::none()
}
}
fn has_valid_channel(&self, message: &Message) -> bool {
match message.cid {
// Only INIT commands use the broadcast channel.
CtapHid::CHANNEL_BROADCAST => message.cmd == CtapHid::COMMAND_INIT,
CtapHid::CHANNEL_BROADCAST => message.cmd == CtapHidCommand::Init,
// Check that the channel is allocated.
_ => self.is_allocated_channel(message.cid),
}
@@ -351,19 +414,15 @@ impl CtapHid {
cid != CtapHid::CHANNEL_RESERVED && u32::from_be_bytes(cid) as usize <= self.allocated_cids
}
fn error_message<E: Env>(env: &mut E, cid: ChannelID, error_code: u8) -> HidPacketIterator {
// This unwrap is safe because the payload length is 1 <= 7609 bytes.
CtapHid::split_message(
env,
Message {
cid,
cmd: CtapHid::COMMAND_ERROR,
payload: vec![error_code],
},
)
.unwrap()
fn error_message(cid: ChannelID, error_code: u8) -> Message {
Message {
cid,
cmd: CtapHidCommand::Error,
payload: vec![error_code],
}
}
/// Helper function to parse a raw packet.
pub fn process_single_packet(packet: &HidPacket) -> (&ChannelID, ProcessedPacket) {
let (cid, rest) = array_refs![packet, 4, 60];
if rest[0] & CtapHid::PACKET_TYPE_MASK != 0 {
@@ -388,73 +447,65 @@ impl CtapHid {
}
}
fn split_message<E: Env>(env: &mut E, message: Message) -> Option<HidPacketIterator> {
debug_ctap!(env, "Sending message: {:02x?}", message);
HidPacketIterator::new(message)
}
pub fn keepalive<E: Env>(
env: &mut E,
cid: ChannelID,
status: KeepaliveStatus,
) -> HidPacketIterator {
let status_code = match status {
KeepaliveStatus::Processing => 1,
KeepaliveStatus::UpNeeded => 2,
};
// This unwrap is safe because the payload length is 1 <= 7609 bytes.
CtapHid::split_message(
env,
Message {
/// Splits the message and unwraps the result.
///
/// Unwrapping handles the case of payload lengths > 7609 bytes. All responses are fixed
/// length, with the exception of:
/// - PING, but here output equals the (validated) input,
/// - CBOR, where long responses are conceivable.
///
/// Long CBOR responses should not happen, but we might not catch all edge cases, like for
/// example long user names that are part of the output of an assertion. These cases should be
/// correctly handled by the CTAP implementation. It is therefore an internal error from the
/// HID perspective.
fn split_message(message: Message) -> HidPacketIterator {
let cid = message.cid;
HidPacketIterator::new(message).unwrap_or_else(|| {
// The error payload is 1 <= 7609 bytes, so unwrap() is safe.
HidPacketIterator::new(Message {
cid,
cmd: CtapHid::COMMAND_KEEPALIVE,
payload: vec![status_code],
},
)
.unwrap()
cmd: CtapHidCommand::Cbor,
payload: vec![Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR as u8],
})
.unwrap()
})
}
/// Generates the HID response packets for a keepalive status.
pub fn keepalive(cid: ChannelID, status: KeepaliveStatus) -> HidPacketIterator {
// This unwrap is safe because the payload length is 1 <= 7609 bytes.
CtapHid::split_message(Message {
cid,
cmd: CtapHidCommand::Keepalive,
payload: vec![status as u8],
})
}
/// Returns whether a wink permission is currently granted.
pub fn should_wink(&self, now: ClockValue) -> bool {
self.wink_permission.is_granted(now)
}
#[cfg(feature = "with_ctap1")]
fn ctap1_error_message<E: Env>(
env: &mut E,
cid: ChannelID,
error_code: ctap1::Ctap1StatusCode,
) -> HidPacketIterator {
// This unwrap is safe because the payload length is 2 <= 7609 bytes
fn ctap1_error_message(cid: ChannelID, error_code: ctap1::Ctap1StatusCode) -> Message {
let code: u16 = error_code.into();
CtapHid::split_message(
env,
Message {
cid,
cmd: CtapHid::COMMAND_MSG,
payload: code.to_be_bytes().to_vec(),
},
)
.unwrap()
Message {
cid,
cmd: CtapHidCommand::Msg,
payload: code.to_be_bytes().to_vec(),
}
}
#[cfg(feature = "with_ctap1")]
fn ctap1_success_message<E: Env>(
env: &mut E,
cid: ChannelID,
payload: &[u8],
) -> HidPacketIterator {
fn ctap1_success_message(cid: ChannelID, payload: &[u8]) -> Message {
let mut response = payload.to_vec();
let code: u16 = ctap1::Ctap1StatusCode::SW_SUCCESS.into();
response.extend_from_slice(&code.to_be_bytes());
CtapHid::split_message(
env,
Message {
cid,
cmd: CtapHid::COMMAND_MSG,
payload: response,
},
)
.unwrap()
Message {
cid,
cmd: CtapHidCommand::Msg,
payload: response,
}
}
}
@@ -468,10 +519,10 @@ mod test {
const DUMMY_CLOCK_VALUE: ClockValue = ClockValue::new(0, CLOCK_FREQUENCY_HZ);
const DUMMY_TIMESTAMP: Timestamp<isize> = Timestamp::from_ms(0);
fn process_messages<E: Env>(
env: &mut E,
fn process_messages(
env: &mut TestEnv,
ctap_hid: &mut CtapHid,
ctap_state: &mut CtapState<E>,
ctap_state: &mut CtapState,
request: Vec<Message>,
) -> Option<Vec<Message>> {
let mut result = Vec::new();
@@ -492,10 +543,10 @@ mod test {
Some(result)
}
fn cid_from_init<E: Env>(
env: &mut E,
fn cid_from_init(
env: &mut TestEnv,
ctap_hid: &mut CtapHid,
ctap_state: &mut CtapState<E>,
ctap_state: &mut CtapState,
) -> ChannelID {
let nonce = vec![0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0];
let reply = process_messages(
@@ -504,7 +555,7 @@ mod test {
ctap_state,
vec![Message {
cid: CtapHid::CHANNEL_BROADCAST,
cmd: CtapHid::COMMAND_INIT,
cmd: CtapHidCommand::Init,
payload: nonce.clone(),
}],
);
@@ -526,7 +577,7 @@ mod test {
for payload_len in 0..7609 {
let message = Message {
cid: [0x12, 0x34, 0x56, 0x78],
cmd: 0x00,
cmd: CtapHidCommand::Cbor,
payload: vec![0xFF; payload_len],
};
@@ -578,7 +629,7 @@ mod test {
&mut ctap_state,
vec![Message {
cid: CtapHid::CHANNEL_BROADCAST,
cmd: CtapHid::COMMAND_INIT,
cmd: CtapHidCommand::Init,
payload: vec![0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0],
}],
);
@@ -587,7 +638,7 @@ mod test {
reply,
Some(vec![Message {
cid: CtapHid::CHANNEL_BROADCAST,
cmd: CtapHid::COMMAND_INIT,
cmd: CtapHidCommand::Init,
payload: vec![
0x12, // Nonce
0x34,
@@ -649,7 +700,7 @@ mod test {
result,
vec![Message {
cid,
cmd: CtapHid::COMMAND_INIT,
cmd: CtapHidCommand::Init,
payload: vec![
0x12, // Nonce
0x34,
@@ -686,7 +737,7 @@ mod test {
&mut ctap_state,
vec![Message {
cid,
cmd: CtapHid::COMMAND_PING,
cmd: CtapHidCommand::Ping,
payload: vec![0x99, 0x99],
}],
);
@@ -695,7 +746,7 @@ mod test {
reply,
Some(vec![Message {
cid,
cmd: CtapHid::COMMAND_PING,
cmd: CtapHidCommand::Ping,
payload: vec![0x99, 0x99]
}])
);

View File

@@ -13,7 +13,7 @@
// limitations under the License.
use super::super::customization::MAX_MSG_SIZE;
use super::{ChannelID, CtapHid, HidPacket, Message, ProcessedPacket};
use super::{ChannelID, CtapHid, CtapHidCommand, HidPacket, Message, ProcessedPacket};
use alloc::vec::Vec;
use core::mem::swap;
use libtock_drivers::timer::Timestamp;
@@ -131,7 +131,7 @@ impl MessageAssembler {
// Unexpected initialization packet.
ProcessedPacket::InitPacket { cmd, len, data } => {
self.reset();
if cmd == CtapHid::COMMAND_INIT {
if cmd == CtapHidCommand::Init as u8 {
self.parse_init_packet(*cid, cmd, len, data, timestamp)
} else {
Err((*cid, Error::UnexpectedInit))
@@ -189,7 +189,7 @@ impl MessageAssembler {
swap(&mut self.payload, &mut payload);
Some(Message {
cid: self.cid,
cmd: self.cmd,
cmd: CtapHidCommand::from(self.cmd),
payload,
})
}
@@ -225,12 +225,12 @@ mod test {
let mut assembler = MessageAssembler::new();
assert_eq!(
assembler.parse_packet(
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x80]),
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x90]),
DUMMY_TIMESTAMP
),
Ok(Some(Message {
cid: [0x12, 0x34, 0x56, 0x78],
cmd: 0x00,
cmd: CtapHidCommand::Cbor,
payload: vec![]
}))
);
@@ -241,12 +241,12 @@ mod test {
let mut assembler = MessageAssembler::new();
assert_eq!(
assembler.parse_packet(
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x80, 0x00, 0x10]),
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x90, 0x00, 0x10]),
DUMMY_TIMESTAMP
),
Ok(Some(Message {
cid: [0x12, 0x34, 0x56, 0x78],
cmd: 0x00,
cmd: CtapHidCommand::Cbor,
payload: vec![0x00; 0x10]
}))
);
@@ -260,12 +260,12 @@ mod test {
let mut assembler = MessageAssembler::new();
assert_eq!(
assembler.parse_packet(
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x80, 0x00, 0x10], 0xFF),
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x90, 0x00, 0x10], 0xFF),
DUMMY_TIMESTAMP
),
Ok(Some(Message {
cid: [0x12, 0x34, 0x56, 0x78],
cmd: 0x00,
cmd: CtapHidCommand::Cbor,
payload: vec![0xFF; 0x10]
}))
);
@@ -288,7 +288,7 @@ mod test {
),
Ok(Some(Message {
cid: [0x12, 0x34, 0x56, 0x78],
cmd: 0x01,
cmd: CtapHidCommand::Ping,
payload: vec![0x00; 0x40]
}))
);
@@ -318,7 +318,7 @@ mod test {
),
Ok(Some(Message {
cid: [0x12, 0x34, 0x56, 0x78],
cmd: 0x01,
cmd: CtapHidCommand::Ping,
payload: vec![0x00; 0x80]
}))
);
@@ -350,7 +350,7 @@ mod test {
),
Ok(Some(Message {
cid: [0x12, 0x34, 0x56, 0x78],
cmd: 0x01,
cmd: CtapHidCommand::Ping,
payload: vec![0x00; 0x1DB9]
}))
);
@@ -362,12 +362,15 @@ mod test {
let mut assembler = MessageAssembler::new();
for i in 0..10 {
// Introduce some variability in the messages.
let cmd = 2 * i;
let cmd = CtapHidCommand::from(i + 1);
let byte = 3 * i;
assert_eq!(
assembler.parse_packet(
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x80 | cmd, 0x00, 0x80], byte),
&byte_extend(
&[0x12, 0x34, 0x56, 0x78, 0x80 | cmd as u8, 0x00, 0x80],
byte
),
DUMMY_TIMESTAMP
),
Ok(None)
@@ -400,12 +403,12 @@ mod test {
for i in 0..10 {
// Introduce some variability in the messages.
let cid = 0x78 + i;
let cmd = 2 * i;
let cmd = CtapHidCommand::from(i + 1);
let byte = 3 * i;
assert_eq!(
assembler.parse_packet(
&byte_extend(&[0x12, 0x34, 0x56, cid, 0x80 | cmd, 0x00, 0x80], byte),
&byte_extend(&[0x12, 0x34, 0x56, cid, 0x80 | cmd as u8, 0x00, 0x80], byte),
DUMMY_TIMESTAMP
),
Ok(None)
@@ -443,11 +446,12 @@ mod test {
);
// Check that many sorts of packets on another channel are ignored.
for cmd in 0..=0xFF {
for i in 0..=0xFF {
let cmd = CtapHidCommand::from(i);
for byte in 0..=0xFF {
assert_eq!(
assembler.parse_packet(
&byte_extend(&[0x12, 0x34, 0x56, 0x9A, cmd, 0x00], byte),
&byte_extend(&[0x12, 0x34, 0x56, 0x9A, cmd as u8, 0x00], byte),
DUMMY_TIMESTAMP
),
Err(([0x12, 0x34, 0x56, 0x9A], Error::UnexpectedChannel))
@@ -462,7 +466,7 @@ mod test {
),
Ok(Some(Message {
cid: [0x12, 0x34, 0x56, 0x78],
cmd: 0x01,
cmd: CtapHidCommand::Ping,
payload: vec![0x00; 0x40]
}))
);
@@ -479,12 +483,12 @@ mod test {
let byte = 2 * i;
assert_eq!(
assembler.parse_packet(
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x80, 0x00, 0x10], byte),
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x10], byte),
DUMMY_TIMESTAMP
),
Ok(Some(Message {
cid: [0x12, 0x34, 0x56, 0x78],
cmd: 0x00,
cmd: CtapHidCommand::Ping,
payload: vec![byte; 0x10]
}))
);
@@ -584,7 +588,7 @@ mod test {
assembler.parse_packet(&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x7F]), timestamp),
Ok(Some(Message {
cid: [0x12, 0x34, 0x56, 0x78],
cmd: 0x01,
cmd: CtapHidCommand::Ping,
payload: vec![0x00; 0x1DB9]
}))
);
@@ -612,7 +616,7 @@ mod test {
),
Ok(Some(Message {
cid: [0x12, 0x34, 0x56, 0x78],
cmd: 0x06,
cmd: CtapHidCommand::Init,
payload: vec![0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]
}))
);

View File

@@ -14,6 +14,9 @@
use super::{CtapHid, HidPacket, Message};
/// Iterator for HID packets.
///
/// The `new` constructor splits the CTAP `Message` into `HidPacket`s for sending over USB.
pub struct HidPacketIterator(Option<MessageSplitter>);
impl HidPacketIterator {
@@ -99,7 +102,7 @@ impl Iterator for MessageSplitter {
match self.seq {
None => {
// First, send an initialization packet.
self.packet[4] = self.message.cmd | CtapHid::TYPE_INIT_BIT;
self.packet[4] = self.message.cmd as u8 | CtapHid::TYPE_INIT_BIT;
self.packet[5] = (payload_len >> 8) as u8;
self.packet[6] = payload_len as u8;
@@ -128,6 +131,7 @@ impl Iterator for MessageSplitter {
#[cfg(test)]
mod test {
use super::super::CtapHidCommand;
use super::*;
fn assert_packet_output_equality(message: Message, expected_packets: Vec<HidPacket>) {
@@ -142,11 +146,11 @@ mod test {
fn test_hid_packet_iterator_single_packet() {
let message = Message {
cid: [0x12, 0x34, 0x56, 0x78],
cmd: 0x4C,
cmd: CtapHidCommand::Cbor,
payload: vec![0xAA, 0xBB],
};
let expected_packets: Vec<HidPacket> = vec![[
0x12, 0x34, 0x56, 0x78, 0xCC, 0x00, 0x02, 0xAA, 0xBB, 0x00, 0x00, 0x00, 0x00, 0x00,
0x12, 0x34, 0x56, 0x78, 0x90, 0x00, 0x02, 0xAA, 0xBB, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -159,11 +163,11 @@ mod test {
fn test_hid_packet_iterator_big_single_packet() {
let message = Message {
cid: [0x12, 0x34, 0x56, 0x78],
cmd: 0x4C,
cmd: CtapHidCommand::Cbor,
payload: vec![0xAA; 64 - 7],
};
let expected_packets: Vec<HidPacket> = vec![[
0x12, 0x34, 0x56, 0x78, 0xCC, 0x00, 0x39, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0x12, 0x34, 0x56, 0x78, 0x90, 0x00, 0x39, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
@@ -176,12 +180,12 @@ mod test {
fn test_hid_packet_iterator_two_packets() {
let message = Message {
cid: [0x12, 0x34, 0x56, 0x78],
cmd: 0x4C,
cmd: CtapHidCommand::Cbor,
payload: vec![0xAA; 64 - 7 + 1],
};
let expected_packets: Vec<HidPacket> = vec![
[
0x12, 0x34, 0x56, 0x78, 0xCC, 0x00, 0x3A, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0x12, 0x34, 0x56, 0x78, 0x90, 0x00, 0x3A, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
@@ -204,12 +208,12 @@ mod test {
payload.extend(vec![0xBB; 64 - 5]);
let message = Message {
cid: [0x12, 0x34, 0x56, 0x78],
cmd: 0x4C,
cmd: CtapHidCommand::Cbor,
payload,
};
let expected_packets: Vec<HidPacket> = vec![
[
0x12, 0x34, 0x56, 0x78, 0xCC, 0x00, 0x74, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0x12, 0x34, 0x56, 0x78, 0x90, 0x00, 0x74, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
@@ -238,12 +242,12 @@ mod test {
let message = Message {
cid: [0x12, 0x34, 0x56, 0x78],
cmd: 0xAB,
cmd: CtapHidCommand::Msg,
payload,
};
let mut expected_packets: Vec<HidPacket> = vec![[
0x12, 0x34, 0x56, 0x78, 0xAB, 0x1D, 0xB9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x12, 0x34, 0x56, 0x78, 0x83, 0x1D, 0xB9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
@@ -271,7 +275,7 @@ mod test {
assert_eq!(payload.len(), 0x1dba);
let message = Message {
cid: [0x12, 0x34, 0x56, 0x78],
cmd: 0xAB,
cmd: CtapHidCommand::Msg,
payload,
};
assert!(HidPacketIterator::new(message).is_none());
@@ -283,7 +287,7 @@ mod test {
let payload = vec![0xFF; 0x10000];
let message = Message {
cid: [0x12, 0x34, 0x56, 0x78],
cmd: 0xAB,
cmd: CtapHidCommand::Msg,
payload,
};
assert!(HidPacketIterator::new(message).is_none());

View File

@@ -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<E: Env>(
pub fn process_command(
&mut self,
persistent_store: &mut PersistentStore<E>,
env: &mut impl Env,
client_pin: &mut ClientPin,
large_blobs_params: AuthenticatorLargeBlobsParameters,
) -> Result<ResponseData, Ctap2StatusCode> {
@@ -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))

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff