Merge pull request #438 from ia0/env_store
Add Store and UpgradeStorage to Env
This commit is contained in:
@@ -17,7 +17,7 @@
|
|||||||
extern crate lang_items;
|
extern crate lang_items;
|
||||||
|
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
use ctap2::embedded_flash::new_storage;
|
use ctap2::env::tock::steal_storage;
|
||||||
use libtock_drivers::console::Console;
|
use libtock_drivers::console::Console;
|
||||||
use libtock_drivers::led;
|
use libtock_drivers::led;
|
||||||
use libtock_drivers::result::FlexUnwrap;
|
use libtock_drivers::result::FlexUnwrap;
|
||||||
@@ -37,7 +37,7 @@ fn is_page_erased(storage: &dyn Storage, page: usize) -> bool {
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
led::get(1).flex_unwrap().on().flex_unwrap(); // red on dongle
|
led::get(1).flex_unwrap().on().flex_unwrap(); // red on dongle
|
||||||
let mut storage = new_storage().unwrap();
|
let mut storage = unsafe { steal_storage() }.unwrap();
|
||||||
let num_pages = storage.num_pages();
|
let num_pages = storage.num_pages();
|
||||||
writeln!(Console::new(), "Erase {} pages of storage:", num_pages).unwrap();
|
writeln!(Console::new(), "Erase {} pages of storage:", num_pages).unwrap();
|
||||||
for page in 0..num_pages {
|
for page in 0..num_pages {
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ use alloc::string::{String, ToString};
|
|||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use alloc::{format, vec};
|
use alloc::{format, vec};
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
use ctap2::embedded_flash::{new_storage, Storage};
|
use ctap2::env::tock::{steal_storage, TockEnv};
|
||||||
|
use ctap2::env::Env;
|
||||||
use libtock_drivers::console::Console;
|
use libtock_drivers::console::Console;
|
||||||
use libtock_drivers::timer::{self, Duration, Timer, Timestamp};
|
use libtock_drivers::timer::{self, Duration, Timer, Timestamp};
|
||||||
use persistent_store::Store;
|
use persistent_store::Store;
|
||||||
@@ -40,9 +41,9 @@ fn measure<T>(timer: &Timer, operation: impl FnOnce() -> T) -> (T, Duration<f64>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Only use one store at a time.
|
// Only use one store at a time.
|
||||||
unsafe fn boot_store(erase: bool) -> Store<Storage> {
|
unsafe fn boot_store(erase: bool) -> Store<<TockEnv as Env>::Storage> {
|
||||||
use persistent_store::Storage;
|
use persistent_store::Storage;
|
||||||
let mut storage = new_storage().unwrap();
|
let mut storage = steal_storage().unwrap();
|
||||||
let num_pages = storage.num_pages();
|
let num_pages = storage.num_pages();
|
||||||
if erase {
|
if erase {
|
||||||
for page in 0..num_pages {
|
for page in 0..num_pages {
|
||||||
@@ -59,7 +60,7 @@ struct StorageConfig {
|
|||||||
|
|
||||||
fn storage_config() -> StorageConfig {
|
fn storage_config() -> StorageConfig {
|
||||||
use persistent_store::Storage;
|
use persistent_store::Storage;
|
||||||
let storage = new_storage().unwrap();
|
let storage = unsafe { steal_storage() }.unwrap();
|
||||||
StorageConfig {
|
StorageConfig {
|
||||||
num_pages: storage.num_pages(),
|
num_pages: storage.num_pages(),
|
||||||
}
|
}
|
||||||
|
|||||||
6
src/api/mod.rs
Normal file
6
src/api/mod.rs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
//! APIs for the environment.
|
||||||
|
//!
|
||||||
|
//! The [environment](crate::env::Env) is split into components. Each component has an API described
|
||||||
|
//! by a trait. This module gathers the API of those components.
|
||||||
|
|
||||||
|
pub mod upgrade_storage;
|
||||||
@@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
use persistent_store::StorageResult;
|
use persistent_store::StorageResult;
|
||||||
|
|
||||||
|
pub(crate) mod helper;
|
||||||
|
|
||||||
/// Accessors to storage locations used for upgrading from a CTAP command.
|
/// Accessors to storage locations used for upgrading from a CTAP command.
|
||||||
pub trait UpgradeStorage {
|
pub trait UpgradeStorage {
|
||||||
/// Reads a slice of the partition, if within bounds.
|
/// Reads a slice of the partition, if within bounds.
|
||||||
@@ -19,8 +19,9 @@ use super::data_formats::{
|
|||||||
use super::pin_protocol::{verify_pin_uv_auth_token, PinProtocol, SharedSecret};
|
use super::pin_protocol::{verify_pin_uv_auth_token, PinProtocol, SharedSecret};
|
||||||
use super::response::{AuthenticatorClientPinResponse, ResponseData};
|
use super::response::{AuthenticatorClientPinResponse, ResponseData};
|
||||||
use super::status_code::Ctap2StatusCode;
|
use super::status_code::Ctap2StatusCode;
|
||||||
use super::storage::PersistentStore;
|
|
||||||
use super::token_state::PinUvAuthTokenState;
|
use super::token_state::PinUvAuthTokenState;
|
||||||
|
use crate::ctap::storage;
|
||||||
|
use crate::env::Env;
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use alloc::str;
|
use alloc::str;
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
@@ -76,12 +77,12 @@ fn decrypt_pin(
|
|||||||
/// padding. Next, it is checked against the PIN policy. Last, it is hashed and
|
/// padding. Next, it is checked against the PIN policy. Last, it is hashed and
|
||||||
/// truncated for persistent storage.
|
/// truncated for persistent storage.
|
||||||
fn check_and_store_new_pin(
|
fn check_and_store_new_pin(
|
||||||
persistent_store: &mut PersistentStore,
|
env: &mut impl Env,
|
||||||
shared_secret: &dyn SharedSecret,
|
shared_secret: &dyn SharedSecret,
|
||||||
new_pin_enc: Vec<u8>,
|
new_pin_enc: Vec<u8>,
|
||||||
) -> Result<(), Ctap2StatusCode> {
|
) -> Result<(), Ctap2StatusCode> {
|
||||||
let pin = decrypt_pin(shared_secret, new_pin_enc)?;
|
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();
|
let pin_length = str::from_utf8(&pin).unwrap_or("").chars().count();
|
||||||
if pin_length < min_pin_length || pin.len() == PIN_PADDED_LENGTH {
|
if pin_length < min_pin_length || pin.len() == PIN_PADDED_LENGTH {
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_POLICY_VIOLATION);
|
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_POLICY_VIOLATION);
|
||||||
@@ -89,7 +90,7 @@ fn check_and_store_new_pin(
|
|||||||
let mut pin_hash = [0u8; PIN_AUTH_LENGTH];
|
let mut pin_hash = [0u8; PIN_AUTH_LENGTH];
|
||||||
pin_hash.copy_from_slice(&Sha256::hash(&pin[..])[..PIN_AUTH_LENGTH]);
|
pin_hash.copy_from_slice(&Sha256::hash(&pin[..])[..PIN_AUTH_LENGTH]);
|
||||||
// The PIN length is always < PIN_PADDED_LENGTH < 256.
|
// 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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,26 +158,25 @@ impl ClientPin {
|
|||||||
/// Also, in case of failure, the key agreement key is randomly reset.
|
/// Also, in case of failure, the key agreement key is randomly reset.
|
||||||
fn verify_pin_hash_enc(
|
fn verify_pin_hash_enc(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut impl Rng256,
|
env: &mut impl Env,
|
||||||
persistent_store: &mut PersistentStore,
|
|
||||||
pin_uv_auth_protocol: PinUvAuthProtocol,
|
pin_uv_auth_protocol: PinUvAuthProtocol,
|
||||||
shared_secret: &dyn SharedSecret,
|
shared_secret: &dyn SharedSecret,
|
||||||
pin_hash_enc: Vec<u8>,
|
pin_hash_enc: Vec<u8>,
|
||||||
) -> Result<(), Ctap2StatusCode> {
|
) -> Result<(), Ctap2StatusCode> {
|
||||||
match persistent_store.pin_hash()? {
|
match storage::pin_hash(env)? {
|
||||||
Some(pin_hash) => {
|
Some(pin_hash) => {
|
||||||
if self.consecutive_pin_mismatches >= 3 {
|
if self.consecutive_pin_mismatches >= 3 {
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_BLOCKED);
|
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_BLOCKED);
|
||||||
}
|
}
|
||||||
persistent_store.decr_pin_retries()?;
|
storage::decr_pin_retries(env)?;
|
||||||
let pin_hash_dec = shared_secret
|
let pin_hash_dec = shared_secret
|
||||||
.decrypt(&pin_hash_enc)
|
.decrypt(&pin_hash_enc)
|
||||||
.map_err(|_| Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)?;
|
.map_err(|_| Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)?;
|
||||||
|
|
||||||
if !bool::from(pin_hash.ct_eq(&pin_hash_dec)) {
|
if !bool::from(pin_hash.ct_eq(&pin_hash_dec)) {
|
||||||
self.get_mut_pin_protocol(pin_uv_auth_protocol)
|
self.get_mut_pin_protocol(pin_uv_auth_protocol)
|
||||||
.regenerate(rng);
|
.regenerate(env.rng());
|
||||||
if persistent_store.pin_retries()? == 0 {
|
if storage::pin_retries(env)? == 0 {
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_BLOCKED);
|
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_BLOCKED);
|
||||||
}
|
}
|
||||||
self.consecutive_pin_mismatches += 1;
|
self.consecutive_pin_mismatches += 1;
|
||||||
@@ -189,19 +189,19 @@ impl ClientPin {
|
|||||||
// This status code is not explicitly mentioned in the specification.
|
// This status code is not explicitly mentioned in the specification.
|
||||||
None => return Err(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED),
|
None => return Err(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED),
|
||||||
}
|
}
|
||||||
persistent_store.reset_pin_retries()?;
|
storage::reset_pin_retries(env)?;
|
||||||
self.consecutive_pin_mismatches = 0;
|
self.consecutive_pin_mismatches = 0;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_get_pin_retries(
|
fn process_get_pin_retries(
|
||||||
&self,
|
&self,
|
||||||
persistent_store: &PersistentStore,
|
env: &mut impl Env,
|
||||||
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
|
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
|
||||||
Ok(AuthenticatorClientPinResponse {
|
Ok(AuthenticatorClientPinResponse {
|
||||||
key_agreement: None,
|
key_agreement: None,
|
||||||
pin_uv_auth_token: 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),
|
power_cycle_state: Some(self.consecutive_pin_mismatches >= 3),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -224,7 +224,7 @@ impl ClientPin {
|
|||||||
|
|
||||||
fn process_set_pin(
|
fn process_set_pin(
|
||||||
&mut self,
|
&mut self,
|
||||||
persistent_store: &mut PersistentStore,
|
env: &mut impl Env,
|
||||||
client_pin_params: AuthenticatorClientPinParameters,
|
client_pin_params: AuthenticatorClientPinParameters,
|
||||||
) -> Result<(), Ctap2StatusCode> {
|
) -> Result<(), Ctap2StatusCode> {
|
||||||
let AuthenticatorClientPinParameters {
|
let AuthenticatorClientPinParameters {
|
||||||
@@ -238,21 +238,20 @@ impl ClientPin {
|
|||||||
let pin_uv_auth_param = ok_or_missing(pin_uv_auth_param)?;
|
let pin_uv_auth_param = ok_or_missing(pin_uv_auth_param)?;
|
||||||
let new_pin_enc = ok_or_missing(new_pin_enc)?;
|
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);
|
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID);
|
||||||
}
|
}
|
||||||
let shared_secret = self.get_shared_secret(pin_uv_auth_protocol, key_agreement)?;
|
let shared_secret = self.get_shared_secret(pin_uv_auth_protocol, key_agreement)?;
|
||||||
shared_secret.verify(&new_pin_enc, &pin_uv_auth_param)?;
|
shared_secret.verify(&new_pin_enc, &pin_uv_auth_param)?;
|
||||||
|
|
||||||
check_and_store_new_pin(persistent_store, shared_secret.as_ref(), new_pin_enc)?;
|
check_and_store_new_pin(env, shared_secret.as_ref(), new_pin_enc)?;
|
||||||
persistent_store.reset_pin_retries()?;
|
storage::reset_pin_retries(env)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_change_pin(
|
fn process_change_pin(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut impl Rng256,
|
env: &mut impl Env,
|
||||||
persistent_store: &mut PersistentStore,
|
|
||||||
client_pin_params: AuthenticatorClientPinParameters,
|
client_pin_params: AuthenticatorClientPinParameters,
|
||||||
) -> Result<(), Ctap2StatusCode> {
|
) -> Result<(), Ctap2StatusCode> {
|
||||||
let AuthenticatorClientPinParameters {
|
let AuthenticatorClientPinParameters {
|
||||||
@@ -268,7 +267,7 @@ impl ClientPin {
|
|||||||
let new_pin_enc = ok_or_missing(new_pin_enc)?;
|
let new_pin_enc = ok_or_missing(new_pin_enc)?;
|
||||||
let pin_hash_enc = ok_or_missing(pin_hash_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);
|
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_BLOCKED);
|
||||||
}
|
}
|
||||||
let shared_secret = self.get_shared_secret(pin_uv_auth_protocol, key_agreement)?;
|
let shared_secret = self.get_shared_secret(pin_uv_auth_protocol, key_agreement)?;
|
||||||
@@ -276,23 +275,21 @@ impl ClientPin {
|
|||||||
auth_param_data.extend(&pin_hash_enc);
|
auth_param_data.extend(&pin_hash_enc);
|
||||||
shared_secret.verify(&auth_param_data, &pin_uv_auth_param)?;
|
shared_secret.verify(&auth_param_data, &pin_uv_auth_param)?;
|
||||||
self.verify_pin_hash_enc(
|
self.verify_pin_hash_enc(
|
||||||
rng,
|
env,
|
||||||
persistent_store,
|
|
||||||
pin_uv_auth_protocol,
|
pin_uv_auth_protocol,
|
||||||
shared_secret.as_ref(),
|
shared_secret.as_ref(),
|
||||||
pin_hash_enc,
|
pin_hash_enc,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
check_and_store_new_pin(persistent_store, shared_secret.as_ref(), new_pin_enc)?;
|
check_and_store_new_pin(env, shared_secret.as_ref(), new_pin_enc)?;
|
||||||
self.pin_protocol_v1.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(rng);
|
self.pin_protocol_v2.reset_pin_uv_auth_token(env.rng());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_get_pin_token(
|
fn process_get_pin_token(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut impl Rng256,
|
env: &mut impl Env,
|
||||||
persistent_store: &mut PersistentStore,
|
|
||||||
client_pin_params: AuthenticatorClientPinParameters,
|
client_pin_params: AuthenticatorClientPinParameters,
|
||||||
now: ClockValue,
|
now: ClockValue,
|
||||||
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
|
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
|
||||||
@@ -310,28 +307,27 @@ impl ClientPin {
|
|||||||
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
|
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);
|
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_BLOCKED);
|
||||||
}
|
}
|
||||||
let shared_secret = self.get_shared_secret(pin_uv_auth_protocol, key_agreement)?;
|
let shared_secret = self.get_shared_secret(pin_uv_auth_protocol, key_agreement)?;
|
||||||
self.verify_pin_hash_enc(
|
self.verify_pin_hash_enc(
|
||||||
rng,
|
env,
|
||||||
persistent_store,
|
|
||||||
pin_uv_auth_protocol,
|
pin_uv_auth_protocol,
|
||||||
shared_secret.as_ref(),
|
shared_secret.as_ref(),
|
||||||
pin_hash_enc,
|
pin_hash_enc,
|
||||||
)?;
|
)?;
|
||||||
if persistent_store.has_force_pin_change()? {
|
if storage::has_force_pin_change(env)? {
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID);
|
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.pin_protocol_v1.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(rng);
|
self.pin_protocol_v2.reset_pin_uv_auth_token(env.rng());
|
||||||
self.pin_uv_auth_token_state
|
self.pin_uv_auth_token_state
|
||||||
.begin_using_pin_uv_auth_token(now);
|
.begin_using_pin_uv_auth_token(now);
|
||||||
self.pin_uv_auth_token_state.set_default_permissions();
|
self.pin_uv_auth_token_state.set_default_permissions();
|
||||||
let pin_uv_auth_token = shared_secret.encrypt(
|
let pin_uv_auth_token = shared_secret.encrypt(
|
||||||
rng,
|
env.rng(),
|
||||||
self.get_pin_protocol(pin_uv_auth_protocol)
|
self.get_pin_protocol(pin_uv_auth_protocol)
|
||||||
.get_pin_uv_auth_token(),
|
.get_pin_uv_auth_token(),
|
||||||
)?;
|
)?;
|
||||||
@@ -361,8 +357,7 @@ impl ClientPin {
|
|||||||
|
|
||||||
fn process_get_pin_uv_auth_token_using_pin_with_permissions(
|
fn process_get_pin_uv_auth_token_using_pin_with_permissions(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut impl Rng256,
|
env: &mut impl Env,
|
||||||
persistent_store: &mut PersistentStore,
|
|
||||||
mut client_pin_params: AuthenticatorClientPinParameters,
|
mut client_pin_params: AuthenticatorClientPinParameters,
|
||||||
now: ClockValue,
|
now: ClockValue,
|
||||||
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
|
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
|
||||||
@@ -380,7 +375,7 @@ impl ClientPin {
|
|||||||
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
|
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(permissions);
|
||||||
self.pin_uv_auth_token_state
|
self.pin_uv_auth_token_state
|
||||||
.set_permissions_rp_id(permissions_rp_id);
|
.set_permissions_rp_id(permissions_rp_id);
|
||||||
@@ -391,28 +386,25 @@ impl ClientPin {
|
|||||||
/// Processes the authenticatorClientPin command.
|
/// Processes the authenticatorClientPin command.
|
||||||
pub fn process_command(
|
pub fn process_command(
|
||||||
&mut self,
|
&mut self,
|
||||||
rng: &mut impl Rng256,
|
env: &mut impl Env,
|
||||||
persistent_store: &mut PersistentStore,
|
|
||||||
client_pin_params: AuthenticatorClientPinParameters,
|
client_pin_params: AuthenticatorClientPinParameters,
|
||||||
now: ClockValue,
|
now: ClockValue,
|
||||||
) -> Result<ResponseData, Ctap2StatusCode> {
|
) -> Result<ResponseData, Ctap2StatusCode> {
|
||||||
let response = match client_pin_params.sub_command {
|
let response = match client_pin_params.sub_command {
|
||||||
ClientPinSubCommand::GetPinRetries => {
|
ClientPinSubCommand::GetPinRetries => Some(self.process_get_pin_retries(env)?),
|
||||||
Some(self.process_get_pin_retries(persistent_store)?)
|
|
||||||
}
|
|
||||||
ClientPinSubCommand::GetKeyAgreement => {
|
ClientPinSubCommand::GetKeyAgreement => {
|
||||||
Some(self.process_get_key_agreement(client_pin_params)?)
|
Some(self.process_get_key_agreement(client_pin_params)?)
|
||||||
}
|
}
|
||||||
ClientPinSubCommand::SetPin => {
|
ClientPinSubCommand::SetPin => {
|
||||||
self.process_set_pin(persistent_store, client_pin_params)?;
|
self.process_set_pin(env, client_pin_params)?;
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
ClientPinSubCommand::ChangePin => {
|
ClientPinSubCommand::ChangePin => {
|
||||||
self.process_change_pin(rng, persistent_store, client_pin_params)?;
|
self.process_change_pin(env, client_pin_params)?;
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
ClientPinSubCommand::GetPinToken => {
|
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(
|
ClientPinSubCommand::GetPinUvAuthTokenUsingUvWithPermissions => Some(
|
||||||
self.process_get_pin_uv_auth_token_using_uv_with_permissions(client_pin_params)?,
|
self.process_get_pin_uv_auth_token_using_uv_with_permissions(client_pin_params)?,
|
||||||
@@ -420,8 +412,7 @@ impl ClientPin {
|
|||||||
ClientPinSubCommand::GetUvRetries => Some(self.process_get_uv_retries()?),
|
ClientPinSubCommand::GetUvRetries => Some(self.process_get_uv_retries()?),
|
||||||
ClientPinSubCommand::GetPinUvAuthTokenUsingPinWithPermissions => Some(
|
ClientPinSubCommand::GetPinUvAuthTokenUsingPinWithPermissions => Some(
|
||||||
self.process_get_pin_uv_auth_token_using_pin_with_permissions(
|
self.process_get_pin_uv_auth_token_using_pin_with_permissions(
|
||||||
rng,
|
env,
|
||||||
persistent_store,
|
|
||||||
client_pin_params,
|
client_pin_params,
|
||||||
now,
|
now,
|
||||||
)?,
|
)?,
|
||||||
@@ -570,11 +561,10 @@ impl ClientPin {
|
|||||||
pin_uv_auth_token: [u8; PIN_TOKEN_LENGTH],
|
pin_uv_auth_token: [u8; PIN_TOKEN_LENGTH],
|
||||||
pin_uv_auth_protocol: PinUvAuthProtocol,
|
pin_uv_auth_protocol: PinUvAuthProtocol,
|
||||||
) -> ClientPin {
|
) -> ClientPin {
|
||||||
use crypto::rng256::ThreadRng256;
|
let mut env = crate::env::test::TestEnv::new();
|
||||||
let mut rng = ThreadRng256 {};
|
|
||||||
let (key_agreement_key_v1, key_agreement_key_v2) = match pin_uv_auth_protocol {
|
let (key_agreement_key_v1, key_agreement_key_v2) = match pin_uv_auth_protocol {
|
||||||
PinUvAuthProtocol::V1 => (key_agreement_key, crypto::ecdh::SecKey::gensk(&mut rng)),
|
PinUvAuthProtocol::V1 => (key_agreement_key, crypto::ecdh::SecKey::gensk(env.rng())),
|
||||||
PinUvAuthProtocol::V2 => (crypto::ecdh::SecKey::gensk(&mut rng), key_agreement_key),
|
PinUvAuthProtocol::V2 => (crypto::ecdh::SecKey::gensk(env.rng()), key_agreement_key),
|
||||||
};
|
};
|
||||||
let mut pin_uv_auth_token_state = PinUvAuthTokenState::new();
|
let mut pin_uv_auth_token_state = PinUvAuthTokenState::new();
|
||||||
pin_uv_auth_token_state.set_permissions(0xFF);
|
pin_uv_auth_token_state.set_permissions(0xFF);
|
||||||
@@ -594,29 +584,29 @@ impl ClientPin {
|
|||||||
mod test {
|
mod test {
|
||||||
use super::super::pin_protocol::authenticate_pin_uv_auth_token;
|
use super::super::pin_protocol::authenticate_pin_uv_auth_token;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::env::test::TestEnv;
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
use crypto::rng256::ThreadRng256;
|
|
||||||
use libtock_drivers::timer::Duration;
|
use libtock_drivers::timer::Duration;
|
||||||
|
|
||||||
const CLOCK_FREQUENCY_HZ: usize = 32768;
|
const CLOCK_FREQUENCY_HZ: usize = 32768;
|
||||||
const DUMMY_CLOCK_VALUE: ClockValue = ClockValue::new(0, CLOCK_FREQUENCY_HZ);
|
const DUMMY_CLOCK_VALUE: ClockValue = ClockValue::new(0, CLOCK_FREQUENCY_HZ);
|
||||||
|
|
||||||
/// Stores a PIN hash corresponding to the dummy PIN "1234".
|
/// Stores a PIN hash corresponding to the dummy PIN "1234".
|
||||||
fn set_standard_pin(persistent_store: &mut PersistentStore) {
|
fn set_standard_pin(env: &mut TestEnv) {
|
||||||
let mut pin = [0u8; 64];
|
let mut pin = [0u8; 64];
|
||||||
pin[..4].copy_from_slice(b"1234");
|
pin[..4].copy_from_slice(b"1234");
|
||||||
let mut pin_hash = [0u8; 16];
|
let mut pin_hash = [0u8; 16];
|
||||||
pin_hash.copy_from_slice(&Sha256::hash(&pin[..])[..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.
|
/// Fails on PINs bigger than 64 bytes.
|
||||||
fn encrypt_pin(shared_secret: &dyn SharedSecret, pin: Vec<u8>) -> Vec<u8> {
|
fn encrypt_pin(shared_secret: &dyn SharedSecret, pin: Vec<u8>) -> Vec<u8> {
|
||||||
assert!(pin.len() <= 64);
|
assert!(pin.len() <= 64);
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut padded_pin = [0u8; 64];
|
let mut padded_pin = [0u8; 64];
|
||||||
padded_pin[..pin.len()].copy_from_slice(&pin[..]);
|
padded_pin[..pin.len()].copy_from_slice(&pin[..]);
|
||||||
shared_secret.encrypt(&mut rng, &padded_pin).unwrap()
|
shared_secret.encrypt(env.rng(), &padded_pin).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a ClientPin instance and a shared secret for testing.
|
/// Generates a ClientPin instance and a shared secret for testing.
|
||||||
@@ -628,8 +618,8 @@ mod test {
|
|||||||
fn create_client_pin_and_shared_secret(
|
fn create_client_pin_and_shared_secret(
|
||||||
pin_uv_auth_protocol: PinUvAuthProtocol,
|
pin_uv_auth_protocol: PinUvAuthProtocol,
|
||||||
) -> (ClientPin, Box<dyn SharedSecret>) {
|
) -> (ClientPin, Box<dyn SharedSecret>) {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pk = key_agreement_key.genpk();
|
let pk = key_agreement_key.genpk();
|
||||||
let key_agreement = CoseKey::from(pk);
|
let key_agreement = CoseKey::from(pk);
|
||||||
let pin_uv_auth_token = [0x91; PIN_TOKEN_LENGTH];
|
let pin_uv_auth_token = [0x91; PIN_TOKEN_LENGTH];
|
||||||
@@ -649,7 +639,7 @@ mod test {
|
|||||||
pin_uv_auth_protocol: PinUvAuthProtocol,
|
pin_uv_auth_protocol: PinUvAuthProtocol,
|
||||||
sub_command: ClientPinSubCommand,
|
sub_command: ClientPinSubCommand,
|
||||||
) -> (ClientPin, AuthenticatorClientPinParameters) {
|
) -> (ClientPin, AuthenticatorClientPinParameters) {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let (client_pin, shared_secret) = create_client_pin_and_shared_secret(pin_uv_auth_protocol);
|
let (client_pin, shared_secret) = create_client_pin_and_shared_secret(pin_uv_auth_protocol);
|
||||||
|
|
||||||
let pin = b"1234";
|
let pin = b"1234";
|
||||||
@@ -658,12 +648,12 @@ mod test {
|
|||||||
let pin_hash = Sha256::hash(&padded_pin);
|
let pin_hash = Sha256::hash(&padded_pin);
|
||||||
let new_pin_enc = shared_secret
|
let new_pin_enc = shared_secret
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.encrypt(&mut rng, &padded_pin)
|
.encrypt(env.rng(), &padded_pin)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let pin_uv_auth_param = shared_secret.as_ref().authenticate(&new_pin_enc);
|
let pin_uv_auth_param = shared_secret.as_ref().authenticate(&new_pin_enc);
|
||||||
let pin_hash_enc = shared_secret
|
let pin_hash_enc = shared_secret
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.encrypt(&mut rng, &pin_hash[..16])
|
.encrypt(env.rng(), &pin_hash[..16])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let (permissions, permissions_rp_id) = match sub_command {
|
let (permissions, permissions_rp_id) = match sub_command {
|
||||||
ClientPinSubCommand::GetPinUvAuthTokenUsingUvWithPermissions
|
ClientPinSubCommand::GetPinUvAuthTokenUsingUvWithPermissions
|
||||||
@@ -691,8 +681,8 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mix_pin_protocols() {
|
fn test_mix_pin_protocols() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let client_pin = ClientPin::new(&mut rng);
|
let client_pin = ClientPin::new(env.rng());
|
||||||
let pin_protocol_v1 = client_pin.get_pin_protocol(PinUvAuthProtocol::V1);
|
let pin_protocol_v1 = client_pin.get_pin_protocol(PinUvAuthProtocol::V1);
|
||||||
let pin_protocol_v2 = client_pin.get_pin_protocol(PinUvAuthProtocol::V2);
|
let pin_protocol_v2 = client_pin.get_pin_protocol(PinUvAuthProtocol::V2);
|
||||||
let message = vec![0xAA; 16];
|
let message = vec![0xAA; 16];
|
||||||
@@ -703,38 +693,37 @@ mod test {
|
|||||||
let shared_secret_v2 = pin_protocol_v2
|
let shared_secret_v2 = pin_protocol_v2
|
||||||
.decapsulate(pin_protocol_v2.get_public_key(), PinUvAuthProtocol::V2)
|
.decapsulate(pin_protocol_v2.get_public_key(), PinUvAuthProtocol::V2)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let ciphertext = shared_secret_v1.encrypt(&mut rng, &message).unwrap();
|
let ciphertext = shared_secret_v1.encrypt(env.rng(), &message).unwrap();
|
||||||
let plaintext = shared_secret_v2.decrypt(&ciphertext).unwrap();
|
let plaintext = shared_secret_v2.decrypt(&ciphertext).unwrap();
|
||||||
assert_ne!(&message, &plaintext);
|
assert_ne!(&message, &plaintext);
|
||||||
let ciphertext = shared_secret_v2.encrypt(&mut rng, &message).unwrap();
|
let ciphertext = shared_secret_v2.encrypt(env.rng(), &message).unwrap();
|
||||||
let plaintext = shared_secret_v1.decrypt(&ciphertext).unwrap();
|
let plaintext = shared_secret_v1.decrypt(&ciphertext).unwrap();
|
||||||
assert_ne!(&message, &plaintext);
|
assert_ne!(&message, &plaintext);
|
||||||
|
|
||||||
let fake_secret_v1 = pin_protocol_v1
|
let fake_secret_v1 = pin_protocol_v1
|
||||||
.decapsulate(pin_protocol_v2.get_public_key(), PinUvAuthProtocol::V1)
|
.decapsulate(pin_protocol_v2.get_public_key(), PinUvAuthProtocol::V1)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let ciphertext = shared_secret_v1.encrypt(&mut rng, &message).unwrap();
|
let ciphertext = shared_secret_v1.encrypt(env.rng(), &message).unwrap();
|
||||||
let plaintext = fake_secret_v1.decrypt(&ciphertext).unwrap();
|
let plaintext = fake_secret_v1.decrypt(&ciphertext).unwrap();
|
||||||
assert_ne!(&message, &plaintext);
|
assert_ne!(&message, &plaintext);
|
||||||
let ciphertext = fake_secret_v1.encrypt(&mut rng, &message).unwrap();
|
let ciphertext = fake_secret_v1.encrypt(env.rng(), &message).unwrap();
|
||||||
let plaintext = shared_secret_v1.decrypt(&ciphertext).unwrap();
|
let plaintext = shared_secret_v1.decrypt(&ciphertext).unwrap();
|
||||||
assert_ne!(&message, &plaintext);
|
assert_ne!(&message, &plaintext);
|
||||||
|
|
||||||
let fake_secret_v2 = pin_protocol_v2
|
let fake_secret_v2 = pin_protocol_v2
|
||||||
.decapsulate(pin_protocol_v1.get_public_key(), PinUvAuthProtocol::V2)
|
.decapsulate(pin_protocol_v1.get_public_key(), PinUvAuthProtocol::V2)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let ciphertext = shared_secret_v2.encrypt(&mut rng, &message).unwrap();
|
let ciphertext = shared_secret_v2.encrypt(env.rng(), &message).unwrap();
|
||||||
let plaintext = fake_secret_v2.decrypt(&ciphertext).unwrap();
|
let plaintext = fake_secret_v2.decrypt(&ciphertext).unwrap();
|
||||||
assert_ne!(&message, &plaintext);
|
assert_ne!(&message, &plaintext);
|
||||||
let ciphertext = fake_secret_v2.encrypt(&mut rng, &message).unwrap();
|
let ciphertext = fake_secret_v2.encrypt(env.rng(), &message).unwrap();
|
||||||
let plaintext = shared_secret_v2.decrypt(&ciphertext).unwrap();
|
let plaintext = shared_secret_v2.decrypt(&ciphertext).unwrap();
|
||||||
assert_ne!(&message, &plaintext);
|
assert_ne!(&message, &plaintext);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_helper_verify_pin_hash_enc(pin_uv_auth_protocol: PinUvAuthProtocol) {
|
fn test_helper_verify_pin_hash_enc(pin_uv_auth_protocol: PinUvAuthProtocol) {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
let mut client_pin = ClientPin::new(env.rng());
|
||||||
let mut client_pin = ClientPin::new(&mut rng);
|
|
||||||
let pin_protocol = client_pin.get_pin_protocol(pin_uv_auth_protocol);
|
let pin_protocol = client_pin.get_pin_protocol(pin_uv_auth_protocol);
|
||||||
let shared_secret = pin_protocol
|
let shared_secret = pin_protocol
|
||||||
.decapsulate(pin_protocol.get_public_key(), pin_uv_auth_protocol)
|
.decapsulate(pin_protocol.get_public_key(), pin_uv_auth_protocol)
|
||||||
@@ -744,13 +733,15 @@ mod test {
|
|||||||
0x01, 0xD9, 0x88, 0x40, 0x50, 0xBB, 0xD0, 0x7A, 0x23, 0x1A, 0xEB, 0x69, 0xD8, 0x36,
|
0x01, 0xD9, 0x88, 0x40, 0x50, 0xBB, 0xD0, 0x7A, 0x23, 0x1A, 0xEB, 0x69, 0xD8, 0x36,
|
||||||
0xC4, 0x12,
|
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().encrypt(&mut rng, &pin_hash).unwrap();
|
let pin_hash_enc = shared_secret
|
||||||
|
.as_ref()
|
||||||
|
.encrypt(env.rng(), &pin_hash)
|
||||||
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.verify_pin_hash_enc(
|
client_pin.verify_pin_hash_enc(
|
||||||
&mut rng,
|
&mut env,
|
||||||
&mut persistent_store,
|
|
||||||
pin_uv_auth_protocol,
|
pin_uv_auth_protocol,
|
||||||
shared_secret.as_ref(),
|
shared_secret.as_ref(),
|
||||||
pin_hash_enc
|
pin_hash_enc
|
||||||
@@ -761,8 +752,7 @@ mod test {
|
|||||||
let pin_hash_enc = vec![0xEE; 16];
|
let pin_hash_enc = vec![0xEE; 16];
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.verify_pin_hash_enc(
|
client_pin.verify_pin_hash_enc(
|
||||||
&mut rng,
|
&mut env,
|
||||||
&mut persistent_store,
|
|
||||||
pin_uv_auth_protocol,
|
pin_uv_auth_protocol,
|
||||||
shared_secret.as_ref(),
|
shared_secret.as_ref(),
|
||||||
pin_hash_enc
|
pin_hash_enc
|
||||||
@@ -770,12 +760,14 @@ mod test {
|
|||||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)
|
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)
|
||||||
);
|
);
|
||||||
|
|
||||||
let pin_hash_enc = shared_secret.as_ref().encrypt(&mut rng, &pin_hash).unwrap();
|
let pin_hash_enc = shared_secret
|
||||||
|
.as_ref()
|
||||||
|
.encrypt(env.rng(), &pin_hash)
|
||||||
|
.unwrap();
|
||||||
client_pin.consecutive_pin_mismatches = 3;
|
client_pin.consecutive_pin_mismatches = 3;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.verify_pin_hash_enc(
|
client_pin.verify_pin_hash_enc(
|
||||||
&mut rng,
|
&mut env,
|
||||||
&mut persistent_store,
|
|
||||||
pin_uv_auth_protocol,
|
pin_uv_auth_protocol,
|
||||||
shared_secret.as_ref(),
|
shared_secret.as_ref(),
|
||||||
pin_hash_enc
|
pin_hash_enc
|
||||||
@@ -787,8 +779,7 @@ mod test {
|
|||||||
let pin_hash_enc = vec![0x77; PIN_AUTH_LENGTH - 1];
|
let pin_hash_enc = vec![0x77; PIN_AUTH_LENGTH - 1];
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.verify_pin_hash_enc(
|
client_pin.verify_pin_hash_enc(
|
||||||
&mut rng,
|
&mut env,
|
||||||
&mut persistent_store,
|
|
||||||
pin_uv_auth_protocol,
|
pin_uv_auth_protocol,
|
||||||
shared_secret.as_ref(),
|
shared_secret.as_ref(),
|
||||||
pin_hash_enc
|
pin_hash_enc
|
||||||
@@ -799,8 +790,7 @@ mod test {
|
|||||||
let pin_hash_enc = vec![0x77; PIN_AUTH_LENGTH + 1];
|
let pin_hash_enc = vec![0x77; PIN_AUTH_LENGTH + 1];
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.verify_pin_hash_enc(
|
client_pin.verify_pin_hash_enc(
|
||||||
&mut rng,
|
&mut env,
|
||||||
&mut persistent_store,
|
|
||||||
pin_uv_auth_protocol,
|
pin_uv_auth_protocol,
|
||||||
shared_secret.as_ref(),
|
shared_secret.as_ref(),
|
||||||
pin_hash_enc
|
pin_hash_enc
|
||||||
@@ -824,21 +814,15 @@ mod test {
|
|||||||
pin_uv_auth_protocol,
|
pin_uv_auth_protocol,
|
||||||
ClientPinSubCommand::GetPinRetries,
|
ClientPinSubCommand::GetPinRetries,
|
||||||
);
|
);
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
|
||||||
let expected_response = Some(AuthenticatorClientPinResponse {
|
let expected_response = Some(AuthenticatorClientPinResponse {
|
||||||
key_agreement: None,
|
key_agreement: None,
|
||||||
pin_uv_auth_token: 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),
|
power_cycle_state: Some(false),
|
||||||
});
|
});
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.process_command(
|
client_pin.process_command(&mut env, params.clone(), DUMMY_CLOCK_VALUE),
|
||||||
&mut rng,
|
|
||||||
&mut persistent_store,
|
|
||||||
params.clone(),
|
|
||||||
DUMMY_CLOCK_VALUE
|
|
||||||
),
|
|
||||||
Ok(ResponseData::AuthenticatorClientPin(expected_response))
|
Ok(ResponseData::AuthenticatorClientPin(expected_response))
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -846,11 +830,11 @@ mod test {
|
|||||||
let expected_response = Some(AuthenticatorClientPinResponse {
|
let expected_response = Some(AuthenticatorClientPinResponse {
|
||||||
key_agreement: None,
|
key_agreement: None,
|
||||||
pin_uv_auth_token: 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),
|
power_cycle_state: Some(true),
|
||||||
});
|
});
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.process_command(&mut rng, &mut persistent_store, params, DUMMY_CLOCK_VALUE),
|
client_pin.process_command(&mut env, params, DUMMY_CLOCK_VALUE),
|
||||||
Ok(ResponseData::AuthenticatorClientPin(expected_response))
|
Ok(ResponseData::AuthenticatorClientPin(expected_response))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -870,8 +854,7 @@ mod test {
|
|||||||
pin_uv_auth_protocol,
|
pin_uv_auth_protocol,
|
||||||
ClientPinSubCommand::GetKeyAgreement,
|
ClientPinSubCommand::GetKeyAgreement,
|
||||||
);
|
);
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
|
||||||
let expected_response = Some(AuthenticatorClientPinResponse {
|
let expected_response = Some(AuthenticatorClientPinResponse {
|
||||||
key_agreement: params.key_agreement.clone(),
|
key_agreement: params.key_agreement.clone(),
|
||||||
pin_uv_auth_token: None,
|
pin_uv_auth_token: None,
|
||||||
@@ -879,7 +862,7 @@ mod test {
|
|||||||
power_cycle_state: None,
|
power_cycle_state: None,
|
||||||
});
|
});
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.process_command(&mut rng, &mut persistent_store, params, DUMMY_CLOCK_VALUE),
|
client_pin.process_command(&mut env, params, DUMMY_CLOCK_VALUE),
|
||||||
Ok(ResponseData::AuthenticatorClientPin(expected_response))
|
Ok(ResponseData::AuthenticatorClientPin(expected_response))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -897,10 +880,9 @@ mod test {
|
|||||||
fn test_helper_process_set_pin(pin_uv_auth_protocol: PinUvAuthProtocol) {
|
fn test_helper_process_set_pin(pin_uv_auth_protocol: PinUvAuthProtocol) {
|
||||||
let (mut client_pin, params) =
|
let (mut client_pin, params) =
|
||||||
create_client_pin_and_parameters(pin_uv_auth_protocol, ClientPinSubCommand::SetPin);
|
create_client_pin_and_parameters(pin_uv_auth_protocol, ClientPinSubCommand::SetPin);
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.process_command(&mut rng, &mut persistent_store, params, DUMMY_CLOCK_VALUE),
|
client_pin.process_command(&mut env, params, DUMMY_CLOCK_VALUE),
|
||||||
Ok(ResponseData::AuthenticatorClientPin(None))
|
Ok(ResponseData::AuthenticatorClientPin(None))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -925,41 +907,30 @@ mod test {
|
|||||||
params.pin_uv_auth_protocol,
|
params.pin_uv_auth_protocol,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
set_standard_pin(&mut env);
|
||||||
set_standard_pin(&mut persistent_store);
|
|
||||||
|
|
||||||
let mut auth_param_data = params.new_pin_enc.clone().unwrap();
|
let mut auth_param_data = params.new_pin_enc.clone().unwrap();
|
||||||
auth_param_data.extend(params.pin_hash_enc.as_ref().unwrap());
|
auth_param_data.extend(params.pin_hash_enc.as_ref().unwrap());
|
||||||
let pin_uv_auth_param = shared_secret.authenticate(&auth_param_data);
|
let pin_uv_auth_param = shared_secret.authenticate(&auth_param_data);
|
||||||
params.pin_uv_auth_param = Some(pin_uv_auth_param);
|
params.pin_uv_auth_param = Some(pin_uv_auth_param);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.process_command(
|
client_pin.process_command(&mut env, params.clone(), DUMMY_CLOCK_VALUE),
|
||||||
&mut rng,
|
|
||||||
&mut persistent_store,
|
|
||||||
params.clone(),
|
|
||||||
DUMMY_CLOCK_VALUE
|
|
||||||
),
|
|
||||||
Ok(ResponseData::AuthenticatorClientPin(None))
|
Ok(ResponseData::AuthenticatorClientPin(None))
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut bad_params = params.clone();
|
let mut bad_params = params.clone();
|
||||||
bad_params.pin_hash_enc = Some(vec![0xEE; 16]);
|
bad_params.pin_hash_enc = Some(vec![0xEE; 16]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.process_command(
|
client_pin.process_command(&mut env, bad_params, DUMMY_CLOCK_VALUE),
|
||||||
&mut rng,
|
|
||||||
&mut persistent_store,
|
|
||||||
bad_params,
|
|
||||||
DUMMY_CLOCK_VALUE
|
|
||||||
),
|
|
||||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
|
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
|
||||||
);
|
);
|
||||||
|
|
||||||
while persistent_store.pin_retries().unwrap() > 0 {
|
while storage::pin_retries(&mut env).unwrap() > 0 {
|
||||||
persistent_store.decr_pin_retries().unwrap();
|
storage::decr_pin_retries(&mut env).unwrap();
|
||||||
}
|
}
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.process_command(&mut rng, &mut persistent_store, params, DUMMY_CLOCK_VALUE),
|
client_pin.process_command(&mut env, params, DUMMY_CLOCK_VALUE),
|
||||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_BLOCKED)
|
Err(Ctap2StatusCode::CTAP2_ERR_PIN_BLOCKED)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -986,17 +957,11 @@ mod test {
|
|||||||
params.pin_uv_auth_protocol,
|
params.pin_uv_auth_protocol,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
set_standard_pin(&mut env);
|
||||||
set_standard_pin(&mut persistent_store);
|
|
||||||
|
|
||||||
let response = client_pin
|
let response = client_pin
|
||||||
.process_command(
|
.process_command(&mut env, params.clone(), DUMMY_CLOCK_VALUE)
|
||||||
&mut rng,
|
|
||||||
&mut persistent_store,
|
|
||||||
params.clone(),
|
|
||||||
DUMMY_CLOCK_VALUE,
|
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let encrypted_token = match response {
|
let encrypted_token = match response {
|
||||||
ResponseData::AuthenticatorClientPin(Some(response)) => {
|
ResponseData::AuthenticatorClientPin(Some(response)) => {
|
||||||
@@ -1032,12 +997,7 @@ mod test {
|
|||||||
let mut bad_params = params;
|
let mut bad_params = params;
|
||||||
bad_params.pin_hash_enc = Some(vec![0xEE; 16]);
|
bad_params.pin_hash_enc = Some(vec![0xEE; 16]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.process_command(
|
client_pin.process_command(&mut env, bad_params, DUMMY_CLOCK_VALUE),
|
||||||
&mut rng,
|
|
||||||
&mut persistent_store,
|
|
||||||
bad_params,
|
|
||||||
DUMMY_CLOCK_VALUE
|
|
||||||
),
|
|
||||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)
|
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1057,13 +1017,12 @@ mod test {
|
|||||||
pin_uv_auth_protocol,
|
pin_uv_auth_protocol,
|
||||||
ClientPinSubCommand::GetPinToken,
|
ClientPinSubCommand::GetPinToken,
|
||||||
);
|
);
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
set_standard_pin(&mut env);
|
||||||
set_standard_pin(&mut persistent_store);
|
|
||||||
|
|
||||||
assert_eq!(persistent_store.force_pin_change(), Ok(()));
|
assert_eq!(storage::force_pin_change(&mut env), Ok(()));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.process_command(&mut rng, &mut persistent_store, params, DUMMY_CLOCK_VALUE),
|
client_pin.process_command(&mut env, params, DUMMY_CLOCK_VALUE),
|
||||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID),
|
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1092,17 +1051,11 @@ mod test {
|
|||||||
params.pin_uv_auth_protocol,
|
params.pin_uv_auth_protocol,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
set_standard_pin(&mut env);
|
||||||
set_standard_pin(&mut persistent_store);
|
|
||||||
|
|
||||||
let response = client_pin
|
let response = client_pin
|
||||||
.process_command(
|
.process_command(&mut env, params.clone(), DUMMY_CLOCK_VALUE)
|
||||||
&mut rng,
|
|
||||||
&mut persistent_store,
|
|
||||||
params.clone(),
|
|
||||||
DUMMY_CLOCK_VALUE,
|
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let encrypted_token = match response {
|
let encrypted_token = match response {
|
||||||
ResponseData::AuthenticatorClientPin(Some(response)) => {
|
ResponseData::AuthenticatorClientPin(Some(response)) => {
|
||||||
@@ -1138,36 +1091,21 @@ mod test {
|
|||||||
let mut bad_params = params.clone();
|
let mut bad_params = params.clone();
|
||||||
bad_params.permissions = Some(0x00);
|
bad_params.permissions = Some(0x00);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.process_command(
|
client_pin.process_command(&mut env, bad_params, DUMMY_CLOCK_VALUE),
|
||||||
&mut rng,
|
|
||||||
&mut persistent_store,
|
|
||||||
bad_params,
|
|
||||||
DUMMY_CLOCK_VALUE
|
|
||||||
),
|
|
||||||
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut bad_params = params.clone();
|
let mut bad_params = params.clone();
|
||||||
bad_params.permissions_rp_id = None;
|
bad_params.permissions_rp_id = None;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.process_command(
|
client_pin.process_command(&mut env, bad_params, DUMMY_CLOCK_VALUE),
|
||||||
&mut rng,
|
|
||||||
&mut persistent_store,
|
|
||||||
bad_params,
|
|
||||||
DUMMY_CLOCK_VALUE
|
|
||||||
),
|
|
||||||
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut bad_params = params;
|
let mut bad_params = params;
|
||||||
bad_params.pin_hash_enc = Some(vec![0xEE; 16]);
|
bad_params.pin_hash_enc = Some(vec![0xEE; 16]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.process_command(
|
client_pin.process_command(&mut env, bad_params, DUMMY_CLOCK_VALUE),
|
||||||
&mut rng,
|
|
||||||
&mut persistent_store,
|
|
||||||
bad_params,
|
|
||||||
DUMMY_CLOCK_VALUE
|
|
||||||
),
|
|
||||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)
|
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1189,13 +1127,12 @@ mod test {
|
|||||||
pin_uv_auth_protocol,
|
pin_uv_auth_protocol,
|
||||||
ClientPinSubCommand::GetPinUvAuthTokenUsingPinWithPermissions,
|
ClientPinSubCommand::GetPinUvAuthTokenUsingPinWithPermissions,
|
||||||
);
|
);
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
set_standard_pin(&mut env);
|
||||||
set_standard_pin(&mut persistent_store);
|
|
||||||
|
|
||||||
assert_eq!(persistent_store.force_pin_change(), Ok(()));
|
assert_eq!(storage::force_pin_change(&mut env), Ok(()));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.process_command(&mut rng, &mut persistent_store, params, DUMMY_CLOCK_VALUE),
|
client_pin.process_command(&mut env, params, DUMMY_CLOCK_VALUE),
|
||||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)
|
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1215,8 +1152,8 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn test_helper_decrypt_pin(pin_uv_auth_protocol: PinUvAuthProtocol) {
|
fn test_helper_decrypt_pin(pin_uv_auth_protocol: PinUvAuthProtocol) {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let pin_protocol = PinProtocol::new(&mut rng);
|
let pin_protocol = PinProtocol::new(env.rng());
|
||||||
let shared_secret = pin_protocol
|
let shared_secret = pin_protocol
|
||||||
.decapsulate(pin_protocol.get_public_key(), pin_uv_auth_protocol)
|
.decapsulate(pin_protocol.get_public_key(), pin_uv_auth_protocol)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1259,9 +1196,8 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn test_helper_check_and_store_new_pin(pin_uv_auth_protocol: PinUvAuthProtocol) {
|
fn test_helper_check_and_store_new_pin(pin_uv_auth_protocol: PinUvAuthProtocol) {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
let pin_protocol = PinProtocol::new(env.rng());
|
||||||
let pin_protocol = PinProtocol::new(&mut rng);
|
|
||||||
let shared_secret = pin_protocol
|
let shared_secret = pin_protocol
|
||||||
.decapsulate(pin_protocol.get_public_key(), pin_uv_auth_protocol)
|
.decapsulate(pin_protocol.get_public_key(), pin_uv_auth_protocol)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1286,17 +1222,17 @@ mod test {
|
|||||||
),
|
),
|
||||||
];
|
];
|
||||||
for (pin, result) in test_cases {
|
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);
|
let new_pin_enc = encrypt_pin(shared_secret.as_ref(), pin);
|
||||||
|
|
||||||
assert_eq!(
|
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
|
result
|
||||||
);
|
);
|
||||||
if result.is_ok() {
|
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 {
|
} else {
|
||||||
assert_eq!(old_pin_hash, persistent_store.pin_hash().unwrap());
|
assert_eq!(old_pin_hash, storage::pin_hash(&mut env).unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1317,10 +1253,10 @@ mod test {
|
|||||||
cred_random: &[u8; 32],
|
cred_random: &[u8; 32],
|
||||||
salt: Vec<u8>,
|
salt: Vec<u8>,
|
||||||
) -> Result<Vec<u8>, Ctap2StatusCode> {
|
) -> Result<Vec<u8>, Ctap2StatusCode> {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let (client_pin, shared_secret) = create_client_pin_and_shared_secret(pin_uv_auth_protocol);
|
let (client_pin, shared_secret) = create_client_pin_and_shared_secret(pin_uv_auth_protocol);
|
||||||
|
|
||||||
let salt_enc = shared_secret.as_ref().encrypt(&mut rng, &salt).unwrap();
|
let salt_enc = shared_secret.as_ref().encrypt(env.rng(), &salt).unwrap();
|
||||||
let salt_auth = shared_secret.authenticate(&salt_enc);
|
let salt_auth = shared_secret.authenticate(&salt_enc);
|
||||||
let hmac_secret_input = GetAssertionHmacSecretInput {
|
let hmac_secret_input = GetAssertionHmacSecretInput {
|
||||||
key_agreement: client_pin
|
key_agreement: client_pin
|
||||||
@@ -1330,12 +1266,12 @@ mod test {
|
|||||||
salt_auth,
|
salt_auth,
|
||||||
pin_uv_auth_protocol,
|
pin_uv_auth_protocol,
|
||||||
};
|
};
|
||||||
let output = client_pin.process_hmac_secret(&mut rng, hmac_secret_input, cred_random);
|
let output = client_pin.process_hmac_secret(env.rng(), hmac_secret_input, cred_random);
|
||||||
output.map(|v| shared_secret.as_ref().decrypt(&v).unwrap())
|
output.map(|v| shared_secret.as_ref().decrypt(&v).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_helper_process_hmac_secret_bad_salt_auth(pin_uv_auth_protocol: PinUvAuthProtocol) {
|
fn test_helper_process_hmac_secret_bad_salt_auth(pin_uv_auth_protocol: PinUvAuthProtocol) {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let (client_pin, shared_secret) = create_client_pin_and_shared_secret(pin_uv_auth_protocol);
|
let (client_pin, shared_secret) = create_client_pin_and_shared_secret(pin_uv_auth_protocol);
|
||||||
let cred_random = [0xC9; 32];
|
let cred_random = [0xC9; 32];
|
||||||
|
|
||||||
@@ -1350,7 +1286,7 @@ mod test {
|
|||||||
salt_auth,
|
salt_auth,
|
||||||
pin_uv_auth_protocol,
|
pin_uv_auth_protocol,
|
||||||
};
|
};
|
||||||
let output = client_pin.process_hmac_secret(&mut rng, hmac_secret_input, &cred_random);
|
let output = client_pin.process_hmac_secret(env.rng(), hmac_secret_input, &cred_random);
|
||||||
assert_eq!(output, Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID));
|
assert_eq!(output, Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1451,8 +1387,8 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_has_permission() {
|
fn test_has_permission() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut client_pin = ClientPin::new(&mut rng);
|
let mut client_pin = ClientPin::new(env.rng());
|
||||||
client_pin.pin_uv_auth_token_state.set_permissions(0x7F);
|
client_pin.pin_uv_auth_token_state.set_permissions(0x7F);
|
||||||
for permission in PinPermission::into_enum_iter() {
|
for permission in PinPermission::into_enum_iter() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -1475,8 +1411,8 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_has_no_rp_id_permission() {
|
fn test_has_no_rp_id_permission() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut client_pin = ClientPin::new(&mut rng);
|
let mut client_pin = ClientPin::new(env.rng());
|
||||||
assert_eq!(client_pin.has_no_rp_id_permission(), Ok(()));
|
assert_eq!(client_pin.has_no_rp_id_permission(), Ok(()));
|
||||||
client_pin
|
client_pin
|
||||||
.pin_uv_auth_token_state
|
.pin_uv_auth_token_state
|
||||||
@@ -1489,8 +1425,8 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_has_no_or_rp_id_permission() {
|
fn test_has_no_or_rp_id_permission() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut client_pin = ClientPin::new(&mut rng);
|
let mut client_pin = ClientPin::new(env.rng());
|
||||||
assert_eq!(client_pin.has_no_or_rp_id_permission("example.com"), Ok(()));
|
assert_eq!(client_pin.has_no_or_rp_id_permission("example.com"), Ok(()));
|
||||||
client_pin
|
client_pin
|
||||||
.pin_uv_auth_token_state
|
.pin_uv_auth_token_state
|
||||||
@@ -1504,8 +1440,8 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_has_no_or_rp_id_hash_permission() {
|
fn test_has_no_or_rp_id_hash_permission() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut client_pin = ClientPin::new(&mut rng);
|
let mut client_pin = ClientPin::new(env.rng());
|
||||||
let rp_id_hash = Sha256::hash(b"example.com");
|
let rp_id_hash = Sha256::hash(b"example.com");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.has_no_or_rp_id_hash_permission(&rp_id_hash),
|
client_pin.has_no_or_rp_id_hash_permission(&rp_id_hash),
|
||||||
@@ -1526,8 +1462,8 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ensure_rp_id_permission() {
|
fn test_ensure_rp_id_permission() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut client_pin = ClientPin::new(&mut rng);
|
let mut client_pin = ClientPin::new(env.rng());
|
||||||
assert_eq!(client_pin.ensure_rp_id_permission("example.com"), Ok(()));
|
assert_eq!(client_pin.ensure_rp_id_permission("example.com"), Ok(()));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin
|
client_pin
|
||||||
@@ -1544,8 +1480,8 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_verify_pin_uv_auth_token() {
|
fn test_verify_pin_uv_auth_token() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut client_pin = ClientPin::new(&mut rng);
|
let mut client_pin = ClientPin::new(env.rng());
|
||||||
let message = [0xAA];
|
let message = [0xAA];
|
||||||
client_pin
|
client_pin
|
||||||
.pin_uv_auth_token_state
|
.pin_uv_auth_token_state
|
||||||
@@ -1618,8 +1554,8 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_verify_pin_uv_auth_token_not_in_use() {
|
fn test_verify_pin_uv_auth_token_not_in_use() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let client_pin = ClientPin::new(&mut rng);
|
let client_pin = ClientPin::new(env.rng());
|
||||||
let message = [0xAA];
|
let message = [0xAA];
|
||||||
|
|
||||||
let pin_uv_auth_token_v1 = client_pin
|
let pin_uv_auth_token_v1 = client_pin
|
||||||
@@ -1640,8 +1576,8 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_reset() {
|
fn test_reset() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut client_pin = ClientPin::new(&mut rng);
|
let mut client_pin = ClientPin::new(env.rng());
|
||||||
let public_key_v1 = client_pin.pin_protocol_v1.get_public_key();
|
let public_key_v1 = client_pin.pin_protocol_v1.get_public_key();
|
||||||
let public_key_v2 = client_pin.pin_protocol_v2.get_public_key();
|
let public_key_v2 = client_pin.pin_protocol_v2.get_public_key();
|
||||||
let token_v1 = *client_pin.pin_protocol_v1.get_pin_uv_auth_token();
|
let token_v1 = *client_pin.pin_protocol_v1.get_pin_uv_auth_token();
|
||||||
@@ -1650,7 +1586,7 @@ mod test {
|
|||||||
client_pin
|
client_pin
|
||||||
.pin_uv_auth_token_state
|
.pin_uv_auth_token_state
|
||||||
.set_permissions_rp_id(Some(String::from("example.com")));
|
.set_permissions_rp_id(Some(String::from("example.com")));
|
||||||
client_pin.reset(&mut rng);
|
client_pin.reset(env.rng());
|
||||||
assert_ne!(public_key_v1, client_pin.pin_protocol_v1.get_public_key());
|
assert_ne!(public_key_v1, client_pin.pin_protocol_v1.get_public_key());
|
||||||
assert_ne!(public_key_v2, client_pin.pin_protocol_v2.get_public_key());
|
assert_ne!(public_key_v2, client_pin.pin_protocol_v2.get_public_key());
|
||||||
assert_ne!(
|
assert_ne!(
|
||||||
@@ -1676,13 +1612,12 @@ mod test {
|
|||||||
PinUvAuthProtocol::V2,
|
PinUvAuthProtocol::V2,
|
||||||
ClientPinSubCommand::GetPinUvAuthTokenUsingPinWithPermissions,
|
ClientPinSubCommand::GetPinUvAuthTokenUsingPinWithPermissions,
|
||||||
);
|
);
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
set_standard_pin(&mut env);
|
||||||
set_standard_pin(&mut persistent_store);
|
|
||||||
params.permissions = Some(0xFF);
|
params.permissions = Some(0xFF);
|
||||||
|
|
||||||
assert!(client_pin
|
assert!(client_pin
|
||||||
.process_command(&mut rng, &mut persistent_store, params, DUMMY_CLOCK_VALUE)
|
.process_command(&mut env, params, DUMMY_CLOCK_VALUE)
|
||||||
.is_ok());
|
.is_ok());
|
||||||
for permission in PinPermission::into_enum_iter() {
|
for permission in PinPermission::into_enum_iter() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -1723,13 +1658,12 @@ mod test {
|
|||||||
PinUvAuthProtocol::V2,
|
PinUvAuthProtocol::V2,
|
||||||
ClientPinSubCommand::GetPinUvAuthTokenUsingPinWithPermissions,
|
ClientPinSubCommand::GetPinUvAuthTokenUsingPinWithPermissions,
|
||||||
);
|
);
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
set_standard_pin(&mut env);
|
||||||
set_standard_pin(&mut persistent_store);
|
|
||||||
params.permissions = Some(0xFF);
|
params.permissions = Some(0xFF);
|
||||||
|
|
||||||
assert!(client_pin
|
assert!(client_pin
|
||||||
.process_command(&mut rng, &mut persistent_store, params, DUMMY_CLOCK_VALUE)
|
.process_command(&mut env, params, DUMMY_CLOCK_VALUE)
|
||||||
.is_ok());
|
.is_ok());
|
||||||
for permission in PinPermission::into_enum_iter() {
|
for permission in PinPermission::into_enum_iter() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -18,15 +18,16 @@ use super::customization::ENTERPRISE_ATTESTATION_MODE;
|
|||||||
use super::data_formats::{ConfigSubCommand, ConfigSubCommandParams, SetMinPinLengthParams};
|
use super::data_formats::{ConfigSubCommand, ConfigSubCommandParams, SetMinPinLengthParams};
|
||||||
use super::response::ResponseData;
|
use super::response::ResponseData;
|
||||||
use super::status_code::Ctap2StatusCode;
|
use super::status_code::Ctap2StatusCode;
|
||||||
use super::storage::PersistentStore;
|
use crate::ctap::storage;
|
||||||
|
use crate::env::Env;
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
|
|
||||||
/// Processes the subcommand enableEnterpriseAttestation for AuthenticatorConfig.
|
/// Processes the subcommand enableEnterpriseAttestation for AuthenticatorConfig.
|
||||||
fn process_enable_enterprise_attestation(
|
fn process_enable_enterprise_attestation(
|
||||||
persistent_store: &mut PersistentStore,
|
env: &mut impl Env,
|
||||||
) -> Result<ResponseData, Ctap2StatusCode> {
|
) -> Result<ResponseData, Ctap2StatusCode> {
|
||||||
if ENTERPRISE_ATTESTATION_MODE.is_some() {
|
if ENTERPRISE_ATTESTATION_MODE.is_some() {
|
||||||
persistent_store.enable_enterprise_attestation()?;
|
storage::enable_enterprise_attestation(env)?;
|
||||||
Ok(ResponseData::AuthenticatorConfig)
|
Ok(ResponseData::AuthenticatorConfig)
|
||||||
} else {
|
} else {
|
||||||
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
||||||
@@ -34,16 +35,14 @@ fn process_enable_enterprise_attestation(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Processes the subcommand toggleAlwaysUv for AuthenticatorConfig.
|
/// Processes the subcommand toggleAlwaysUv for AuthenticatorConfig.
|
||||||
fn process_toggle_always_uv(
|
fn process_toggle_always_uv(env: &mut impl Env) -> Result<ResponseData, Ctap2StatusCode> {
|
||||||
persistent_store: &mut PersistentStore,
|
storage::toggle_always_uv(env)?;
|
||||||
) -> Result<ResponseData, Ctap2StatusCode> {
|
|
||||||
persistent_store.toggle_always_uv()?;
|
|
||||||
Ok(ResponseData::AuthenticatorConfig)
|
Ok(ResponseData::AuthenticatorConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes the subcommand setMinPINLength for AuthenticatorConfig.
|
/// Processes the subcommand setMinPINLength for AuthenticatorConfig.
|
||||||
fn process_set_min_pin_length(
|
fn process_set_min_pin_length(
|
||||||
persistent_store: &mut PersistentStore,
|
env: &mut impl Env,
|
||||||
params: SetMinPinLengthParams,
|
params: SetMinPinLengthParams,
|
||||||
) -> Result<ResponseData, Ctap2StatusCode> {
|
) -> Result<ResponseData, Ctap2StatusCode> {
|
||||||
let SetMinPinLengthParams {
|
let SetMinPinLengthParams {
|
||||||
@@ -51,31 +50,31 @@ fn process_set_min_pin_length(
|
|||||||
min_pin_length_rp_ids,
|
min_pin_length_rp_ids,
|
||||||
force_change_pin,
|
force_change_pin,
|
||||||
} = params;
|
} = 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);
|
let new_min_pin_length = new_min_pin_length.unwrap_or(store_min_pin_length);
|
||||||
if new_min_pin_length < store_min_pin_length {
|
if new_min_pin_length < store_min_pin_length {
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_POLICY_VIOLATION);
|
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_POLICY_VIOLATION);
|
||||||
}
|
}
|
||||||
let mut force_change_pin = force_change_pin.unwrap_or(false);
|
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);
|
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;
|
force_change_pin |= new_min_pin_length > old_length;
|
||||||
}
|
}
|
||||||
if force_change_pin {
|
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 {
|
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)
|
Ok(ResponseData::AuthenticatorConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes the AuthenticatorConfig command.
|
/// Processes the AuthenticatorConfig command.
|
||||||
pub fn process_config(
|
pub fn process_config(
|
||||||
persistent_store: &mut PersistentStore,
|
env: &mut impl Env,
|
||||||
client_pin: &mut ClientPin,
|
client_pin: &mut ClientPin,
|
||||||
params: AuthenticatorConfigParameters,
|
params: AuthenticatorConfigParameters,
|
||||||
) -> Result<ResponseData, Ctap2StatusCode> {
|
) -> Result<ResponseData, Ctap2StatusCode> {
|
||||||
@@ -86,9 +85,9 @@ pub fn process_config(
|
|||||||
pin_uv_auth_protocol,
|
pin_uv_auth_protocol,
|
||||||
} = params;
|
} = params;
|
||||||
|
|
||||||
let enforce_uv = !matches!(sub_command, ConfigSubCommand::ToggleAlwaysUv)
|
let enforce_uv =
|
||||||
&& persistent_store.has_always_uv()?;
|
!matches!(sub_command, ConfigSubCommand::ToggleAlwaysUv) && storage::has_always_uv(env)?;
|
||||||
if persistent_store.pin_hash()?.is_some() || enforce_uv {
|
if storage::pin_hash(env)?.is_some() || enforce_uv {
|
||||||
let pin_uv_auth_param =
|
let pin_uv_auth_param =
|
||||||
pin_uv_auth_param.ok_or(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED)?;
|
pin_uv_auth_param.ok_or(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED)?;
|
||||||
let pin_uv_auth_protocol =
|
let pin_uv_auth_protocol =
|
||||||
@@ -108,13 +107,11 @@ pub fn process_config(
|
|||||||
}
|
}
|
||||||
|
|
||||||
match sub_command {
|
match sub_command {
|
||||||
ConfigSubCommand::EnableEnterpriseAttestation => {
|
ConfigSubCommand::EnableEnterpriseAttestation => process_enable_enterprise_attestation(env),
|
||||||
process_enable_enterprise_attestation(persistent_store)
|
ConfigSubCommand::ToggleAlwaysUv => process_toggle_always_uv(env),
|
||||||
}
|
|
||||||
ConfigSubCommand::ToggleAlwaysUv => process_toggle_always_uv(persistent_store),
|
|
||||||
ConfigSubCommand::SetMinPinLength => {
|
ConfigSubCommand::SetMinPinLength => {
|
||||||
if let Some(ConfigSubCommandParams::SetMinPinLength(params)) = sub_command_params {
|
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 {
|
} else {
|
||||||
Err(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)
|
Err(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)
|
||||||
}
|
}
|
||||||
@@ -129,13 +126,12 @@ mod test {
|
|||||||
use crate::ctap::customization::ENFORCE_ALWAYS_UV;
|
use crate::ctap::customization::ENFORCE_ALWAYS_UV;
|
||||||
use crate::ctap::data_formats::PinUvAuthProtocol;
|
use crate::ctap::data_formats::PinUvAuthProtocol;
|
||||||
use crate::ctap::pin_protocol::authenticate_pin_uv_auth_token;
|
use crate::ctap::pin_protocol::authenticate_pin_uv_auth_token;
|
||||||
use crypto::rng256::ThreadRng256;
|
use crate::env::test::TestEnv;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_process_enable_enterprise_attestation() {
|
fn test_process_enable_enterprise_attestation() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin =
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
@@ -146,11 +142,11 @@ mod test {
|
|||||||
pin_uv_auth_param: None,
|
pin_uv_auth_param: None,
|
||||||
pin_uv_auth_protocol: 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() {
|
if ENTERPRISE_ATTESTATION_MODE.is_some() {
|
||||||
assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig));
|
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 {
|
} else {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
config_response,
|
config_response,
|
||||||
@@ -161,9 +157,8 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_process_toggle_always_uv() {
|
fn test_process_toggle_always_uv() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin =
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
@@ -174,9 +169,9 @@ mod test {
|
|||||||
pin_uv_auth_param: None,
|
pin_uv_auth_param: None,
|
||||||
pin_uv_auth_protocol: 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_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 {
|
let config_params = AuthenticatorConfigParameters {
|
||||||
sub_command: ConfigSubCommand::ToggleAlwaysUv,
|
sub_command: ConfigSubCommand::ToggleAlwaysUv,
|
||||||
@@ -184,7 +179,7 @@ mod test {
|
|||||||
pin_uv_auth_param: None,
|
pin_uv_auth_param: None,
|
||||||
pin_uv_auth_protocol: 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 {
|
if ENFORCE_ALWAYS_UV {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
config_response,
|
config_response,
|
||||||
@@ -192,18 +187,17 @@ mod test {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig));
|
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) {
|
fn test_helper_process_toggle_always_uv_with_pin(pin_uv_auth_protocol: PinUvAuthProtocol) {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin =
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol);
|
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];
|
let mut config_data = vec![0xFF; 32];
|
||||||
config_data.extend(&[0x0D, ConfigSubCommand::ToggleAlwaysUv as u8]);
|
config_data.extend(&[0x0D, ConfigSubCommand::ToggleAlwaysUv as u8]);
|
||||||
@@ -215,7 +209,7 @@ mod test {
|
|||||||
pin_uv_auth_param: Some(pin_uv_auth_param.clone()),
|
pin_uv_auth_param: Some(pin_uv_auth_param.clone()),
|
||||||
pin_uv_auth_protocol: Some(pin_uv_auth_protocol),
|
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 {
|
if ENFORCE_ALWAYS_UV {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
config_response,
|
config_response,
|
||||||
@@ -224,7 +218,7 @@ mod test {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig));
|
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 {
|
let config_params = AuthenticatorConfigParameters {
|
||||||
sub_command: ConfigSubCommand::ToggleAlwaysUv,
|
sub_command: ConfigSubCommand::ToggleAlwaysUv,
|
||||||
@@ -232,9 +226,9 @@ mod test {
|
|||||||
pin_uv_auth_param: Some(pin_uv_auth_param),
|
pin_uv_auth_param: Some(pin_uv_auth_param),
|
||||||
pin_uv_auth_protocol: Some(pin_uv_auth_protocol),
|
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_eq!(config_response, Ok(ResponseData::AuthenticatorConfig));
|
||||||
assert!(!persistent_store.has_always_uv().unwrap());
|
assert!(!storage::has_always_uv(&mut env).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -268,9 +262,8 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_process_set_min_pin_length() {
|
fn test_process_set_min_pin_length() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin =
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
@@ -278,13 +271,13 @@ mod test {
|
|||||||
// First, increase minimum PIN length from 4 to 6 without PIN auth.
|
// First, increase minimum PIN length from 4 to 6 without PIN auth.
|
||||||
let min_pin_length = 6;
|
let min_pin_length = 6;
|
||||||
let config_params = create_min_pin_config_params(min_pin_length, None);
|
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!(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.
|
// 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.
|
// 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 min_pin_length = 8;
|
||||||
let mut config_params = create_min_pin_config_params(min_pin_length, None);
|
let mut config_params = create_min_pin_config_params(min_pin_length, None);
|
||||||
let pin_uv_auth_param = vec![
|
let pin_uv_auth_param = vec![
|
||||||
@@ -292,9 +285,9 @@ mod test {
|
|||||||
0xB2, 0xDE,
|
0xB2, 0xDE,
|
||||||
];
|
];
|
||||||
config_params.pin_uv_auth_param = Some(pin_uv_auth_param);
|
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!(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.
|
// Third, decreasing the minimum PIN length from 8 to 7 fails.
|
||||||
let mut config_params = create_min_pin_config_params(7, None);
|
let mut config_params = create_min_pin_config_params(7, None);
|
||||||
@@ -303,19 +296,18 @@ mod test {
|
|||||||
0xA7, 0x71,
|
0xA7, 0x71,
|
||||||
];
|
];
|
||||||
config_params.pin_uv_auth_param = Some(pin_uv_auth_param);
|
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!(
|
assert_eq!(
|
||||||
config_response,
|
config_response,
|
||||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_POLICY_VIOLATION)
|
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]
|
#[test]
|
||||||
fn test_process_set_min_pin_length_rp_ids() {
|
fn test_process_set_min_pin_length_rp_ids() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin =
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
@@ -325,11 +317,11 @@ mod test {
|
|||||||
let min_pin_length_rp_ids = vec!["example.com".to_string()];
|
let min_pin_length_rp_ids = vec!["example.com".to_string()];
|
||||||
let config_params =
|
let config_params =
|
||||||
create_min_pin_config_params(min_pin_length, Some(min_pin_length_rp_ids.clone()));
|
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!(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!(
|
assert_eq!(
|
||||||
persistent_store.min_pin_length_rp_ids(),
|
storage::min_pin_length_rp_ids(&mut env),
|
||||||
Ok(min_pin_length_rp_ids)
|
Ok(min_pin_length_rp_ids)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -337,7 +329,7 @@ mod test {
|
|||||||
let min_pin_length = 8;
|
let min_pin_length = 8;
|
||||||
let min_pin_length_rp_ids = vec!["another.example.com".to_string()];
|
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.
|
// 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 =
|
let mut config_params =
|
||||||
create_min_pin_config_params(min_pin_length, Some(min_pin_length_rp_ids.clone()));
|
create_min_pin_config_params(min_pin_length, Some(min_pin_length_rp_ids.clone()));
|
||||||
let pin_uv_auth_param = vec![
|
let pin_uv_auth_param = vec![
|
||||||
@@ -345,11 +337,11 @@ mod test {
|
|||||||
0xD6, 0xDA,
|
0xD6, 0xDA,
|
||||||
];
|
];
|
||||||
config_params.pin_uv_auth_param = Some(pin_uv_auth_param.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, Ok(ResponseData::AuthenticatorConfig));
|
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!(
|
assert_eq!(
|
||||||
persistent_store.min_pin_length_rp_ids(),
|
storage::min_pin_length_rp_ids(&mut env),
|
||||||
Ok(min_pin_length_rp_ids.clone())
|
Ok(min_pin_length_rp_ids.clone())
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -358,14 +350,14 @@ mod test {
|
|||||||
let mut config_params =
|
let mut config_params =
|
||||||
create_min_pin_config_params(9, Some(min_pin_length_rp_ids.clone()));
|
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());
|
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!(
|
assert_eq!(
|
||||||
config_response,
|
config_response,
|
||||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
|
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!(
|
assert_eq!(
|
||||||
persistent_store.min_pin_length_rp_ids(),
|
storage::min_pin_length_rp_ids(&mut env),
|
||||||
Ok(min_pin_length_rp_ids.clone())
|
Ok(min_pin_length_rp_ids.clone())
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -376,28 +368,27 @@ mod test {
|
|||||||
Some(vec!["counter.example.com".to_string()]),
|
Some(vec!["counter.example.com".to_string()]),
|
||||||
);
|
);
|
||||||
config_params.pin_uv_auth_param = Some(pin_uv_auth_param);
|
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!(
|
assert_eq!(
|
||||||
config_response,
|
config_response,
|
||||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
|
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!(
|
assert_eq!(
|
||||||
persistent_store.min_pin_length_rp_ids(),
|
storage::min_pin_length_rp_ids(&mut env),
|
||||||
Ok(min_pin_length_rp_ids)
|
Ok(min_pin_length_rp_ids)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_process_set_min_pin_length_force_pin_change_implicit() {
|
fn test_process_set_min_pin_length_force_pin_change_implicit() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin =
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
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.
|
// Increase min PIN, force PIN change.
|
||||||
let min_pin_length = 6;
|
let min_pin_length = 6;
|
||||||
let mut config_params = create_min_pin_config_params(min_pin_length, None);
|
let mut config_params = create_min_pin_config_params(min_pin_length, None);
|
||||||
@@ -406,28 +397,27 @@ mod test {
|
|||||||
0xA8, 0xC8,
|
0xA8, 0xC8,
|
||||||
]);
|
]);
|
||||||
config_params.pin_uv_auth_param = pin_uv_auth_param;
|
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!(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.has_force_pin_change(), Ok(true));
|
assert_eq!(storage::has_force_pin_change(&mut env), Ok(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_process_set_min_pin_length_force_pin_change_explicit() {
|
fn test_process_set_min_pin_length_force_pin_change_explicit() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin =
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
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![
|
let pin_uv_auth_param = Some(vec![
|
||||||
0xE3, 0x74, 0xF4, 0x27, 0xBE, 0x7D, 0x40, 0xB5, 0x71, 0xB6, 0xB4, 0x1A, 0xD2, 0xC1,
|
0xE3, 0x74, 0xF4, 0x27, 0xBE, 0x7D, 0x40, 0xB5, 0x71, 0xB6, 0xB4, 0x1A, 0xD2, 0xC1,
|
||||||
0x53, 0xD7,
|
0x53, 0xD7,
|
||||||
]);
|
]);
|
||||||
let set_min_pin_length_params = SetMinPinLengthParams {
|
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,
|
min_pin_length_rp_ids: None,
|
||||||
force_change_pin: Some(true),
|
force_change_pin: Some(true),
|
||||||
};
|
};
|
||||||
@@ -439,16 +429,15 @@ mod test {
|
|||||||
pin_uv_auth_param,
|
pin_uv_auth_param,
|
||||||
pin_uv_auth_protocol: Some(PinUvAuthProtocol::V1),
|
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!(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]
|
#[test]
|
||||||
fn test_process_config_vendor_prototype() {
|
fn test_process_config_vendor_prototype() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin =
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
@@ -459,7 +448,7 @@ mod test {
|
|||||||
pin_uv_auth_param: None,
|
pin_uv_auth_param: None,
|
||||||
pin_uv_auth_protocol: 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!(
|
assert_eq!(
|
||||||
config_response,
|
config_response,
|
||||||
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
||||||
|
|||||||
@@ -21,8 +21,9 @@ use super::data_formats::{
|
|||||||
};
|
};
|
||||||
use super::response::{AuthenticatorCredentialManagementResponse, ResponseData};
|
use super::response::{AuthenticatorCredentialManagementResponse, ResponseData};
|
||||||
use super::status_code::Ctap2StatusCode;
|
use super::status_code::Ctap2StatusCode;
|
||||||
use super::storage::PersistentStore;
|
|
||||||
use super::{StatefulCommand, StatefulPermission};
|
use super::{StatefulCommand, StatefulPermission};
|
||||||
|
use crate::ctap::storage;
|
||||||
|
use crate::env::Env;
|
||||||
use alloc::collections::BTreeSet;
|
use alloc::collections::BTreeSet;
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
@@ -32,12 +33,10 @@ use crypto::Hash256;
|
|||||||
use libtock_drivers::timer::ClockValue;
|
use libtock_drivers::timer::ClockValue;
|
||||||
|
|
||||||
/// Generates a set with all existing RP IDs.
|
/// Generates a set with all existing RP IDs.
|
||||||
fn get_stored_rp_ids(
|
fn get_stored_rp_ids(env: &mut impl Env) -> Result<BTreeSet<String>, Ctap2StatusCode> {
|
||||||
persistent_store: &PersistentStore,
|
|
||||||
) -> Result<BTreeSet<String>, Ctap2StatusCode> {
|
|
||||||
let mut rp_set = BTreeSet::new();
|
let mut rp_set = BTreeSet::new();
|
||||||
let mut iter_result = Ok(());
|
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);
|
rp_set.insert(credential.rp_id);
|
||||||
}
|
}
|
||||||
iter_result?;
|
iter_result?;
|
||||||
@@ -109,7 +108,7 @@ fn enumerate_credentials_response(
|
|||||||
///
|
///
|
||||||
/// Either no RP ID is associated, or the RP ID matches the stored credential.
|
/// Either no RP ID is associated, or the RP ID matches the stored credential.
|
||||||
fn check_rp_id_permissions(
|
fn check_rp_id_permissions(
|
||||||
persistent_store: &mut PersistentStore,
|
env: &mut impl Env,
|
||||||
client_pin: &mut ClientPin,
|
client_pin: &mut ClientPin,
|
||||||
credential_id: &[u8],
|
credential_id: &[u8],
|
||||||
) -> Result<(), Ctap2StatusCode> {
|
) -> Result<(), Ctap2StatusCode> {
|
||||||
@@ -117,18 +116,18 @@ fn check_rp_id_permissions(
|
|||||||
if client_pin.has_no_rp_id_permission().is_ok() {
|
if client_pin.has_no_rp_id_permission().is_ok() {
|
||||||
return 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)
|
client_pin.has_no_or_rp_id_permission(&credential.rp_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes the subcommand getCredsMetadata for CredentialManagement.
|
/// Processes the subcommand getCredsMetadata for CredentialManagement.
|
||||||
fn process_get_creds_metadata(
|
fn process_get_creds_metadata(
|
||||||
persistent_store: &PersistentStore,
|
env: &mut impl Env,
|
||||||
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
|
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
|
||||||
Ok(AuthenticatorCredentialManagementResponse {
|
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(
|
max_possible_remaining_resident_credentials_count: Some(
|
||||||
persistent_store.remaining_credentials()? as u64,
|
storage::remaining_credentials(env)? as u64,
|
||||||
),
|
),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
@@ -136,11 +135,11 @@ fn process_get_creds_metadata(
|
|||||||
|
|
||||||
/// Processes the subcommand enumerateRPsBegin for CredentialManagement.
|
/// Processes the subcommand enumerateRPsBegin for CredentialManagement.
|
||||||
fn process_enumerate_rps_begin(
|
fn process_enumerate_rps_begin(
|
||||||
persistent_store: &PersistentStore,
|
env: &mut impl Env,
|
||||||
stateful_command_permission: &mut StatefulPermission,
|
stateful_command_permission: &mut StatefulPermission,
|
||||||
now: ClockValue,
|
now: ClockValue,
|
||||||
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
|
) -> 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();
|
let total_rps = rp_set.len();
|
||||||
|
|
||||||
if total_rps > 1 {
|
if total_rps > 1 {
|
||||||
@@ -156,11 +155,11 @@ fn process_enumerate_rps_begin(
|
|||||||
|
|
||||||
/// Processes the subcommand enumerateRPsGetNextRP for CredentialManagement.
|
/// Processes the subcommand enumerateRPsGetNextRP for CredentialManagement.
|
||||||
fn process_enumerate_rps_get_next_rp(
|
fn process_enumerate_rps_get_next_rp(
|
||||||
persistent_store: &PersistentStore,
|
env: &mut impl Env,
|
||||||
stateful_command_permission: &mut StatefulPermission,
|
stateful_command_permission: &mut StatefulPermission,
|
||||||
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
|
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
|
||||||
let rp_id_index = stateful_command_permission.next_enumerate_rp()?;
|
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.
|
// A BTreeSet is already sorted.
|
||||||
let rp_id = rp_set
|
let rp_id = rp_set
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -171,7 +170,7 @@ fn process_enumerate_rps_get_next_rp(
|
|||||||
|
|
||||||
/// Processes the subcommand enumerateCredentialsBegin for CredentialManagement.
|
/// Processes the subcommand enumerateCredentialsBegin for CredentialManagement.
|
||||||
fn process_enumerate_credentials_begin(
|
fn process_enumerate_credentials_begin(
|
||||||
persistent_store: &PersistentStore,
|
env: &mut impl Env,
|
||||||
stateful_command_permission: &mut StatefulPermission,
|
stateful_command_permission: &mut StatefulPermission,
|
||||||
client_pin: &mut ClientPin,
|
client_pin: &mut ClientPin,
|
||||||
sub_command_params: CredentialManagementSubCommandParameters,
|
sub_command_params: CredentialManagementSubCommandParameters,
|
||||||
@@ -182,7 +181,7 @@ fn process_enumerate_credentials_begin(
|
|||||||
.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?;
|
.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?;
|
||||||
client_pin.has_no_or_rp_id_hash_permission(&rp_id_hash[..])?;
|
client_pin.has_no_or_rp_id_hash_permission(&rp_id_hash[..])?;
|
||||||
let mut iter_result = Ok(());
|
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
|
let mut rp_credentials: Vec<usize> = iter
|
||||||
.filter_map(|(key, credential)| {
|
.filter_map(|(key, credential)| {
|
||||||
let cred_rp_id_hash = Sha256::hash(credential.rp_id.as_bytes());
|
let cred_rp_id_hash = Sha256::hash(credential.rp_id.as_bytes());
|
||||||
@@ -198,7 +197,7 @@ fn process_enumerate_credentials_begin(
|
|||||||
let current_key = rp_credentials
|
let current_key = rp_credentials
|
||||||
.pop()
|
.pop()
|
||||||
.ok_or(Ctap2StatusCode::CTAP2_ERR_NO_CREDENTIALS)?;
|
.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 {
|
if total_credentials > 1 {
|
||||||
stateful_command_permission
|
stateful_command_permission
|
||||||
.set_command(now, StatefulCommand::EnumerateCredentials(rp_credentials));
|
.set_command(now, StatefulCommand::EnumerateCredentials(rp_credentials));
|
||||||
@@ -208,17 +207,17 @@ fn process_enumerate_credentials_begin(
|
|||||||
|
|
||||||
/// Processes the subcommand enumerateCredentialsGetNextCredential for CredentialManagement.
|
/// Processes the subcommand enumerateCredentialsGetNextCredential for CredentialManagement.
|
||||||
fn process_enumerate_credentials_get_next_credential(
|
fn process_enumerate_credentials_get_next_credential(
|
||||||
persistent_store: &PersistentStore,
|
env: &mut impl Env,
|
||||||
stateful_command_permission: &mut StatefulPermission,
|
stateful_command_permission: &mut StatefulPermission,
|
||||||
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
|
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
|
||||||
let credential_key = stateful_command_permission.next_enumerate_credential()?;
|
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)
|
enumerate_credentials_response(credential, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes the subcommand deleteCredential for CredentialManagement.
|
/// Processes the subcommand deleteCredential for CredentialManagement.
|
||||||
fn process_delete_credential(
|
fn process_delete_credential(
|
||||||
persistent_store: &mut PersistentStore,
|
env: &mut impl Env,
|
||||||
client_pin: &mut ClientPin,
|
client_pin: &mut ClientPin,
|
||||||
sub_command_params: CredentialManagementSubCommandParameters,
|
sub_command_params: CredentialManagementSubCommandParameters,
|
||||||
) -> Result<(), Ctap2StatusCode> {
|
) -> Result<(), Ctap2StatusCode> {
|
||||||
@@ -226,13 +225,13 @@ fn process_delete_credential(
|
|||||||
.credential_id
|
.credential_id
|
||||||
.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?
|
.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?
|
||||||
.key_id;
|
.key_id;
|
||||||
check_rp_id_permissions(persistent_store, client_pin, &credential_id)?;
|
check_rp_id_permissions(env, client_pin, &credential_id)?;
|
||||||
persistent_store.delete_credential(&credential_id)
|
storage::delete_credential(env, &credential_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes the subcommand updateUserInformation for CredentialManagement.
|
/// Processes the subcommand updateUserInformation for CredentialManagement.
|
||||||
fn process_update_user_information(
|
fn process_update_user_information(
|
||||||
persistent_store: &mut PersistentStore,
|
env: &mut impl Env,
|
||||||
client_pin: &mut ClientPin,
|
client_pin: &mut ClientPin,
|
||||||
sub_command_params: CredentialManagementSubCommandParameters,
|
sub_command_params: CredentialManagementSubCommandParameters,
|
||||||
) -> Result<(), Ctap2StatusCode> {
|
) -> Result<(), Ctap2StatusCode> {
|
||||||
@@ -243,13 +242,13 @@ fn process_update_user_information(
|
|||||||
let user = sub_command_params
|
let user = sub_command_params
|
||||||
.user
|
.user
|
||||||
.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?;
|
.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?;
|
||||||
check_rp_id_permissions(persistent_store, client_pin, &credential_id)?;
|
check_rp_id_permissions(env, client_pin, &credential_id)?;
|
||||||
persistent_store.update_credential(&credential_id, user)
|
storage::update_credential(env, &credential_id, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes the CredentialManagement command and all its subcommands.
|
/// Processes the CredentialManagement command and all its subcommands.
|
||||||
pub fn process_credential_management(
|
pub fn process_credential_management(
|
||||||
persistent_store: &mut PersistentStore,
|
env: &mut impl Env,
|
||||||
stateful_command_permission: &mut StatefulPermission,
|
stateful_command_permission: &mut StatefulPermission,
|
||||||
client_pin: &mut ClientPin,
|
client_pin: &mut ClientPin,
|
||||||
cred_management_params: AuthenticatorCredentialManagementParameters,
|
cred_management_params: AuthenticatorCredentialManagementParameters,
|
||||||
@@ -305,37 +304,34 @@ pub fn process_credential_management(
|
|||||||
let response = match sub_command {
|
let response = match sub_command {
|
||||||
CredentialManagementSubCommand::GetCredsMetadata => {
|
CredentialManagementSubCommand::GetCredsMetadata => {
|
||||||
client_pin.has_no_rp_id_permission()?;
|
client_pin.has_no_rp_id_permission()?;
|
||||||
Some(process_get_creds_metadata(persistent_store)?)
|
Some(process_get_creds_metadata(env)?)
|
||||||
}
|
}
|
||||||
CredentialManagementSubCommand::EnumerateRpsBegin => {
|
CredentialManagementSubCommand::EnumerateRpsBegin => {
|
||||||
client_pin.has_no_rp_id_permission()?;
|
client_pin.has_no_rp_id_permission()?;
|
||||||
Some(process_enumerate_rps_begin(
|
Some(process_enumerate_rps_begin(
|
||||||
persistent_store,
|
env,
|
||||||
stateful_command_permission,
|
stateful_command_permission,
|
||||||
now,
|
now,
|
||||||
)?)
|
)?)
|
||||||
}
|
}
|
||||||
CredentialManagementSubCommand::EnumerateRpsGetNextRp => Some(
|
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 => {
|
CredentialManagementSubCommand::EnumerateCredentialsBegin => {
|
||||||
Some(process_enumerate_credentials_begin(
|
Some(process_enumerate_credentials_begin(
|
||||||
persistent_store,
|
env,
|
||||||
stateful_command_permission,
|
stateful_command_permission,
|
||||||
client_pin,
|
client_pin,
|
||||||
sub_command_params.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
sub_command_params.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
||||||
now,
|
now,
|
||||||
)?)
|
)?)
|
||||||
}
|
}
|
||||||
CredentialManagementSubCommand::EnumerateCredentialsGetNextCredential => {
|
CredentialManagementSubCommand::EnumerateCredentialsGetNextCredential => Some(
|
||||||
Some(process_enumerate_credentials_get_next_credential(
|
process_enumerate_credentials_get_next_credential(env, stateful_command_permission)?,
|
||||||
persistent_store,
|
),
|
||||||
stateful_command_permission,
|
|
||||||
)?)
|
|
||||||
}
|
|
||||||
CredentialManagementSubCommand::DeleteCredential => {
|
CredentialManagementSubCommand::DeleteCredential => {
|
||||||
process_delete_credential(
|
process_delete_credential(
|
||||||
persistent_store,
|
env,
|
||||||
client_pin,
|
client_pin,
|
||||||
sub_command_params.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
sub_command_params.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
||||||
)?;
|
)?;
|
||||||
@@ -343,7 +339,7 @@ pub fn process_credential_management(
|
|||||||
}
|
}
|
||||||
CredentialManagementSubCommand::UpdateUserInformation => {
|
CredentialManagementSubCommand::UpdateUserInformation => {
|
||||||
process_update_user_information(
|
process_update_user_information(
|
||||||
persistent_store,
|
env,
|
||||||
client_pin,
|
client_pin,
|
||||||
sub_command_params.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
sub_command_params.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
||||||
)?;
|
)?;
|
||||||
@@ -395,7 +391,7 @@ mod test {
|
|||||||
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
|
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
|
||||||
ctap_state.client_pin = client_pin;
|
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 management_data = vec![CredentialManagementSubCommand::GetCredsMetadata as u8];
|
||||||
let pin_uv_auth_param = authenticate_pin_uv_auth_token(
|
let pin_uv_auth_param = authenticate_pin_uv_auth_token(
|
||||||
&pin_uv_auth_token,
|
&pin_uv_auth_token,
|
||||||
@@ -410,7 +406,7 @@ mod test {
|
|||||||
pin_uv_auth_param: Some(pin_uv_auth_param.clone()),
|
pin_uv_auth_param: Some(pin_uv_auth_param.clone()),
|
||||||
};
|
};
|
||||||
let cred_management_response = process_credential_management(
|
let cred_management_response = process_credential_management(
|
||||||
&mut ctap_state.persistent_store,
|
&mut env,
|
||||||
&mut ctap_state.stateful_command_permission,
|
&mut ctap_state.stateful_command_permission,
|
||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
@@ -426,10 +422,7 @@ mod test {
|
|||||||
_ => panic!("Invalid response type"),
|
_ => panic!("Invalid response type"),
|
||||||
};
|
};
|
||||||
|
|
||||||
ctap_state
|
storage::store_credential(&mut env, credential_source).unwrap();
|
||||||
.persistent_store
|
|
||||||
.store_credential(credential_source)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let cred_management_params = AuthenticatorCredentialManagementParameters {
|
let cred_management_params = AuthenticatorCredentialManagementParameters {
|
||||||
sub_command: CredentialManagementSubCommand::GetCredsMetadata,
|
sub_command: CredentialManagementSubCommand::GetCredsMetadata,
|
||||||
@@ -438,7 +431,7 @@ mod test {
|
|||||||
pin_uv_auth_param: Some(pin_uv_auth_param),
|
pin_uv_auth_param: Some(pin_uv_auth_param),
|
||||||
};
|
};
|
||||||
let cred_management_response = process_credential_management(
|
let cred_management_response = process_credential_management(
|
||||||
&mut ctap_state.persistent_store,
|
&mut env,
|
||||||
&mut ctap_state.stateful_command_permission,
|
&mut ctap_state.stateful_command_permission,
|
||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
@@ -480,16 +473,10 @@ mod test {
|
|||||||
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
|
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
|
||||||
ctap_state.client_pin = client_pin;
|
ctap_state.client_pin = client_pin;
|
||||||
|
|
||||||
ctap_state
|
storage::store_credential(&mut env, credential_source1).unwrap();
|
||||||
.persistent_store
|
storage::store_credential(&mut env, credential_source2).unwrap();
|
||||||
.store_credential(credential_source1)
|
|
||||||
.unwrap();
|
|
||||||
ctap_state
|
|
||||||
.persistent_store
|
|
||||||
.store_credential(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![
|
let pin_uv_auth_param = Some(vec![
|
||||||
0x1A, 0xA4, 0x96, 0xDA, 0x62, 0x80, 0x28, 0x13, 0xEB, 0x32, 0xB9, 0xF1, 0xD2, 0xA9,
|
0x1A, 0xA4, 0x96, 0xDA, 0x62, 0x80, 0x28, 0x13, 0xEB, 0x32, 0xB9, 0xF1, 0xD2, 0xA9,
|
||||||
0xD0, 0xD1,
|
0xD0, 0xD1,
|
||||||
@@ -502,7 +489,7 @@ mod test {
|
|||||||
pin_uv_auth_param,
|
pin_uv_auth_param,
|
||||||
};
|
};
|
||||||
let cred_management_response = process_credential_management(
|
let cred_management_response = process_credential_management(
|
||||||
&mut ctap_state.persistent_store,
|
&mut env,
|
||||||
&mut ctap_state.stateful_command_permission,
|
&mut ctap_state.stateful_command_permission,
|
||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
@@ -526,7 +513,7 @@ mod test {
|
|||||||
pin_uv_auth_param: None,
|
pin_uv_auth_param: None,
|
||||||
};
|
};
|
||||||
let cred_management_response = process_credential_management(
|
let cred_management_response = process_credential_management(
|
||||||
&mut ctap_state.persistent_store,
|
&mut env,
|
||||||
&mut ctap_state.stateful_command_permission,
|
&mut ctap_state.stateful_command_permission,
|
||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
@@ -551,7 +538,7 @@ mod test {
|
|||||||
pin_uv_auth_param: None,
|
pin_uv_auth_param: None,
|
||||||
};
|
};
|
||||||
let cred_management_response = process_credential_management(
|
let cred_management_response = process_credential_management(
|
||||||
&mut ctap_state.persistent_store,
|
&mut env,
|
||||||
&mut ctap_state.stateful_command_permission,
|
&mut ctap_state.stateful_command_permission,
|
||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
@@ -579,13 +566,10 @@ mod test {
|
|||||||
for i in 0..NUM_CREDENTIALS {
|
for i in 0..NUM_CREDENTIALS {
|
||||||
let mut credential = credential_source.clone();
|
let mut credential = credential_source.clone();
|
||||||
credential.rp_id = i.to_string();
|
credential.rp_id = i.to_string();
|
||||||
ctap_state
|
storage::store_credential(&mut env, credential).unwrap();
|
||||||
.persistent_store
|
|
||||||
.store_credential(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![
|
let pin_uv_auth_param = Some(vec![
|
||||||
0x1A, 0xA4, 0x96, 0xDA, 0x62, 0x80, 0x28, 0x13, 0xEB, 0x32, 0xB9, 0xF1, 0xD2, 0xA9,
|
0x1A, 0xA4, 0x96, 0xDA, 0x62, 0x80, 0x28, 0x13, 0xEB, 0x32, 0xB9, 0xF1, 0xD2, 0xA9,
|
||||||
0xD0, 0xD1,
|
0xD0, 0xD1,
|
||||||
@@ -603,7 +587,7 @@ mod test {
|
|||||||
|
|
||||||
for _ in 0..NUM_CREDENTIALS {
|
for _ in 0..NUM_CREDENTIALS {
|
||||||
let cred_management_response = process_credential_management(
|
let cred_management_response = process_credential_management(
|
||||||
&mut ctap_state.persistent_store,
|
&mut env,
|
||||||
&mut ctap_state.stateful_command_permission,
|
&mut ctap_state.stateful_command_permission,
|
||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
@@ -633,7 +617,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let cred_management_response = process_credential_management(
|
let cred_management_response = process_credential_management(
|
||||||
&mut ctap_state.persistent_store,
|
&mut env,
|
||||||
&mut ctap_state.stateful_command_permission,
|
&mut ctap_state.stateful_command_permission,
|
||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
@@ -662,16 +646,10 @@ mod test {
|
|||||||
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
|
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
|
||||||
ctap_state.client_pin = client_pin;
|
ctap_state.client_pin = client_pin;
|
||||||
|
|
||||||
ctap_state
|
storage::store_credential(&mut env, credential_source1).unwrap();
|
||||||
.persistent_store
|
storage::store_credential(&mut env, credential_source2).unwrap();
|
||||||
.store_credential(credential_source1)
|
|
||||||
.unwrap();
|
|
||||||
ctap_state
|
|
||||||
.persistent_store
|
|
||||||
.store_credential(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![
|
let pin_uv_auth_param = Some(vec![
|
||||||
0xF8, 0xB0, 0x3C, 0xC1, 0xD5, 0x58, 0x9C, 0xB7, 0x4D, 0x42, 0xA1, 0x64, 0x14, 0x28,
|
0xF8, 0xB0, 0x3C, 0xC1, 0xD5, 0x58, 0x9C, 0xB7, 0x4D, 0x42, 0xA1, 0x64, 0x14, 0x28,
|
||||||
0x2B, 0x68,
|
0x2B, 0x68,
|
||||||
@@ -691,7 +669,7 @@ mod test {
|
|||||||
pin_uv_auth_param,
|
pin_uv_auth_param,
|
||||||
};
|
};
|
||||||
let cred_management_response = process_credential_management(
|
let cred_management_response = process_credential_management(
|
||||||
&mut ctap_state.persistent_store,
|
&mut env,
|
||||||
&mut ctap_state.stateful_command_permission,
|
&mut ctap_state.stateful_command_permission,
|
||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
@@ -714,7 +692,7 @@ mod test {
|
|||||||
pin_uv_auth_param: None,
|
pin_uv_auth_param: None,
|
||||||
};
|
};
|
||||||
let cred_management_response = process_credential_management(
|
let cred_management_response = process_credential_management(
|
||||||
&mut ctap_state.persistent_store,
|
&mut env,
|
||||||
&mut ctap_state.stateful_command_permission,
|
&mut ctap_state.stateful_command_permission,
|
||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
@@ -738,7 +716,7 @@ mod test {
|
|||||||
pin_uv_auth_param: None,
|
pin_uv_auth_param: None,
|
||||||
};
|
};
|
||||||
let cred_management_response = process_credential_management(
|
let cred_management_response = process_credential_management(
|
||||||
&mut ctap_state.persistent_store,
|
&mut env,
|
||||||
&mut ctap_state.stateful_command_permission,
|
&mut ctap_state.stateful_command_permission,
|
||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
@@ -763,12 +741,9 @@ mod test {
|
|||||||
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
|
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
|
||||||
ctap_state.client_pin = client_pin;
|
ctap_state.client_pin = client_pin;
|
||||||
|
|
||||||
ctap_state
|
storage::store_credential(&mut env, credential_source).unwrap();
|
||||||
.persistent_store
|
|
||||||
.store_credential(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![
|
let pin_uv_auth_param = Some(vec![
|
||||||
0xBD, 0xE3, 0xEF, 0x8A, 0x77, 0x01, 0xB1, 0x69, 0x19, 0xE6, 0x62, 0xB9, 0x9B, 0x89,
|
0xBD, 0xE3, 0xEF, 0x8A, 0x77, 0x01, 0xB1, 0x69, 0x19, 0xE6, 0x62, 0xB9, 0x9B, 0x89,
|
||||||
0x9C, 0x64,
|
0x9C, 0x64,
|
||||||
@@ -791,7 +766,7 @@ mod test {
|
|||||||
pin_uv_auth_param: pin_uv_auth_param.clone(),
|
pin_uv_auth_param: pin_uv_auth_param.clone(),
|
||||||
};
|
};
|
||||||
let cred_management_response = process_credential_management(
|
let cred_management_response = process_credential_management(
|
||||||
&mut ctap_state.persistent_store,
|
&mut env,
|
||||||
&mut ctap_state.stateful_command_permission,
|
&mut ctap_state.stateful_command_permission,
|
||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
@@ -809,7 +784,7 @@ mod test {
|
|||||||
pin_uv_auth_param,
|
pin_uv_auth_param,
|
||||||
};
|
};
|
||||||
let cred_management_response = process_credential_management(
|
let cred_management_response = process_credential_management(
|
||||||
&mut ctap_state.persistent_store,
|
&mut env,
|
||||||
&mut ctap_state.stateful_command_permission,
|
&mut ctap_state.stateful_command_permission,
|
||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
@@ -834,12 +809,9 @@ mod test {
|
|||||||
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
|
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
|
||||||
ctap_state.client_pin = client_pin;
|
ctap_state.client_pin = client_pin;
|
||||||
|
|
||||||
ctap_state
|
storage::store_credential(&mut env, credential_source).unwrap();
|
||||||
.persistent_store
|
|
||||||
.store_credential(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![
|
let pin_uv_auth_param = Some(vec![
|
||||||
0xA5, 0x55, 0x8F, 0x03, 0xC3, 0xD3, 0x73, 0x1C, 0x07, 0xDA, 0x1F, 0x8C, 0xC7, 0xBD,
|
0xA5, 0x55, 0x8F, 0x03, 0xC3, 0xD3, 0x73, 0x1C, 0x07, 0xDA, 0x1F, 0x8C, 0xC7, 0xBD,
|
||||||
0x9D, 0xB7,
|
0x9D, 0xB7,
|
||||||
@@ -868,7 +840,7 @@ mod test {
|
|||||||
pin_uv_auth_param,
|
pin_uv_auth_param,
|
||||||
};
|
};
|
||||||
let cred_management_response = process_credential_management(
|
let cred_management_response = process_credential_management(
|
||||||
&mut ctap_state.persistent_store,
|
&mut env,
|
||||||
&mut ctap_state.stateful_command_permission,
|
&mut ctap_state.stateful_command_permission,
|
||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
@@ -879,9 +851,8 @@ mod test {
|
|||||||
Ok(ResponseData::AuthenticatorCredentialManagement(None))
|
Ok(ResponseData::AuthenticatorCredentialManagement(None))
|
||||||
);
|
);
|
||||||
|
|
||||||
let updated_credential = ctap_state
|
let updated_credential =
|
||||||
.persistent_store
|
storage::find_credential(&mut env, "example.com", &[0x1D; 32], false)
|
||||||
.find_credential("example.com", &[0x1D; 32], false)
|
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(updated_credential.user_handle, vec![0x01]);
|
assert_eq!(updated_credential.user_handle, vec![0x01]);
|
||||||
@@ -898,7 +869,7 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
|
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 {
|
let cred_management_params = AuthenticatorCredentialManagementParameters {
|
||||||
sub_command: CredentialManagementSubCommand::GetCredsMetadata,
|
sub_command: CredentialManagementSubCommand::GetCredsMetadata,
|
||||||
@@ -907,7 +878,7 @@ mod test {
|
|||||||
pin_uv_auth_param: Some(vec![0u8; 16]),
|
pin_uv_auth_param: Some(vec![0u8; 16]),
|
||||||
};
|
};
|
||||||
let cred_management_response = process_credential_management(
|
let cred_management_response = process_credential_management(
|
||||||
&mut ctap_state.persistent_store,
|
&mut env,
|
||||||
&mut ctap_state.stateful_command_permission,
|
&mut ctap_state.stateful_command_permission,
|
||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
use super::apdu::{Apdu, ApduStatusCode};
|
use super::apdu::{Apdu, ApduStatusCode};
|
||||||
use super::CtapState;
|
use super::CtapState;
|
||||||
|
use crate::ctap::storage;
|
||||||
use crate::env::Env;
|
use crate::env::Env;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use arrayref::array_ref;
|
use arrayref::array_ref;
|
||||||
@@ -185,7 +186,7 @@ impl Ctap1Command {
|
|||||||
clock_value: ClockValue,
|
clock_value: ClockValue,
|
||||||
) -> Result<Vec<u8>, Ctap1StatusCode> {
|
) -> Result<Vec<u8>, Ctap1StatusCode> {
|
||||||
if !ctap_state
|
if !ctap_state
|
||||||
.allows_ctap1()
|
.allows_ctap1(env)
|
||||||
.map_err(|_| Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?
|
.map_err(|_| Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?
|
||||||
{
|
{
|
||||||
return Err(Ctap1StatusCode::SW_COMMAND_NOT_ALLOWED);
|
return Err(Ctap1StatusCode::SW_COMMAND_NOT_ALLOWED);
|
||||||
@@ -259,14 +260,10 @@ impl Ctap1Command {
|
|||||||
return Err(Ctap1StatusCode::SW_INTERNAL_EXCEPTION);
|
return Err(Ctap1StatusCode::SW_INTERNAL_EXCEPTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
let certificate = ctap_state
|
let certificate = storage::attestation_certificate(env)
|
||||||
.persistent_store
|
|
||||||
.attestation_certificate()
|
|
||||||
.map_err(|_| Ctap1StatusCode::SW_MEMERR)?
|
.map_err(|_| Ctap1StatusCode::SW_MEMERR)?
|
||||||
.ok_or(Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?;
|
.ok_or(Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?;
|
||||||
let private_key = ctap_state
|
let private_key = storage::attestation_private_key(env)
|
||||||
.persistent_store
|
|
||||||
.attestation_private_key()
|
|
||||||
.map_err(|_| Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?
|
.map_err(|_| Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?
|
||||||
.ok_or(Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?;
|
.ok_or(Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?;
|
||||||
|
|
||||||
@@ -316,7 +313,7 @@ impl Ctap1Command {
|
|||||||
ctap_state: &mut CtapState,
|
ctap_state: &mut CtapState,
|
||||||
) -> Result<Vec<u8>, Ctap1StatusCode> {
|
) -> Result<Vec<u8>, Ctap1StatusCode> {
|
||||||
let credential_source = ctap_state
|
let credential_source = ctap_state
|
||||||
.decrypt_credential_source(key_handle, &application)
|
.decrypt_credential_source(env, key_handle, &application)
|
||||||
.map_err(|_| Ctap1StatusCode::SW_WRONG_DATA)?;
|
.map_err(|_| Ctap1StatusCode::SW_WRONG_DATA)?;
|
||||||
if let Some(credential_source) = credential_source {
|
if let Some(credential_source) = credential_source {
|
||||||
if flags == Ctap1Flags::CheckOnly {
|
if flags == Ctap1Flags::CheckOnly {
|
||||||
@@ -326,7 +323,11 @@ impl Ctap1Command {
|
|||||||
.increment_global_signature_counter(env)
|
.increment_global_signature_counter(env)
|
||||||
.map_err(|_| Ctap1StatusCode::SW_WRONG_DATA)?;
|
.map_err(|_| Ctap1StatusCode::SW_WRONG_DATA)?;
|
||||||
let mut signature_data = ctap_state
|
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)?;
|
.map_err(|_| Ctap1StatusCode::SW_WRONG_DATA)?;
|
||||||
signature_data.extend(&challenge);
|
signature_data.extend(&challenge);
|
||||||
let signature = credential_source
|
let signature = credential_source
|
||||||
@@ -400,7 +401,7 @@ mod test {
|
|||||||
env.user_presence()
|
env.user_presence()
|
||||||
.set(|_| panic!("Unexpected user presence check in CTAP1"));
|
.set(|_| panic!("Unexpected user presence check in CTAP1"));
|
||||||
let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE);
|
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 application = [0x0A; 32];
|
||||||
let message = create_register_message(&application);
|
let message = create_register_message(&application);
|
||||||
@@ -428,10 +429,7 @@ mod test {
|
|||||||
assert_eq!(response, Err(Ctap1StatusCode::SW_INTERNAL_EXCEPTION));
|
assert_eq!(response, Err(Ctap1StatusCode::SW_INTERNAL_EXCEPTION));
|
||||||
|
|
||||||
let fake_key = [0x41u8; key_material::ATTESTATION_PRIVATE_KEY_LENGTH];
|
let fake_key = [0x41u8; key_material::ATTESTATION_PRIVATE_KEY_LENGTH];
|
||||||
assert!(ctap_state
|
assert!(storage::set_attestation_private_key(&mut env, &fake_key).is_ok());
|
||||||
.persistent_store
|
|
||||||
.set_attestation_private_key(&fake_key)
|
|
||||||
.is_ok());
|
|
||||||
ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE);
|
ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE);
|
||||||
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
|
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
|
||||||
let response =
|
let response =
|
||||||
@@ -440,10 +438,7 @@ mod test {
|
|||||||
assert_eq!(response, Err(Ctap1StatusCode::SW_INTERNAL_EXCEPTION));
|
assert_eq!(response, Err(Ctap1StatusCode::SW_INTERNAL_EXCEPTION));
|
||||||
|
|
||||||
let fake_cert = [0x99u8; 100]; // Arbitrary length
|
let fake_cert = [0x99u8; 100]; // Arbitrary length
|
||||||
assert!(ctap_state
|
assert!(storage::set_attestation_certificate(&mut env, &fake_cert[..]).is_ok());
|
||||||
.persistent_store
|
|
||||||
.set_attestation_certificate(&fake_cert[..])
|
|
||||||
.is_ok());
|
|
||||||
ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE);
|
ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE);
|
||||||
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
|
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
|
||||||
let response =
|
let response =
|
||||||
@@ -452,7 +447,11 @@ mod test {
|
|||||||
assert_eq!(response[0], Ctap1Command::LEGACY_BYTE);
|
assert_eq!(response[0], Ctap1Command::LEGACY_BYTE);
|
||||||
assert_eq!(response[66], CREDENTIAL_ID_SIZE as u8);
|
assert_eq!(response[66], CREDENTIAL_ID_SIZE as u8);
|
||||||
assert!(ctap_state
|
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()
|
.unwrap()
|
||||||
.is_some());
|
.is_some());
|
||||||
const CERT_START: usize = 67 + CREDENTIAL_ID_SIZE;
|
const CERT_START: usize = 67 + CREDENTIAL_ID_SIZE;
|
||||||
@@ -677,10 +676,7 @@ mod test {
|
|||||||
assert_eq!(response[0], 0x01);
|
assert_eq!(response[0], 0x01);
|
||||||
check_signature_counter(
|
check_signature_counter(
|
||||||
array_ref!(response, 1, 4),
|
array_ref!(response, 1, 4),
|
||||||
ctap_state
|
storage::global_signature_counter(&mut env).unwrap(),
|
||||||
.persistent_store
|
|
||||||
.global_signature_counter()
|
|
||||||
.unwrap(),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -709,10 +705,7 @@ mod test {
|
|||||||
assert_eq!(response[0], 0x01);
|
assert_eq!(response[0], 0x01);
|
||||||
check_signature_counter(
|
check_signature_counter(
|
||||||
array_ref!(response, 1, 4),
|
array_ref!(response, 1, 4),
|
||||||
ctap_state
|
storage::global_signature_counter(&mut env).unwrap(),
|
||||||
.persistent_store
|
|
||||||
.global_signature_counter()
|
|
||||||
.unwrap(),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -526,7 +526,7 @@ mod test {
|
|||||||
const DUMMY_TIMESTAMP: Timestamp<isize> = Timestamp::from_ms(0);
|
const DUMMY_TIMESTAMP: Timestamp<isize> = Timestamp::from_ms(0);
|
||||||
|
|
||||||
fn process_messages(
|
fn process_messages(
|
||||||
env: &mut impl Env,
|
env: &mut TestEnv,
|
||||||
ctap_hid: &mut CtapHid,
|
ctap_hid: &mut CtapHid,
|
||||||
ctap_state: &mut CtapState,
|
ctap_state: &mut CtapState,
|
||||||
request: Vec<Message>,
|
request: Vec<Message>,
|
||||||
@@ -550,7 +550,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn cid_from_init(
|
fn cid_from_init(
|
||||||
env: &mut impl Env,
|
env: &mut TestEnv,
|
||||||
ctap_hid: &mut CtapHid,
|
ctap_hid: &mut CtapHid,
|
||||||
ctap_state: &mut CtapState,
|
ctap_state: &mut CtapState,
|
||||||
) -> ChannelID {
|
) -> ChannelID {
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ use super::command::AuthenticatorLargeBlobsParameters;
|
|||||||
use super::customization::MAX_MSG_SIZE;
|
use super::customization::MAX_MSG_SIZE;
|
||||||
use super::response::{AuthenticatorLargeBlobsResponse, ResponseData};
|
use super::response::{AuthenticatorLargeBlobsResponse, ResponseData};
|
||||||
use super::status_code::Ctap2StatusCode;
|
use super::status_code::Ctap2StatusCode;
|
||||||
use super::storage::PersistentStore;
|
use crate::ctap::storage;
|
||||||
|
use crate::env::Env;
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use byteorder::{ByteOrder, LittleEndian};
|
use byteorder::{ByteOrder, LittleEndian};
|
||||||
@@ -46,7 +47,7 @@ impl LargeBlobs {
|
|||||||
/// Process the large blob command.
|
/// Process the large blob command.
|
||||||
pub fn process_command(
|
pub fn process_command(
|
||||||
&mut self,
|
&mut self,
|
||||||
persistent_store: &mut PersistentStore,
|
env: &mut impl Env,
|
||||||
client_pin: &mut ClientPin,
|
client_pin: &mut ClientPin,
|
||||||
large_blobs_params: AuthenticatorLargeBlobsParameters,
|
large_blobs_params: AuthenticatorLargeBlobsParameters,
|
||||||
) -> Result<ResponseData, Ctap2StatusCode> {
|
) -> Result<ResponseData, Ctap2StatusCode> {
|
||||||
@@ -65,7 +66,7 @@ impl LargeBlobs {
|
|||||||
if get > MAX_FRAGMENT_LENGTH || offset.checked_add(get).is_none() {
|
if get > MAX_FRAGMENT_LENGTH || offset.checked_add(get).is_none() {
|
||||||
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_LENGTH);
|
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(
|
return Ok(ResponseData::AuthenticatorLargeBlobs(Some(
|
||||||
AuthenticatorLargeBlobsResponse { config },
|
AuthenticatorLargeBlobsResponse { config },
|
||||||
)));
|
)));
|
||||||
@@ -84,7 +85,7 @@ impl LargeBlobs {
|
|||||||
if offset != self.expected_next_offset {
|
if offset != self.expected_next_offset {
|
||||||
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_SEQ);
|
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 =
|
let pin_uv_auth_param =
|
||||||
pin_uv_auth_param.ok_or(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED)?;
|
pin_uv_auth_param.ok_or(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED)?;
|
||||||
let pin_uv_auth_protocol =
|
let pin_uv_auth_protocol =
|
||||||
@@ -121,7 +122,7 @@ impl LargeBlobs {
|
|||||||
self.buffer = Vec::new();
|
self.buffer = Vec::new();
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_INTEGRITY_FAILURE);
|
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();
|
self.buffer = Vec::new();
|
||||||
}
|
}
|
||||||
return Ok(ResponseData::AuthenticatorLargeBlobs(None));
|
return Ok(ResponseData::AuthenticatorLargeBlobs(None));
|
||||||
@@ -137,13 +138,12 @@ mod test {
|
|||||||
use super::super::data_formats::PinUvAuthProtocol;
|
use super::super::data_formats::PinUvAuthProtocol;
|
||||||
use super::super::pin_protocol::authenticate_pin_uv_auth_token;
|
use super::super::pin_protocol::authenticate_pin_uv_auth_token;
|
||||||
use super::*;
|
use super::*;
|
||||||
use crypto::rng256::ThreadRng256;
|
use crate::env::test::TestEnv;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_process_command_get_empty() {
|
fn test_process_command_get_empty() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin =
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
@@ -162,7 +162,7 @@ mod test {
|
|||||||
pin_uv_auth_protocol: None,
|
pin_uv_auth_protocol: None,
|
||||||
};
|
};
|
||||||
let large_blobs_response =
|
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() {
|
match large_blobs_response.unwrap() {
|
||||||
ResponseData::AuthenticatorLargeBlobs(Some(response)) => {
|
ResponseData::AuthenticatorLargeBlobs(Some(response)) => {
|
||||||
assert_eq!(response.config, large_blob);
|
assert_eq!(response.config, large_blob);
|
||||||
@@ -173,9 +173,8 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_process_command_commit_and_get() {
|
fn test_process_command_commit_and_get() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin =
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
@@ -195,7 +194,7 @@ mod test {
|
|||||||
pin_uv_auth_protocol: None,
|
pin_uv_auth_protocol: None,
|
||||||
};
|
};
|
||||||
let large_blobs_response =
|
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!(
|
assert_eq!(
|
||||||
large_blobs_response,
|
large_blobs_response,
|
||||||
Ok(ResponseData::AuthenticatorLargeBlobs(None))
|
Ok(ResponseData::AuthenticatorLargeBlobs(None))
|
||||||
@@ -210,7 +209,7 @@ mod test {
|
|||||||
pin_uv_auth_protocol: None,
|
pin_uv_auth_protocol: None,
|
||||||
};
|
};
|
||||||
let large_blobs_response =
|
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!(
|
assert_eq!(
|
||||||
large_blobs_response,
|
large_blobs_response,
|
||||||
Ok(ResponseData::AuthenticatorLargeBlobs(None))
|
Ok(ResponseData::AuthenticatorLargeBlobs(None))
|
||||||
@@ -225,7 +224,7 @@ mod test {
|
|||||||
pin_uv_auth_protocol: None,
|
pin_uv_auth_protocol: None,
|
||||||
};
|
};
|
||||||
let large_blobs_response =
|
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() {
|
match large_blobs_response.unwrap() {
|
||||||
ResponseData::AuthenticatorLargeBlobs(Some(response)) => {
|
ResponseData::AuthenticatorLargeBlobs(Some(response)) => {
|
||||||
assert_eq!(response.config, large_blob);
|
assert_eq!(response.config, large_blob);
|
||||||
@@ -236,9 +235,8 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_process_command_commit_unexpected_offset() {
|
fn test_process_command_commit_unexpected_offset() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin =
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
@@ -258,7 +256,7 @@ mod test {
|
|||||||
pin_uv_auth_protocol: None,
|
pin_uv_auth_protocol: None,
|
||||||
};
|
};
|
||||||
let large_blobs_response =
|
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!(
|
assert_eq!(
|
||||||
large_blobs_response,
|
large_blobs_response,
|
||||||
Ok(ResponseData::AuthenticatorLargeBlobs(None))
|
Ok(ResponseData::AuthenticatorLargeBlobs(None))
|
||||||
@@ -274,7 +272,7 @@ mod test {
|
|||||||
pin_uv_auth_protocol: None,
|
pin_uv_auth_protocol: None,
|
||||||
};
|
};
|
||||||
let large_blobs_response =
|
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!(
|
assert_eq!(
|
||||||
large_blobs_response,
|
large_blobs_response,
|
||||||
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_SEQ),
|
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_SEQ),
|
||||||
@@ -283,9 +281,8 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_process_command_commit_unexpected_length() {
|
fn test_process_command_commit_unexpected_length() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin =
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
@@ -306,7 +303,7 @@ mod test {
|
|||||||
pin_uv_auth_protocol: None,
|
pin_uv_auth_protocol: None,
|
||||||
};
|
};
|
||||||
let large_blobs_response =
|
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!(
|
assert_eq!(
|
||||||
large_blobs_response,
|
large_blobs_response,
|
||||||
Ok(ResponseData::AuthenticatorLargeBlobs(None))
|
Ok(ResponseData::AuthenticatorLargeBlobs(None))
|
||||||
@@ -321,7 +318,7 @@ mod test {
|
|||||||
pin_uv_auth_protocol: None,
|
pin_uv_auth_protocol: None,
|
||||||
};
|
};
|
||||||
let large_blobs_response =
|
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!(
|
assert_eq!(
|
||||||
large_blobs_response,
|
large_blobs_response,
|
||||||
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER),
|
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER),
|
||||||
@@ -330,9 +327,8 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_process_command_commit_end_offset_overflow() {
|
fn test_process_command_commit_end_offset_overflow() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin =
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
@@ -347,16 +343,15 @@ mod test {
|
|||||||
pin_uv_auth_protocol: None,
|
pin_uv_auth_protocol: None,
|
||||||
};
|
};
|
||||||
assert_eq!(
|
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),
|
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_LENGTH),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_process_command_commit_unexpected_hash() {
|
fn test_process_command_commit_unexpected_hash() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin =
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
@@ -375,7 +370,7 @@ mod test {
|
|||||||
pin_uv_auth_protocol: None,
|
pin_uv_auth_protocol: None,
|
||||||
};
|
};
|
||||||
let large_blobs_response =
|
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!(
|
assert_eq!(
|
||||||
large_blobs_response,
|
large_blobs_response,
|
||||||
Err(Ctap2StatusCode::CTAP2_ERR_INTEGRITY_FAILURE),
|
Err(Ctap2StatusCode::CTAP2_ERR_INTEGRITY_FAILURE),
|
||||||
@@ -383,9 +378,8 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn test_helper_process_command_commit_with_pin(pin_uv_auth_protocol: PinUvAuthProtocol) {
|
fn test_helper_process_command_commit_with_pin(pin_uv_auth_protocol: PinUvAuthProtocol) {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut env = TestEnv::new();
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng);
|
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin =
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol);
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol);
|
||||||
@@ -396,7 +390,7 @@ mod test {
|
|||||||
let mut large_blob = vec![0x1B; DATA_LEN];
|
let mut large_blob = vec![0x1B; DATA_LEN];
|
||||||
large_blob.extend_from_slice(&Sha256::hash(&large_blob[..])[..TRUNCATED_HASH_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];
|
let mut large_blob_data = vec![0xFF; 32];
|
||||||
// Command constant and offset bytes.
|
// Command constant and offset bytes.
|
||||||
large_blob_data.extend(&[0x0C, 0x00, 0x00, 0x00, 0x00, 0x00]);
|
large_blob_data.extend(&[0x0C, 0x00, 0x00, 0x00, 0x00, 0x00]);
|
||||||
@@ -416,7 +410,7 @@ mod test {
|
|||||||
pin_uv_auth_protocol: Some(pin_uv_auth_protocol),
|
pin_uv_auth_protocol: Some(pin_uv_auth_protocol),
|
||||||
};
|
};
|
||||||
let large_blobs_response =
|
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!(
|
assert_eq!(
|
||||||
large_blobs_response,
|
large_blobs_response,
|
||||||
Ok(ResponseData::AuthenticatorLargeBlobs(None))
|
Ok(ResponseData::AuthenticatorLargeBlobs(None))
|
||||||
|
|||||||
429
src/ctap/mod.rs
429
src/ctap/mod.rs
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,65 +0,0 @@
|
|||||||
// Copyright 2019-2021 Google LLC
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
mod buffer_upgrade;
|
|
||||||
mod helper;
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
mod syscall;
|
|
||||||
mod upgrade_storage;
|
|
||||||
|
|
||||||
pub use upgrade_storage::UpgradeStorage;
|
|
||||||
|
|
||||||
/// Definitions for production.
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
mod prod {
|
|
||||||
use super::syscall::{SyscallStorage, SyscallUpgradeStorage};
|
|
||||||
|
|
||||||
pub type Storage = SyscallStorage;
|
|
||||||
|
|
||||||
pub fn new_storage() -> persistent_store::StorageResult<Storage> {
|
|
||||||
Storage::new()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type UpgradeLocations = SyscallUpgradeStorage;
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
pub use self::prod::{new_storage, Storage, UpgradeLocations};
|
|
||||||
|
|
||||||
/// Definitions for testing.
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
mod test {
|
|
||||||
use super::buffer_upgrade::BufferUpgradeStorage;
|
|
||||||
|
|
||||||
pub type Storage = persistent_store::BufferStorage;
|
|
||||||
|
|
||||||
pub fn new_storage() -> persistent_store::StorageResult<Storage> {
|
|
||||||
// Use the Nordic configuration.
|
|
||||||
const PAGE_SIZE: usize = 0x1000;
|
|
||||||
const NUM_PAGES: usize = 20;
|
|
||||||
let store = vec![0xff; NUM_PAGES * PAGE_SIZE].into_boxed_slice();
|
|
||||||
let options = persistent_store::BufferOptions {
|
|
||||||
word_size: 4,
|
|
||||||
page_size: PAGE_SIZE,
|
|
||||||
max_word_writes: 2,
|
|
||||||
max_page_erases: 10000,
|
|
||||||
strict_mode: true,
|
|
||||||
};
|
|
||||||
Ok(Storage::new(store, options))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type UpgradeLocations = BufferUpgradeStorage;
|
|
||||||
}
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub use self::test::{new_storage, Storage, UpgradeLocations};
|
|
||||||
11
src/env/mod.rs
vendored
11
src/env/mod.rs
vendored
@@ -1,6 +1,8 @@
|
|||||||
|
use crate::api::upgrade_storage::UpgradeStorage;
|
||||||
use crate::ctap::hid::ChannelID;
|
use crate::ctap::hid::ChannelID;
|
||||||
use crate::ctap::status_code::Ctap2StatusCode;
|
use crate::ctap::status_code::Ctap2StatusCode;
|
||||||
use crypto::rng256::Rng256;
|
use crypto::rng256::Rng256;
|
||||||
|
use persistent_store::{Storage, Store};
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub mod test;
|
pub mod test;
|
||||||
@@ -17,7 +19,16 @@ pub trait UserPresence {
|
|||||||
pub trait Env {
|
pub trait Env {
|
||||||
type Rng: Rng256;
|
type Rng: Rng256;
|
||||||
type UserPresence: UserPresence;
|
type UserPresence: UserPresence;
|
||||||
|
type Storage: Storage;
|
||||||
|
type UpgradeStorage: UpgradeStorage;
|
||||||
|
|
||||||
fn rng(&mut self) -> &mut Self::Rng;
|
fn rng(&mut self) -> &mut Self::Rng;
|
||||||
fn user_presence(&mut self) -> &mut Self::UserPresence;
|
fn user_presence(&mut self) -> &mut Self::UserPresence;
|
||||||
|
fn store(&mut self) -> &mut Store<Self::Storage>;
|
||||||
|
|
||||||
|
/// Returns the upgrade storage instance.
|
||||||
|
///
|
||||||
|
/// Upgrade storage is optional, so implementations may return `None`. However, implementations
|
||||||
|
/// should either always return `None` or always return `Some`.
|
||||||
|
fn upgrade_storage(&mut self) -> Option<&mut Self::UpgradeStorage>;
|
||||||
}
|
}
|
||||||
|
|||||||
48
src/env/test.rs
vendored
48
src/env/test.rs
vendored
@@ -1,48 +0,0 @@
|
|||||||
use crate::ctap::hid::ChannelID;
|
|
||||||
use crate::ctap::status_code::Ctap2StatusCode;
|
|
||||||
use crate::env::{Env, UserPresence};
|
|
||||||
use crypto::rng256::ThreadRng256;
|
|
||||||
|
|
||||||
pub struct TestEnv {
|
|
||||||
rng: ThreadRng256,
|
|
||||||
user_presence: TestUserPresence,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TestUserPresence {
|
|
||||||
check: Box<dyn Fn(ChannelID) -> Result<(), Ctap2StatusCode>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TestEnv {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let rng = ThreadRng256 {};
|
|
||||||
let user_presence = TestUserPresence {
|
|
||||||
check: Box::new(|_| Ok(())),
|
|
||||||
};
|
|
||||||
TestEnv { rng, user_presence }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TestUserPresence {
|
|
||||||
pub fn set(&mut self, check: impl Fn(ChannelID) -> Result<(), Ctap2StatusCode> + 'static) {
|
|
||||||
self.check = Box::new(check);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UserPresence for TestUserPresence {
|
|
||||||
fn check(&self, cid: ChannelID) -> Result<(), Ctap2StatusCode> {
|
|
||||||
(self.check)(cid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Env for TestEnv {
|
|
||||||
type Rng = ThreadRng256;
|
|
||||||
type UserPresence = TestUserPresence;
|
|
||||||
|
|
||||||
fn rng(&mut self) -> &mut Self::Rng {
|
|
||||||
&mut self.rng
|
|
||||||
}
|
|
||||||
|
|
||||||
fn user_presence(&mut self) -> &mut Self::UserPresence {
|
|
||||||
&mut self.user_presence
|
|
||||||
}
|
|
||||||
}
|
|
||||||
91
src/env/test/mod.rs
vendored
Normal file
91
src/env/test/mod.rs
vendored
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
use self::upgrade_storage::BufferUpgradeStorage;
|
||||||
|
use crate::ctap::hid::ChannelID;
|
||||||
|
use crate::ctap::status_code::Ctap2StatusCode;
|
||||||
|
use crate::env::{Env, UserPresence};
|
||||||
|
use crypto::rng256::ThreadRng256;
|
||||||
|
use persistent_store::{BufferOptions, BufferStorage, Store};
|
||||||
|
|
||||||
|
mod upgrade_storage;
|
||||||
|
|
||||||
|
pub struct TestEnv {
|
||||||
|
rng: ThreadRng256,
|
||||||
|
user_presence: TestUserPresence,
|
||||||
|
store: Store<BufferStorage>,
|
||||||
|
upgrade_storage: Option<BufferUpgradeStorage>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TestUserPresence {
|
||||||
|
check: Box<dyn Fn(ChannelID) -> Result<(), Ctap2StatusCode>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_storage() -> BufferStorage {
|
||||||
|
// Use the Nordic configuration.
|
||||||
|
const PAGE_SIZE: usize = 0x1000;
|
||||||
|
const NUM_PAGES: usize = 20;
|
||||||
|
let store = vec![0xff; NUM_PAGES * PAGE_SIZE].into_boxed_slice();
|
||||||
|
let options = BufferOptions {
|
||||||
|
word_size: 4,
|
||||||
|
page_size: PAGE_SIZE,
|
||||||
|
max_word_writes: 2,
|
||||||
|
max_page_erases: 10000,
|
||||||
|
strict_mode: true,
|
||||||
|
};
|
||||||
|
BufferStorage::new(store, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestEnv {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let rng = ThreadRng256 {};
|
||||||
|
let user_presence = TestUserPresence {
|
||||||
|
check: Box::new(|_| Ok(())),
|
||||||
|
};
|
||||||
|
let storage = new_storage();
|
||||||
|
let store = Store::new(storage).ok().unwrap();
|
||||||
|
let upgrade_storage = Some(BufferUpgradeStorage::new().unwrap());
|
||||||
|
TestEnv {
|
||||||
|
rng,
|
||||||
|
user_presence,
|
||||||
|
store,
|
||||||
|
upgrade_storage,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disable_upgrade_storage(&mut self) {
|
||||||
|
self.upgrade_storage = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestUserPresence {
|
||||||
|
pub fn set(&mut self, check: impl Fn(ChannelID) -> Result<(), Ctap2StatusCode> + 'static) {
|
||||||
|
self.check = Box::new(check);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserPresence for TestUserPresence {
|
||||||
|
fn check(&self, cid: ChannelID) -> Result<(), Ctap2StatusCode> {
|
||||||
|
(self.check)(cid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Env for TestEnv {
|
||||||
|
type Rng = ThreadRng256;
|
||||||
|
type UserPresence = TestUserPresence;
|
||||||
|
type Storage = BufferStorage;
|
||||||
|
type UpgradeStorage = BufferUpgradeStorage;
|
||||||
|
|
||||||
|
fn rng(&mut self) -> &mut Self::Rng {
|
||||||
|
&mut self.rng
|
||||||
|
}
|
||||||
|
|
||||||
|
fn user_presence(&mut self) -> &mut Self::UserPresence {
|
||||||
|
&mut self.user_presence
|
||||||
|
}
|
||||||
|
|
||||||
|
fn store(&mut self) -> &mut Store<Self::Storage> {
|
||||||
|
&mut self.store
|
||||||
|
}
|
||||||
|
|
||||||
|
fn upgrade_storage(&mut self) -> Option<&mut Self::UpgradeStorage> {
|
||||||
|
self.upgrade_storage.as_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,8 +12,8 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use super::helper::ModRange;
|
use crate::api::upgrade_storage::helper::ModRange;
|
||||||
use super::upgrade_storage::UpgradeStorage;
|
use crate::api::upgrade_storage::UpgradeStorage;
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use persistent_store::{StorageError, StorageResult};
|
use persistent_store::{StorageError, StorageResult};
|
||||||
|
|
||||||
48
src/env/tock.rs → src/env/tock/mod.rs
vendored
48
src/env/tock.rs → src/env/tock/mod.rs
vendored
@@ -1,9 +1,11 @@
|
|||||||
|
use self::storage::{SyscallStorage, SyscallUpgradeStorage};
|
||||||
use crate::ctap::hid::{ChannelID, CtapHid, CtapHidCommand, KeepaliveStatus, ProcessedPacket};
|
use crate::ctap::hid::{ChannelID, CtapHid, CtapHidCommand, KeepaliveStatus, ProcessedPacket};
|
||||||
use crate::ctap::status_code::Ctap2StatusCode;
|
use crate::ctap::status_code::Ctap2StatusCode;
|
||||||
use crate::env::{Env, UserPresence};
|
use crate::env::{Env, UserPresence};
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
#[cfg(feature = "debug_ctap")]
|
#[cfg(feature = "debug_ctap")]
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
use crypto::rng256::TockRng256;
|
use crypto::rng256::TockRng256;
|
||||||
use libtock_core::result::{CommandError, EALREADY};
|
use libtock_core::result::{CommandError, EALREADY};
|
||||||
use libtock_drivers::buttons::{self, ButtonState};
|
use libtock_drivers::buttons::{self, ButtonState};
|
||||||
@@ -12,17 +14,49 @@ use libtock_drivers::console::Console;
|
|||||||
use libtock_drivers::result::{FlexUnwrap, TockError};
|
use libtock_drivers::result::{FlexUnwrap, TockError};
|
||||||
use libtock_drivers::timer::Duration;
|
use libtock_drivers::timer::Duration;
|
||||||
use libtock_drivers::{led, timer, usb_ctap_hid};
|
use libtock_drivers::{led, timer, usb_ctap_hid};
|
||||||
|
use persistent_store::{StorageResult, Store};
|
||||||
|
|
||||||
|
mod storage;
|
||||||
|
|
||||||
pub struct TockEnv {
|
pub struct TockEnv {
|
||||||
rng: TockRng256,
|
rng: TockRng256,
|
||||||
|
store: Store<SyscallStorage>,
|
||||||
|
upgrade_storage: Option<SyscallUpgradeStorage>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TockEnv {
|
impl TockEnv {
|
||||||
|
/// Returns the unique instance of the Tock environment.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// - If called a second time.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let rng = TockRng256 {};
|
// Make sure the environment was not already taken.
|
||||||
TockEnv { rng }
|
static TAKEN: AtomicBool = AtomicBool::new(false);
|
||||||
|
assert!(!TAKEN.fetch_or(true, Ordering::SeqCst));
|
||||||
|
let storage = unsafe { steal_storage() }.unwrap();
|
||||||
|
let store = Store::new(storage).ok().unwrap();
|
||||||
|
let upgrade_storage = SyscallUpgradeStorage::new().ok();
|
||||||
|
TockEnv {
|
||||||
|
rng: TockRng256 {},
|
||||||
|
store,
|
||||||
|
upgrade_storage,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new storage instance.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// It is probably technically memory-safe to have multiple storage instances at the same time, but
|
||||||
|
/// for extra precaution we mark the function as unsafe. To ensure correct usage, this function
|
||||||
|
/// should only be called if the previous storage instance was dropped.
|
||||||
|
// This function is exposed to example binaries testing the hardware. This could probably be cleaned
|
||||||
|
// up by having the persistent store return its storage.
|
||||||
|
pub unsafe fn steal_storage() -> StorageResult<SyscallStorage> {
|
||||||
|
SyscallStorage::new()
|
||||||
|
}
|
||||||
|
|
||||||
impl UserPresence for TockEnv {
|
impl UserPresence for TockEnv {
|
||||||
fn check(&self, cid: ChannelID) -> Result<(), Ctap2StatusCode> {
|
fn check(&self, cid: ChannelID) -> Result<(), Ctap2StatusCode> {
|
||||||
@@ -33,6 +67,8 @@ impl UserPresence for TockEnv {
|
|||||||
impl Env for TockEnv {
|
impl Env for TockEnv {
|
||||||
type Rng = TockRng256;
|
type Rng = TockRng256;
|
||||||
type UserPresence = Self;
|
type UserPresence = Self;
|
||||||
|
type Storage = SyscallStorage;
|
||||||
|
type UpgradeStorage = SyscallUpgradeStorage;
|
||||||
|
|
||||||
fn rng(&mut self) -> &mut Self::Rng {
|
fn rng(&mut self) -> &mut Self::Rng {
|
||||||
&mut self.rng
|
&mut self.rng
|
||||||
@@ -41,6 +77,14 @@ impl Env for TockEnv {
|
|||||||
fn user_presence(&mut self) -> &mut Self::UserPresence {
|
fn user_presence(&mut self) -> &mut Self::UserPresence {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn store(&mut self) -> &mut Store<Self::Storage> {
|
||||||
|
&mut self.store
|
||||||
|
}
|
||||||
|
|
||||||
|
fn upgrade_storage(&mut self) -> Option<&mut Self::UpgradeStorage> {
|
||||||
|
self.upgrade_storage.as_mut()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns whether the keepalive was sent, or false if cancelled.
|
// Returns whether the keepalive was sent, or false if cancelled.
|
||||||
@@ -12,8 +12,8 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use super::helper::{find_slice, is_aligned, ModRange};
|
use crate::api::upgrade_storage::helper::{find_slice, is_aligned, ModRange};
|
||||||
use super::upgrade_storage::UpgradeStorage;
|
use crate::api::upgrade_storage::UpgradeStorage;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
use libtock_core::{callback, syscalls};
|
use libtock_core::{callback, syscalls};
|
||||||
@@ -24,13 +24,12 @@ use crate::ctap::CtapState;
|
|||||||
use crate::env::Env;
|
use crate::env::Env;
|
||||||
use libtock_drivers::timer::ClockValue;
|
use libtock_drivers::timer::ClockValue;
|
||||||
|
|
||||||
|
pub mod api;
|
||||||
// Implementation details must be public for testing (in particular fuzzing).
|
// Implementation details must be public for testing (in particular fuzzing).
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub mod ctap;
|
pub mod ctap;
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
mod ctap;
|
mod ctap;
|
||||||
// Store example binaries use the flash directly. Eventually, they should access it from env::tock.
|
|
||||||
pub mod embedded_flash;
|
|
||||||
pub mod env;
|
pub mod env;
|
||||||
|
|
||||||
/// CTAP implementation parameterized by its environment.
|
/// CTAP implementation parameterized by its environment.
|
||||||
|
|||||||
Reference in New Issue
Block a user