Replaced Libtock driver clock with embedded_time::Clock (#422)

* Replaced Libtock driver clock with embedded_time::Clock

* Add unittest and address some comments

* Add unittest and address some comments
This commit is contained in:
Shiling Wang
2022-03-10 16:18:47 +01:00
committed by GitHub
parent d81af2857e
commit 2050f9f272
15 changed files with 587 additions and 413 deletions

View File

@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use super::super::clock::CtapInstant;
use super::command::AuthenticatorClientPinParameters;
use super::data_formats::{
ok_or_missing, ClientPinSubCommand, CoseKey, GetAssertionHmacSecretInput, PinUvAuthProtocol,
@@ -32,7 +33,6 @@ use crypto::sha256::Sha256;
use crypto::Hash256;
#[cfg(test)]
use enum_iterator::IntoEnumIterator;
use libtock_drivers::timer::ClockValue;
use subtle::ConstantTimeEq;
/// The prefix length of the PIN hash that is stored and compared.
@@ -291,7 +291,7 @@ impl ClientPin {
&mut self,
env: &mut impl Env,
client_pin_params: AuthenticatorClientPinParameters,
now: ClockValue,
now: CtapInstant,
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
let AuthenticatorClientPinParameters {
pin_uv_auth_protocol,
@@ -359,7 +359,7 @@ impl ClientPin {
&mut self,
env: &mut impl Env,
mut client_pin_params: AuthenticatorClientPinParameters,
now: ClockValue,
now: CtapInstant,
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
// Mutating client_pin_params is just an optimization to move it into
// process_get_pin_token, without cloning permissions_rp_id here.
@@ -388,7 +388,7 @@ impl ClientPin {
&mut self,
env: &mut impl Env,
client_pin_params: AuthenticatorClientPinParameters,
now: ClockValue,
now: CtapInstant,
) -> Result<ResponseData, Ctap2StatusCode> {
let response = match client_pin_params.sub_command {
ClientPinSubCommand::GetPinRetries => Some(self.process_get_pin_retries(env)?),
@@ -495,7 +495,7 @@ impl ClientPin {
}
/// Updates the running timers, triggers timeout events.
pub fn update_timeouts(&mut self, now: ClockValue) {
pub fn update_timeouts(&mut self, now: CtapInstant) {
self.pin_uv_auth_token_state
.pin_uv_auth_token_usage_timer_observer(now);
}
@@ -568,9 +568,7 @@ impl ClientPin {
};
let mut pin_uv_auth_token_state = PinUvAuthTokenState::new();
pin_uv_auth_token_state.set_permissions(0xFF);
const CLOCK_FREQUENCY_HZ: usize = 32768;
const DUMMY_CLOCK_VALUE: ClockValue = ClockValue::new(0, CLOCK_FREQUENCY_HZ);
pin_uv_auth_token_state.begin_using_pin_uv_auth_token(DUMMY_CLOCK_VALUE);
pin_uv_auth_token_state.begin_using_pin_uv_auth_token(CtapInstant::new(0));
ClientPin {
pin_protocol_v1: PinProtocol::new_test(key_agreement_key_v1, pin_uv_auth_token),
pin_protocol_v2: PinProtocol::new_test(key_agreement_key_v2, pin_uv_auth_token),
@@ -586,10 +584,7 @@ mod test {
use super::*;
use crate::env::test::TestEnv;
use alloc::vec;
use libtock_drivers::timer::Duration;
const CLOCK_FREQUENCY_HZ: usize = 32768;
const DUMMY_CLOCK_VALUE: ClockValue = ClockValue::new(0, CLOCK_FREQUENCY_HZ);
use embedded_time::duration::Milliseconds;
/// Stores a PIN hash corresponding to the dummy PIN "1234".
fn set_standard_pin(env: &mut TestEnv) {
@@ -822,7 +817,7 @@ mod test {
power_cycle_state: Some(false),
});
assert_eq!(
client_pin.process_command(&mut env, params.clone(), DUMMY_CLOCK_VALUE),
client_pin.process_command(&mut env, params.clone(), CtapInstant::new(0)),
Ok(ResponseData::AuthenticatorClientPin(expected_response))
);
@@ -834,7 +829,7 @@ mod test {
power_cycle_state: Some(true),
});
assert_eq!(
client_pin.process_command(&mut env, params, DUMMY_CLOCK_VALUE),
client_pin.process_command(&mut env, params, CtapInstant::new(0)),
Ok(ResponseData::AuthenticatorClientPin(expected_response))
);
}
@@ -862,7 +857,7 @@ mod test {
power_cycle_state: None,
});
assert_eq!(
client_pin.process_command(&mut env, params, DUMMY_CLOCK_VALUE),
client_pin.process_command(&mut env, params, CtapInstant::new(0)),
Ok(ResponseData::AuthenticatorClientPin(expected_response))
);
}
@@ -882,7 +877,7 @@ mod test {
create_client_pin_and_parameters(pin_uv_auth_protocol, ClientPinSubCommand::SetPin);
let mut env = TestEnv::new();
assert_eq!(
client_pin.process_command(&mut env, params, DUMMY_CLOCK_VALUE),
client_pin.process_command(&mut env, params, CtapInstant::new(0)),
Ok(ResponseData::AuthenticatorClientPin(None))
);
}
@@ -915,14 +910,14 @@ mod test {
let pin_uv_auth_param = shared_secret.authenticate(&auth_param_data);
params.pin_uv_auth_param = Some(pin_uv_auth_param);
assert_eq!(
client_pin.process_command(&mut env, params.clone(), DUMMY_CLOCK_VALUE),
client_pin.process_command(&mut env, params.clone(), CtapInstant::new(0)),
Ok(ResponseData::AuthenticatorClientPin(None))
);
let mut bad_params = params.clone();
bad_params.pin_hash_enc = Some(vec![0xEE; 16]);
assert_eq!(
client_pin.process_command(&mut env, bad_params, DUMMY_CLOCK_VALUE),
client_pin.process_command(&mut env, bad_params, CtapInstant::new(0)),
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
);
@@ -930,7 +925,7 @@ mod test {
storage::decr_pin_retries(&mut env).unwrap();
}
assert_eq!(
client_pin.process_command(&mut env, params, DUMMY_CLOCK_VALUE),
client_pin.process_command(&mut env, params, CtapInstant::new(0)),
Err(Ctap2StatusCode::CTAP2_ERR_PIN_BLOCKED)
);
}
@@ -961,7 +956,7 @@ mod test {
set_standard_pin(&mut env);
let response = client_pin
.process_command(&mut env, params.clone(), DUMMY_CLOCK_VALUE)
.process_command(&mut env, params.clone(), CtapInstant::new(0))
.unwrap();
let encrypted_token = match response {
ResponseData::AuthenticatorClientPin(Some(response)) => {
@@ -997,7 +992,7 @@ mod test {
let mut bad_params = params;
bad_params.pin_hash_enc = Some(vec![0xEE; 16]);
assert_eq!(
client_pin.process_command(&mut env, bad_params, DUMMY_CLOCK_VALUE),
client_pin.process_command(&mut env, bad_params, CtapInstant::new(0)),
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)
);
}
@@ -1022,7 +1017,7 @@ mod test {
assert_eq!(storage::force_pin_change(&mut env), Ok(()));
assert_eq!(
client_pin.process_command(&mut env, params, DUMMY_CLOCK_VALUE),
client_pin.process_command(&mut env, params, CtapInstant::new(0)),
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID),
);
}
@@ -1055,7 +1050,7 @@ mod test {
set_standard_pin(&mut env);
let response = client_pin
.process_command(&mut env, params.clone(), DUMMY_CLOCK_VALUE)
.process_command(&mut env, params.clone(), CtapInstant::new(0))
.unwrap();
let encrypted_token = match response {
ResponseData::AuthenticatorClientPin(Some(response)) => {
@@ -1091,21 +1086,21 @@ mod test {
let mut bad_params = params.clone();
bad_params.permissions = Some(0x00);
assert_eq!(
client_pin.process_command(&mut env, bad_params, DUMMY_CLOCK_VALUE),
client_pin.process_command(&mut env, bad_params, CtapInstant::new(0)),
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
);
let mut bad_params = params.clone();
bad_params.permissions_rp_id = None;
assert_eq!(
client_pin.process_command(&mut env, bad_params, DUMMY_CLOCK_VALUE),
client_pin.process_command(&mut env, bad_params, CtapInstant::new(0)),
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
);
let mut bad_params = params;
bad_params.pin_hash_enc = Some(vec![0xEE; 16]);
assert_eq!(
client_pin.process_command(&mut env, bad_params, DUMMY_CLOCK_VALUE),
client_pin.process_command(&mut env, bad_params, CtapInstant::new(0)),
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)
);
}
@@ -1132,7 +1127,7 @@ mod test {
assert_eq!(storage::force_pin_change(&mut env), Ok(()));
assert_eq!(
client_pin.process_command(&mut env, params, DUMMY_CLOCK_VALUE),
client_pin.process_command(&mut env, params, CtapInstant::new(0)),
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)
);
}
@@ -1485,7 +1480,7 @@ mod test {
let message = [0xAA];
client_pin
.pin_uv_auth_token_state
.begin_using_pin_uv_auth_token(DUMMY_CLOCK_VALUE);
.begin_using_pin_uv_auth_token(CtapInstant::new(0));
let pin_uv_auth_token_v1 = client_pin
.get_pin_protocol(PinUvAuthProtocol::V1)
@@ -1617,7 +1612,7 @@ mod test {
params.permissions = Some(0xFF);
assert!(client_pin
.process_command(&mut env, params, DUMMY_CLOCK_VALUE)
.process_command(&mut env, params, CtapInstant::new(0))
.is_ok());
for permission in PinPermission::into_enum_iter() {
assert_eq!(
@@ -1634,7 +1629,7 @@ mod test {
Ok(())
);
let timeout = DUMMY_CLOCK_VALUE.wrapping_add(Duration::from_ms(30001));
let timeout = CtapInstant::new(0) + Milliseconds::new(30001_u32);
client_pin.update_timeouts(timeout);
for permission in PinPermission::into_enum_iter() {
assert_eq!(
@@ -1663,7 +1658,7 @@ mod test {
params.permissions = Some(0xFF);
assert!(client_pin
.process_command(&mut env, params, DUMMY_CLOCK_VALUE)
.process_command(&mut env, params, CtapInstant::new(0))
.is_ok());
for permission in PinPermission::into_enum_iter() {
assert_eq!(

View File

@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use super::super::clock::CtapInstant;
use super::client_pin::{ClientPin, PinPermission};
use super::command::AuthenticatorCredentialManagementParameters;
use super::data_formats::{
@@ -30,7 +31,6 @@ use alloc::vec;
use alloc::vec::Vec;
use crypto::sha256::Sha256;
use crypto::Hash256;
use libtock_drivers::timer::ClockValue;
/// Generates a set with all existing RP IDs.
fn get_stored_rp_ids(env: &mut impl Env) -> Result<BTreeSet<String>, Ctap2StatusCode> {
@@ -137,7 +137,7 @@ fn process_get_creds_metadata(
fn process_enumerate_rps_begin(
env: &mut impl Env,
stateful_command_permission: &mut StatefulPermission,
now: ClockValue,
now: CtapInstant,
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
let rp_set = get_stored_rp_ids(env)?;
let total_rps = rp_set.len();
@@ -174,7 +174,7 @@ fn process_enumerate_credentials_begin(
stateful_command_permission: &mut StatefulPermission,
client_pin: &mut ClientPin,
sub_command_params: CredentialManagementSubCommandParameters,
now: ClockValue,
now: CtapInstant,
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
let rp_id_hash = sub_command_params
.rp_id_hash
@@ -252,7 +252,7 @@ pub fn process_credential_management(
stateful_command_permission: &mut StatefulPermission,
client_pin: &mut ClientPin,
cred_management_params: AuthenticatorCredentialManagementParameters,
now: ClockValue,
now: CtapInstant,
) -> Result<ResponseData, Ctap2StatusCode> {
let AuthenticatorCredentialManagementParameters {
sub_command,
@@ -359,9 +359,6 @@ mod test {
use crate::env::Env;
use crypto::rng256::Rng256;
const CLOCK_FREQUENCY_HZ: usize = 32768;
const DUMMY_CLOCK_VALUE: ClockValue = ClockValue::new(0, CLOCK_FREQUENCY_HZ);
fn create_credential_source(rng: &mut impl Rng256) -> PublicKeyCredentialSource {
let private_key = crypto::ecdsa::SecKey::gensk(rng);
PublicKeyCredentialSource {
@@ -388,7 +385,7 @@ mod test {
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol);
let credential_source = create_credential_source(env.rng());
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
ctap_state.client_pin = client_pin;
storage::set_pin(&mut env, &[0u8; 16], 4).unwrap();
@@ -410,7 +407,7 @@ mod test {
&mut ctap_state.stateful_command_permission,
&mut ctap_state.client_pin,
cred_management_params,
DUMMY_CLOCK_VALUE,
CtapInstant::new(0),
);
let initial_capacity = match cred_management_response.unwrap() {
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
@@ -435,7 +432,7 @@ mod test {
&mut ctap_state.stateful_command_permission,
&mut ctap_state.client_pin,
cred_management_params,
DUMMY_CLOCK_VALUE,
CtapInstant::new(0),
);
match cred_management_response.unwrap() {
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
@@ -470,7 +467,7 @@ mod test {
let mut credential_source2 = create_credential_source(env.rng());
credential_source2.rp_id = "another.example.com".to_string();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
ctap_state.client_pin = client_pin;
storage::store_credential(&mut env, credential_source1).unwrap();
@@ -493,7 +490,7 @@ mod test {
&mut ctap_state.stateful_command_permission,
&mut ctap_state.client_pin,
cred_management_params,
DUMMY_CLOCK_VALUE,
CtapInstant::new(0),
);
let first_rp_id = match cred_management_response.unwrap() {
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
@@ -517,7 +514,7 @@ mod test {
&mut ctap_state.stateful_command_permission,
&mut ctap_state.client_pin,
cred_management_params,
DUMMY_CLOCK_VALUE,
CtapInstant::new(0),
);
let second_rp_id = match cred_management_response.unwrap() {
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
@@ -542,7 +539,7 @@ mod test {
&mut ctap_state.stateful_command_permission,
&mut ctap_state.client_pin,
cred_management_params,
DUMMY_CLOCK_VALUE,
CtapInstant::new(0),
);
assert_eq!(
cred_management_response,
@@ -559,7 +556,7 @@ mod test {
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
let credential_source = create_credential_source(env.rng());
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
ctap_state.client_pin = client_pin;
const NUM_CREDENTIALS: usize = 20;
@@ -591,7 +588,7 @@ mod test {
&mut ctap_state.stateful_command_permission,
&mut ctap_state.client_pin,
cred_management_params,
DUMMY_CLOCK_VALUE,
CtapInstant::new(0),
);
match cred_management_response.unwrap() {
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
@@ -621,7 +618,7 @@ mod test {
&mut ctap_state.stateful_command_permission,
&mut ctap_state.client_pin,
cred_management_params,
DUMMY_CLOCK_VALUE,
CtapInstant::new(0),
);
assert_eq!(
cred_management_response,
@@ -643,7 +640,7 @@ mod test {
credential_source2.user_display_name = Some("User Two".to_string());
credential_source2.user_icon = Some("icon2".to_string());
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
ctap_state.client_pin = client_pin;
storage::store_credential(&mut env, credential_source1).unwrap();
@@ -673,7 +670,7 @@ mod test {
&mut ctap_state.stateful_command_permission,
&mut ctap_state.client_pin,
cred_management_params,
DUMMY_CLOCK_VALUE,
CtapInstant::new(0),
);
let first_credential_id = match cred_management_response.unwrap() {
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
@@ -696,7 +693,7 @@ mod test {
&mut ctap_state.stateful_command_permission,
&mut ctap_state.client_pin,
cred_management_params,
DUMMY_CLOCK_VALUE,
CtapInstant::new(0),
);
let second_credential_id = match cred_management_response.unwrap() {
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
@@ -720,7 +717,7 @@ mod test {
&mut ctap_state.stateful_command_permission,
&mut ctap_state.client_pin,
cred_management_params,
DUMMY_CLOCK_VALUE,
CtapInstant::new(0),
);
assert_eq!(
cred_management_response,
@@ -738,7 +735,7 @@ mod test {
let mut credential_source = create_credential_source(env.rng());
credential_source.credential_id = vec![0x1D; 32];
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
ctap_state.client_pin = client_pin;
storage::store_credential(&mut env, credential_source).unwrap();
@@ -770,7 +767,7 @@ mod test {
&mut ctap_state.stateful_command_permission,
&mut ctap_state.client_pin,
cred_management_params,
DUMMY_CLOCK_VALUE,
CtapInstant::new(0),
);
assert_eq!(
cred_management_response,
@@ -788,7 +785,7 @@ mod test {
&mut ctap_state.stateful_command_permission,
&mut ctap_state.client_pin,
cred_management_params,
DUMMY_CLOCK_VALUE,
CtapInstant::new(0),
);
assert_eq!(
cred_management_response,
@@ -806,7 +803,7 @@ mod test {
let mut credential_source = create_credential_source(env.rng());
credential_source.credential_id = vec![0x1D; 32];
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
ctap_state.client_pin = client_pin;
storage::store_credential(&mut env, credential_source).unwrap();
@@ -844,7 +841,7 @@ mod test {
&mut ctap_state.stateful_command_permission,
&mut ctap_state.client_pin,
cred_management_params,
DUMMY_CLOCK_VALUE,
CtapInstant::new(0),
);
assert_eq!(
cred_management_response,
@@ -867,7 +864,7 @@ mod test {
#[test]
fn test_process_credential_management_invalid_pin_uv_auth_param() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
storage::set_pin(&mut env, &[0u8; 16], 4).unwrap();
@@ -882,7 +879,7 @@ mod test {
&mut ctap_state.stateful_command_permission,
&mut ctap_state.client_pin,
cred_management_params,
DUMMY_CLOCK_VALUE,
CtapInstant::new(0),
);
assert_eq!(
cred_management_response,

View File

@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use super::super::clock::CtapInstant;
use super::apdu::{Apdu, ApduStatusCode};
use super::CtapState;
use crate::ctap::storage;
@@ -20,7 +21,6 @@ use alloc::vec::Vec;
use arrayref::array_ref;
use core::convert::Into;
use core::convert::TryFrom;
use libtock_drivers::timer::ClockValue;
// For now, they're the same thing with apdu.rs containing the authoritative definition
pub type Ctap1StatusCode = ApduStatusCode;
@@ -183,7 +183,7 @@ impl Ctap1Command {
env: &mut impl Env,
message: &[u8],
ctap_state: &mut CtapState,
clock_value: ClockValue,
clock_value: CtapInstant,
) -> Result<Vec<u8>, Ctap1StatusCode> {
if !ctap_state
.allows_ctap1(env)
@@ -347,16 +347,10 @@ impl Ctap1Command {
mod test {
use super::super::{key_material, CREDENTIAL_ID_SIZE, USE_SIGNATURE_COUNTER};
use super::*;
use crate::clock::TEST_CLOCK_FREQUENCY_HZ;
use crate::env::test::TestEnv;
use crypto::Hash256;
const CLOCK_FREQUENCY_HZ: usize = 32768;
const START_CLOCK_VALUE: ClockValue = ClockValue::new(0, CLOCK_FREQUENCY_HZ);
const TIMEOUT_CLOCK_VALUE: ClockValue = ClockValue::new(
(30001 * CLOCK_FREQUENCY_HZ as isize) / 1000,
CLOCK_FREQUENCY_HZ,
);
fn create_register_message(application: &[u8; 32]) -> Vec<u8> {
let mut message = vec![
Ctap1Command::CTAP1_CLA,
@@ -400,15 +394,15 @@ mod test {
let mut env = TestEnv::new();
env.user_presence()
.set(|_| panic!("Unexpected user presence check in CTAP1"));
let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
storage::toggle_always_uv(&mut env).unwrap();
let application = [0x0A; 32];
let message = create_register_message(&application);
ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE);
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
ctap_state.u2f_up_state.consume_up(CtapInstant::new(0));
ctap_state.u2f_up_state.grant_up(CtapInstant::new(0));
let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE);
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
assert_eq!(response, Err(Ctap1StatusCode::SW_COMMAND_NOT_ALLOWED));
}
@@ -417,32 +411,36 @@ mod test {
let mut env = TestEnv::new();
env.user_presence()
.set(|_| panic!("Unexpected user presence check in CTAP1"));
let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let application = [0x0A; 32];
let message = create_register_message(&application);
ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE);
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
ctap_state.u2f_up_state.consume_up(CtapInstant::new(0));
ctap_state.u2f_up_state.grant_up(CtapInstant::new(0));
let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE);
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
// Certificate and private key are missing
assert_eq!(response, Err(Ctap1StatusCode::SW_INTERNAL_EXCEPTION));
let fake_key = [0x41u8; key_material::ATTESTATION_PRIVATE_KEY_LENGTH];
assert!(storage::set_attestation_private_key(&mut env, &fake_key).is_ok());
ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE);
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE);
ctap_state.u2f_up_state.consume_up(CtapInstant::new(0));
ctap_state.u2f_up_state.grant_up(CtapInstant::new(0));
let response = Ctap1Command::process_command(
&mut env,
&message,
&mut ctap_state,
CtapInstant::new(0_u64),
);
// Certificate is still missing
assert_eq!(response, Err(Ctap1StatusCode::SW_INTERNAL_EXCEPTION));
let fake_cert = [0x99u8; 100]; // Arbitrary length
assert!(storage::set_attestation_certificate(&mut env, &fake_cert[..]).is_ok());
ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE);
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
ctap_state.u2f_up_state.consume_up(CtapInstant::new(0));
ctap_state.u2f_up_state.grant_up(CtapInstant::new(0));
let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE)
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0))
.unwrap();
assert_eq!(response[0], Ctap1Command::LEGACY_BYTE);
assert_eq!(response[66], CREDENTIAL_ID_SIZE as u8);
@@ -466,7 +464,7 @@ mod test {
let mut env = TestEnv::new();
env.user_presence()
.set(|_| panic!("Unexpected user presence check in CTAP1"));
let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let application = [0x0A; 32];
let message = create_register_message(&application);
@@ -474,7 +472,7 @@ mod test {
&mut env,
&message[..message.len() - 1],
&mut ctap_state,
START_CLOCK_VALUE,
CtapInstant::new(0),
);
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_LENGTH));
@@ -488,12 +486,14 @@ mod test {
let mut env = TestEnv::new();
env.user_presence()
.set(|_| panic!("Unexpected user presence check in CTAP1"));
let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
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.consume_up(CtapInstant::new(0));
ctap_state.u2f_up_state.grant_up(CtapInstant::new(0));
let timeout_clock_value: CtapInstant =
CtapInstant::new((30001 * TEST_CLOCK_FREQUENCY_HZ as u64) / 1000);
let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, TIMEOUT_CLOCK_VALUE);
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, timeout_clock_value);
assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED));
}
@@ -503,7 +503,7 @@ mod test {
env.user_presence()
.set(|_| panic!("Unexpected user presence check in CTAP1"));
let sk = crypto::ecdsa::SecKey::gensk(env.rng());
let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let rp_id = "example.com";
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
@@ -513,7 +513,7 @@ mod test {
let message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE);
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED));
}
@@ -523,7 +523,7 @@ mod test {
env.user_presence()
.set(|_| panic!("Unexpected user presence check in CTAP1"));
let sk = crypto::ecdsa::SecKey::gensk(env.rng());
let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let rp_id = "example.com";
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
@@ -534,7 +534,7 @@ mod test {
let message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE);
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_DATA));
}
@@ -544,7 +544,7 @@ mod test {
env.user_presence()
.set(|_| panic!("Unexpected user presence check in CTAP1"));
let sk = crypto::ecdsa::SecKey::gensk(env.rng());
let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let rp_id = "example.com";
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
@@ -559,22 +559,22 @@ mod test {
message.push(0x00);
let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE);
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
assert!(response.is_ok());
message.push(0x00);
let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE);
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
assert!(response.is_ok());
message.push(0x00);
let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE);
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
assert!(response.is_ok());
message.push(0x00);
let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE);
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_LENGTH));
}
@@ -584,7 +584,7 @@ mod test {
env.user_presence()
.set(|_| panic!("Unexpected user presence check in CTAP1"));
let sk = crypto::ecdsa::SecKey::gensk(env.rng());
let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let rp_id = "example.com";
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
@@ -596,7 +596,7 @@ mod test {
message[0] = 0xEE;
let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE);
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
assert_eq!(response, Err(Ctap1StatusCode::SW_CLA_INVALID));
}
@@ -606,7 +606,7 @@ mod test {
env.user_presence()
.set(|_| panic!("Unexpected user presence check in CTAP1"));
let sk = crypto::ecdsa::SecKey::gensk(env.rng());
let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let rp_id = "example.com";
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
@@ -618,7 +618,7 @@ mod test {
message[1] = 0xEE;
let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE);
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
assert_eq!(response, Err(Ctap1StatusCode::SW_INS_INVALID));
}
@@ -628,7 +628,7 @@ mod test {
env.user_presence()
.set(|_| panic!("Unexpected user presence check in CTAP1"));
let sk = crypto::ecdsa::SecKey::gensk(env.rng());
let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let rp_id = "example.com";
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
@@ -640,7 +640,7 @@ mod test {
message[2] = 0xEE;
let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE);
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_DATA));
}
@@ -658,7 +658,7 @@ mod test {
env.user_presence()
.set(|_| panic!("Unexpected user presence check in CTAP1"));
let sk = crypto::ecdsa::SecKey::gensk(env.rng());
let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let rp_id = "example.com";
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
@@ -668,10 +668,10 @@ mod test {
let message =
create_authenticate_message(&application, Ctap1Flags::EnforceUpAndSign, &key_handle);
ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE);
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
ctap_state.u2f_up_state.consume_up(CtapInstant::new(0));
ctap_state.u2f_up_state.grant_up(CtapInstant::new(0));
let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE)
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0))
.unwrap();
assert_eq!(response[0], 0x01);
check_signature_counter(
@@ -686,7 +686,7 @@ mod test {
env.user_presence()
.set(|_| panic!("Unexpected user presence check in CTAP1"));
let sk = crypto::ecdsa::SecKey::gensk(env.rng());
let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let rp_id = "example.com";
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
@@ -699,9 +699,13 @@ mod test {
&key_handle,
);
let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, TIMEOUT_CLOCK_VALUE)
.unwrap();
let response = Ctap1Command::process_command(
&mut env,
&message,
&mut ctap_state,
CtapInstant::new((30001 * TEST_CLOCK_FREQUENCY_HZ as u64) / 1000),
)
.unwrap();
assert_eq!(response[0], 0x01);
check_signature_counter(
array_ref!(response, 1, 4),
@@ -719,12 +723,12 @@ mod test {
let mut env = TestEnv::new();
env.user_presence()
.set(|_| panic!("Unexpected user presence check in CTAP1"));
let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
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.consume_up(CtapInstant::new(0));
ctap_state.u2f_up_state.grant_up(CtapInstant::new(0));
let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE);
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_DATA));
}
@@ -738,12 +742,16 @@ mod test {
let mut env = TestEnv::new();
env.user_presence()
.set(|_| panic!("Unexpected user presence check in CTAP1"));
let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE);
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, TIMEOUT_CLOCK_VALUE);
ctap_state.u2f_up_state.consume_up(CtapInstant::new(0));
ctap_state.u2f_up_state.grant_up(CtapInstant::new(0));
let response = Ctap1Command::process_command(
&mut env,
&message,
&mut ctap_state,
CtapInstant::new((30001 * TEST_CLOCK_FREQUENCY_HZ as u64) / 1000),
);
assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED));
}
}

View File

@@ -17,6 +17,7 @@ pub mod send;
use self::receive::MessageAssembler;
use self::send::HidPacketIterator;
use super::super::clock::{ClockInt, CtapInstant};
#[cfg(feature = "with_ctap1")]
use super::ctap1;
use super::status_code::Ctap2StatusCode;
@@ -26,7 +27,7 @@ use crate::env::Env;
use alloc::vec;
use alloc::vec::Vec;
use arrayref::{array_ref, array_refs};
use libtock_drivers::timer::{ClockValue, Duration, Timestamp};
use embedded_time::duration::Milliseconds;
pub type HidPacket = [u8; 64];
pub type ChannelID = [u8; 4];
@@ -166,18 +167,16 @@ impl CtapHid {
const CAPABILITY_CBOR: u8 = 0x04;
#[cfg(not(feature = "with_ctap1"))]
const CAPABILITY_NMSG: u8 = 0x08;
// Capabilitites currently supported by this device.
#[cfg(feature = "with_ctap1")]
const CAPABILITIES: u8 = CtapHid::CAPABILITY_WINK | CtapHid::CAPABILITY_CBOR;
const CAPABILITIES: u8 = Self::CAPABILITY_WINK | Self::CAPABILITY_CBOR;
#[cfg(not(feature = "with_ctap1"))]
const CAPABILITIES: u8 =
CtapHid::CAPABILITY_WINK | CtapHid::CAPABILITY_CBOR | CtapHid::CAPABILITY_NMSG;
const CAPABILITIES: u8 = Self::CAPABILITY_WINK | Self::CAPABILITY_CBOR | Self::CAPABILITY_NMSG;
// TODO: Is this timeout duration specified?
const TIMEOUT_DURATION: Duration<isize> = Duration::from_ms(100);
const WINK_TIMEOUT_DURATION: Duration<isize> = Duration::from_ms(5000);
const TIMEOUT_DURATION: Milliseconds<ClockInt> = Milliseconds(100 as ClockInt);
const WINK_TIMEOUT_DURATION: Milliseconds<ClockInt> = Milliseconds(5000 as ClockInt);
/// Creates a new idle HID state.
pub fn new() -> CtapHid {
CtapHid {
assembler: MessageAssembler::new(),
@@ -209,12 +208,9 @@ impl CtapHid {
&mut self,
env: &mut impl Env,
packet: &HidPacket,
clock_value: ClockValue,
clock_value: CtapInstant,
) -> Option<Message> {
match self
.assembler
.parse_packet(packet, Timestamp::<isize>::from_clock_value(clock_value))
{
match self.assembler.parse_packet(packet, clock_value) {
Ok(Some(message)) => {
debug_ctap!(env, "Received message: {:02x?}", message);
self.preprocess_message(message)
@@ -331,7 +327,7 @@ impl CtapHid {
&mut self,
env: &mut impl Env,
message: Message,
clock_value: ClockValue,
clock_value: CtapInstant,
ctap_state: &mut CtapState,
) -> Message {
// If another command arrives, stop winking to prevent accidential button touches.
@@ -389,7 +385,7 @@ impl CtapHid {
&mut self,
env: &mut impl Env,
packet: &HidPacket,
clock_value: ClockValue,
clock_value: CtapInstant,
ctap_state: &mut CtapState,
) -> HidPacketIterator {
if let Some(message) = self.parse_packet(env, packet, clock_value) {
@@ -482,7 +478,7 @@ impl CtapHid {
}
/// Returns whether a wink permission is currently granted.
pub fn should_wink(&self, now: ClockValue) -> bool {
pub fn should_wink(&self, now: CtapInstant) -> bool {
self.wink_permission.is_granted(now)
}
@@ -514,11 +510,6 @@ mod test {
use super::*;
use crate::env::test::TestEnv;
const CLOCK_FREQUENCY_HZ: usize = 32768;
// Except for tests for timeouts (done in ctap1.rs), transactions are time independant.
const DUMMY_CLOCK_VALUE: ClockValue = ClockValue::new(0, CLOCK_FREQUENCY_HZ);
const DUMMY_TIMESTAMP: Timestamp<isize> = Timestamp::from_ms(0);
fn process_messages(
env: &mut TestEnv,
ctap_hid: &mut CtapHid,
@@ -527,12 +518,14 @@ mod test {
) -> Option<Vec<Message>> {
let mut result = Vec::new();
let mut assembler_reply = MessageAssembler::new();
// Except for tests for timeouts (done in ctap1.rs), transactions are time independant.
for msg_request in request {
for pkt_request in HidPacketIterator::new(msg_request).unwrap() {
for pkt_reply in
ctap_hid.process_hid_packet(env, &pkt_request, DUMMY_CLOCK_VALUE, ctap_state)
ctap_hid.process_hid_packet(env, &pkt_request, CtapInstant::new(0), ctap_state)
{
match assembler_reply.parse_packet(&pkt_reply, DUMMY_TIMESTAMP) {
match assembler_reply.parse_packet(&pkt_reply, CtapInstant::new(0)) {
Ok(Some(message)) => result.push(message),
Ok(None) => (),
Err(_) => return None,
@@ -584,7 +577,7 @@ mod test {
let mut messages = Vec::new();
let mut assembler = MessageAssembler::new();
for packet in HidPacketIterator::new(message.clone()).unwrap() {
match assembler.parse_packet(&packet, DUMMY_TIMESTAMP) {
match assembler.parse_packet(&packet, CtapInstant::new(0)) {
Ok(Some(msg)) => messages.push(msg),
Ok(None) => (),
Err(_) => panic!("Couldn't assemble packet: {:02x?}", &packet as &[u8]),
@@ -598,19 +591,19 @@ mod test {
#[test]
fn test_spurious_continuation_packet() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let mut ctap_hid = CtapHid::new();
let mut packet = [0x00; 64];
packet[0..7].copy_from_slice(&[0xC1, 0xC1, 0xC1, 0xC1, 0x00, 0x51, 0x51]);
let mut assembler_reply = MessageAssembler::new();
for pkt_reply in
ctap_hid.process_hid_packet(&mut env, &packet, DUMMY_CLOCK_VALUE, &mut ctap_state)
ctap_hid.process_hid_packet(&mut env, &packet, CtapInstant::new(0), &mut ctap_state)
{
// Continuation packets are silently ignored.
assert_eq!(
assembler_reply
.parse_packet(&pkt_reply, DUMMY_TIMESTAMP)
.parse_packet(&pkt_reply, CtapInstant::new(0))
.unwrap(),
None
);
@@ -620,7 +613,7 @@ mod test {
#[test]
fn test_command_init() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let mut ctap_hid = CtapHid::new();
let reply = process_messages(
@@ -665,7 +658,7 @@ mod test {
#[test]
fn test_command_init_for_sync() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let mut ctap_hid = CtapHid::new();
let cid = cid_from_init(&mut env, &mut ctap_hid, &mut ctap_state);
@@ -685,11 +678,11 @@ mod test {
for pkt_reply in ctap_hid.process_hid_packet(
&mut env,
pkt_request,
DUMMY_CLOCK_VALUE,
CtapInstant::new(0),
&mut ctap_state,
) {
if let Some(message) = assembler_reply
.parse_packet(&pkt_reply, DUMMY_TIMESTAMP)
.parse_packet(&pkt_reply, CtapInstant::new(0))
.unwrap()
{
result.push(message);
@@ -727,7 +720,7 @@ mod test {
#[test]
fn test_command_ping() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let mut ctap_hid = CtapHid::new();
let cid = cid_from_init(&mut env, &mut ctap_hid, &mut ctap_state);

View File

@@ -12,11 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::clock::CtapInstant;
use super::super::customization::MAX_MSG_SIZE;
use super::{ChannelID, CtapHid, CtapHidCommand, HidPacket, Message, ProcessedPacket};
use alloc::vec::Vec;
use core::mem::swap;
use libtock_drivers::timer::Timestamp;
// A structure to assemble CTAPHID commands from a series of incoming USB HID packets.
pub struct MessageAssembler {
@@ -25,7 +26,7 @@ pub struct MessageAssembler {
// Current channel ID.
cid: ChannelID,
// Timestamp of the last packet received on the current channel.
last_timestamp: Timestamp<isize>,
last_timestamp: CtapInstant,
// Current command.
cmd: u8,
// Sequence number expected for the next packet.
@@ -57,7 +58,7 @@ impl MessageAssembler {
MessageAssembler {
idle: true,
cid: [0, 0, 0, 0],
last_timestamp: Timestamp::from_ms(0),
last_timestamp: CtapInstant::new(0),
cmd: 0,
seq: 0,
remaining_payload_len: 0,
@@ -70,7 +71,7 @@ impl MessageAssembler {
pub fn reset(&mut self) {
self.idle = true;
self.cid = [0, 0, 0, 0];
self.last_timestamp = Timestamp::from_ms(0);
self.last_timestamp = CtapInstant::new(0);
self.cmd = 0;
self.seq = 0;
self.remaining_payload_len = 0;
@@ -87,13 +88,13 @@ impl MessageAssembler {
pub fn parse_packet(
&mut self,
packet: &HidPacket,
timestamp: Timestamp<isize>,
timestamp: CtapInstant,
) -> Result<Option<Message>, (ChannelID, Error)> {
// TODO: Support non-full-speed devices (i.e. packet len != 64)? This isn't recommended by
// section 8.8.1
let (cid, processed_packet) = CtapHid::process_single_packet(packet);
if !self.idle && timestamp - self.last_timestamp >= CtapHid::TIMEOUT_DURATION {
if !self.idle && timestamp >= self.last_timestamp + CtapHid::TIMEOUT_DURATION {
// The current channel timed out.
// Save the channel ID and reset the state.
let current_cid = self.cid;
@@ -160,7 +161,7 @@ impl MessageAssembler {
cmd: u8,
len: usize,
data: &[u8],
timestamp: Timestamp<isize>,
timestamp: CtapInstant,
) -> Result<Option<Message>, (ChannelID, Error)> {
// Reject invalid lengths early to reduce the risk of running out of memory.
// TODO: also reject invalid commands early?
@@ -198,12 +199,10 @@ impl MessageAssembler {
#[cfg(test)]
mod test {
use super::*;
use libtock_drivers::timer::Duration;
use crate::ctap::hid::CtapHid;
use embedded_time::duration::Milliseconds;
// Except for tests that exercise timeouts, all packets are synchronized at the same dummy
// timestamp.
const DUMMY_TIMESTAMP: Timestamp<isize> = Timestamp::from_ms(0);
use super::*;
fn byte_extend(bytes: &[u8], padding: u8) -> HidPacket {
let len = bytes.len();
@@ -223,10 +222,12 @@ mod test {
#[test]
fn test_empty_payload() {
let mut assembler = MessageAssembler::new();
// Except for tests that exercise timeouts, all packets are synchronized at the same dummy
// timestamp.
assert_eq!(
assembler.parse_packet(
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x90]),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Ok(Some(Message {
cid: [0x12, 0x34, 0x56, 0x78],
@@ -242,7 +243,7 @@ mod test {
assert_eq!(
assembler.parse_packet(
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x90, 0x00, 0x10]),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Ok(Some(Message {
cid: [0x12, 0x34, 0x56, 0x78],
@@ -261,7 +262,7 @@ mod test {
assert_eq!(
assembler.parse_packet(
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x90, 0x00, 0x10], 0xFF),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Ok(Some(Message {
cid: [0x12, 0x34, 0x56, 0x78],
@@ -277,14 +278,14 @@ mod test {
assert_eq!(
assembler.parse_packet(
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Ok(None)
);
assert_eq!(
assembler.parse_packet(
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Ok(Some(Message {
cid: [0x12, 0x34, 0x56, 0x78],
@@ -300,21 +301,21 @@ mod test {
assert_eq!(
assembler.parse_packet(
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x80]),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Ok(None)
);
assert_eq!(
assembler.parse_packet(
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Ok(None)
);
assert_eq!(
assembler.parse_packet(
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x01]),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Ok(Some(Message {
cid: [0x12, 0x34, 0x56, 0x78],
@@ -330,7 +331,7 @@ mod test {
assert_eq!(
assembler.parse_packet(
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x1D, 0xB9]),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Ok(None)
);
@@ -338,7 +339,7 @@ mod test {
assert_eq!(
assembler.parse_packet(
&zero_extend(&[0x12, 0x34, 0x56, 0x78, seq]),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Ok(None)
);
@@ -346,7 +347,7 @@ mod test {
assert_eq!(
assembler.parse_packet(
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x7F]),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Ok(Some(Message {
cid: [0x12, 0x34, 0x56, 0x78],
@@ -371,21 +372,21 @@ mod test {
&[0x12, 0x34, 0x56, 0x78, 0x80 | cmd as u8, 0x00, 0x80],
byte
),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Ok(None)
);
assert_eq!(
assembler.parse_packet(
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x00], byte),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Ok(None)
);
assert_eq!(
assembler.parse_packet(
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x01], byte),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Ok(Some(Message {
cid: [0x12, 0x34, 0x56, 0x78],
@@ -409,21 +410,21 @@ mod test {
assert_eq!(
assembler.parse_packet(
&byte_extend(&[0x12, 0x34, 0x56, cid, 0x80 | cmd as u8, 0x00, 0x80], byte),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Ok(None)
);
assert_eq!(
assembler.parse_packet(
&byte_extend(&[0x12, 0x34, 0x56, cid, 0x00], byte),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Ok(None)
);
assert_eq!(
assembler.parse_packet(
&byte_extend(&[0x12, 0x34, 0x56, cid, 0x01], byte),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Ok(Some(Message {
cid: [0x12, 0x34, 0x56, cid],
@@ -440,7 +441,7 @@ mod test {
assert_eq!(
assembler.parse_packet(
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Ok(None)
);
@@ -452,7 +453,7 @@ mod test {
assert_eq!(
assembler.parse_packet(
&byte_extend(&[0x12, 0x34, 0x56, 0x9A, cmd as u8, 0x00], byte),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Err(([0x12, 0x34, 0x56, 0x9A], Error::UnexpectedChannel))
);
@@ -462,7 +463,7 @@ mod test {
assert_eq!(
assembler.parse_packet(
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Ok(Some(Message {
cid: [0x12, 0x34, 0x56, 0x78],
@@ -484,7 +485,7 @@ mod test {
assert_eq!(
assembler.parse_packet(
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x10], byte),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Ok(Some(Message {
cid: [0x12, 0x34, 0x56, 0x78],
@@ -498,7 +499,7 @@ mod test {
assert_eq!(
assembler.parse_packet(
&zero_extend(&[0x12, 0x34, 0x56, 0x78, seq]),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Err(([0x12, 0x34, 0x56, 0x78], Error::UnexpectedContinuation))
);
@@ -511,14 +512,14 @@ mod test {
assert_eq!(
assembler.parse_packet(
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Ok(None)
);
assert_eq!(
assembler.parse_packet(
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x80]),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Err(([0x12, 0x34, 0x56, 0x78], Error::UnexpectedInit))
);
@@ -530,14 +531,14 @@ mod test {
assert_eq!(
assembler.parse_packet(
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Ok(None)
);
assert_eq!(
assembler.parse_packet(
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x01]),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Err(([0x12, 0x34, 0x56, 0x78], Error::UnexpectedSeq))
);
@@ -549,14 +550,14 @@ mod test {
assert_eq!(
assembler.parse_packet(
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Ok(None)
);
assert_eq!(
assembler.parse_packet(
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]),
DUMMY_TIMESTAMP + CtapHid::TIMEOUT_DURATION
CtapInstant::new(0) + CtapHid::TIMEOUT_DURATION
),
Err(([0x12, 0x34, 0x56, 0x78], Error::Timeout))
);
@@ -564,9 +565,9 @@ mod test {
#[test]
fn test_just_in_time_packets() {
let mut timestamp = DUMMY_TIMESTAMP;
let mut timestamp: CtapInstant = CtapInstant::new(0);
// Delay between each packet is just below the threshold.
let delay = CtapHid::TIMEOUT_DURATION - Duration::from_ms(1);
let delay = CtapHid::TIMEOUT_DURATION - Milliseconds(1_u32);
let mut assembler = MessageAssembler::new();
assert_eq!(
@@ -577,13 +578,13 @@ mod test {
Ok(None)
);
for seq in 0..0x7F {
timestamp += delay;
timestamp = timestamp + delay;
assert_eq!(
assembler.parse_packet(&zero_extend(&[0x12, 0x34, 0x56, 0x78, seq]), timestamp),
Ok(None)
);
}
timestamp += delay;
timestamp = timestamp + delay;
assert_eq!(
assembler.parse_packet(&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x7F]), timestamp),
Ok(Some(Message {
@@ -601,7 +602,7 @@ mod test {
assert_eq!(
assembler.parse_packet(
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x02, 0x00], 0x51),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Ok(None)
);
@@ -612,7 +613,7 @@ mod test {
0x12, 0x34, 0x56, 0x78, 0x86, 0x00, 0x08, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC,
0xDE, 0xF0
]),
DUMMY_TIMESTAMP
CtapInstant::new(0)
),
Ok(Some(Message {
cid: [0x12, 0x34, 0x56, 0x78],

View File

@@ -65,6 +65,7 @@ use self::timed_permission::TimedPermission;
use self::timed_permission::U2fUserPresenceState;
use crate::api::firmware_protection::FirmwareProtection;
use crate::api::upgrade_storage::UpgradeStorage;
use crate::clock::{ClockInt, CtapInstant};
use crate::env::{Env, UserPresence};
use alloc::boxed::Box;
use alloc::string::{String, ToString};
@@ -78,7 +79,7 @@ use crypto::hmac::{hmac_256, verify_hmac_256};
use crypto::rng256::Rng256;
use crypto::sha256::Sha256;
use crypto::Hash256;
use libtock_drivers::timer::{ClockValue, Duration};
use embedded_time::duration::Milliseconds;
use sk_cbor as cbor;
use sk_cbor::cbor_map_options;
@@ -101,12 +102,14 @@ const ED_FLAG: u8 = 0x80;
// CTAP2 specification section 6 requires that the depth of nested CBOR structures be limited to at most four levels.
const MAX_CBOR_NESTING_DEPTH: i8 = 4;
pub const TOUCH_TIMEOUT_MS: isize = 30000;
pub const TOUCH_TIMEOUT_MS: ClockInt = 30000;
#[cfg(feature = "with_ctap1")]
const U2F_UP_PROMPT_TIMEOUT: Duration<isize> = Duration::from_ms(10000);
pub const TOUCH_TIMEOUT: Milliseconds<ClockInt> = Milliseconds(TOUCH_TIMEOUT_MS);
#[cfg(feature = "with_ctap1")]
const U2F_UP_PROMPT_TIMEOUT: Milliseconds<ClockInt> = Milliseconds(10000 as ClockInt);
// TODO(kaczmarczyck) 2.1 allows Reset after Reset and 15 seconds?
const RESET_TIMEOUT_DURATION: Duration<isize> = Duration::from_ms(10000);
const STATEFUL_COMMAND_TIMEOUT_DURATION: Duration<isize> = Duration::from_ms(30000);
const RESET_TIMEOUT_DURATION: Milliseconds<ClockInt> = Milliseconds(10000 as ClockInt);
const STATEFUL_COMMAND_TIMEOUT_DURATION: Milliseconds<ClockInt> = Milliseconds(30000 as ClockInt);
pub const FIDO2_VERSION_STRING: &str = "FIDO_2_0";
#[cfg(feature = "with_ctap1")]
@@ -239,7 +242,7 @@ impl StatefulPermission {
///
/// Resets are only possible after a power cycle. Therefore, initialization
/// means allowing Reset, and Reset cannot be granted later.
pub fn new_reset(now: ClockValue) -> StatefulPermission {
pub fn new_reset(now: CtapInstant) -> StatefulPermission {
StatefulPermission {
permission: TimedPermission::granted(now, RESET_TIMEOUT_DURATION),
command_type: Some(StatefulCommand::Reset),
@@ -253,7 +256,7 @@ impl StatefulPermission {
}
/// Checks the permission timeout.
pub fn check_command_permission(&mut self, now: ClockValue) -> Result<(), Ctap2StatusCode> {
pub fn check_command_permission(&mut self, now: CtapInstant) -> Result<(), Ctap2StatusCode> {
if self.permission.is_granted(now) {
Ok(())
} else {
@@ -270,7 +273,7 @@ impl StatefulPermission {
}
/// Sets a new command state, and starts a new clock for timeouts.
pub fn set_command(&mut self, now: ClockValue, new_command_type: StatefulCommand) {
pub fn set_command(&mut self, now: CtapInstant, new_command_type: StatefulCommand) {
match &new_command_type {
// Reset is only allowed after a power cycle.
StatefulCommand::Reset => unreachable!(),
@@ -335,22 +338,19 @@ pub struct CtapState {
}
impl CtapState {
pub fn new(env: &mut impl Env, now: ClockValue) -> Self {
pub fn new(env: &mut impl Env, now: CtapInstant) -> Self {
storage::init(env).ok().unwrap();
let client_pin = ClientPin::new(env.rng());
CtapState {
client_pin,
#[cfg(feature = "with_ctap1")]
u2f_up_state: U2fUserPresenceState::new(
U2F_UP_PROMPT_TIMEOUT,
Duration::from_ms(TOUCH_TIMEOUT_MS),
),
u2f_up_state: U2fUserPresenceState::new(U2F_UP_PROMPT_TIMEOUT, TOUCH_TIMEOUT),
stateful_command_permission: StatefulPermission::new_reset(now),
large_blobs: LargeBlobs::new(),
}
}
pub fn update_timeouts(&mut self, now: ClockValue) {
pub fn update_timeouts(&mut self, now: CtapInstant) {
// Ignore the result, just update.
let _ = self
.stateful_command_permission
@@ -448,7 +448,7 @@ impl CtapState {
env: &mut impl Env,
command_cbor: &[u8],
cid: ChannelID,
now: ClockValue,
now: CtapInstant,
) -> Vec<u8> {
let cmd = Command::deserialize(command_cbor);
debug_ctap!(env, "Received command: {:#?}", cmd);
@@ -457,10 +457,8 @@ impl CtapState {
// Correct behavior between CTAP1 and CTAP2 isn't defined yet. Just a guess.
#[cfg(feature = "with_ctap1")]
{
self.u2f_up_state = U2fUserPresenceState::new(
U2F_UP_PROMPT_TIMEOUT,
Duration::from_ms(TOUCH_TIMEOUT_MS),
);
self.u2f_up_state =
U2fUserPresenceState::new(U2F_UP_PROMPT_TIMEOUT, TOUCH_TIMEOUT);
}
match (&command, self.stateful_command_permission.get_command()) {
(Command::AuthenticatorGetNextAssertion, Ok(StatefulCommand::GetAssertion(_)))
@@ -925,7 +923,7 @@ impl CtapState {
env: &mut impl Env,
get_assertion_params: AuthenticatorGetAssertionParameters,
cid: ChannelID,
now: ClockValue,
now: CtapInstant,
) -> Result<ResponseData, Ctap2StatusCode> {
let AuthenticatorGetAssertionParameters {
rp_id,
@@ -1055,7 +1053,7 @@ impl CtapState {
fn process_get_next_assertion(
&mut self,
env: &mut impl Env,
now: ClockValue,
now: CtapInstant,
) -> Result<ResponseData, Ctap2StatusCode> {
self.stateful_command_permission
.check_command_permission(now)?;
@@ -1136,7 +1134,7 @@ impl CtapState {
&mut self,
env: &mut impl Env,
cid: ChannelID,
now: ClockValue,
now: CtapInstant,
) -> Result<ResponseData, Ctap2StatusCode> {
self.stateful_command_permission
.check_command_permission(now)?;
@@ -1150,10 +1148,7 @@ impl CtapState {
self.client_pin.reset(env.rng());
#[cfg(feature = "with_ctap1")]
{
self.u2f_up_state = U2fUserPresenceState::new(
U2F_UP_PROMPT_TIMEOUT,
Duration::from_ms(TOUCH_TIMEOUT_MS),
);
self.u2f_up_state = U2fUserPresenceState::new(U2F_UP_PROMPT_TIMEOUT, TOUCH_TIMEOUT);
}
Ok(ResponseData::AuthenticatorReset)
}
@@ -1312,12 +1307,12 @@ impl CtapState {
}
#[cfg(feature = "with_ctap1")]
pub fn u2f_grant_user_presence(&mut self, now: ClockValue) {
pub fn u2f_grant_user_presence(&mut self, now: CtapInstant) {
self.u2f_up_state.grant_up(now)
}
#[cfg(feature = "with_ctap1")]
pub fn u2f_needs_user_presence(&mut self, now: ClockValue) -> bool {
pub fn u2f_needs_user_presence(&mut self, now: CtapInstant) -> bool {
self.u2f_up_state.is_up_needed(now)
}
}
@@ -1336,8 +1331,6 @@ mod test {
use crate::env::test::TestEnv;
use cbor::{cbor_array, cbor_array_vec, cbor_map};
const CLOCK_FREQUENCY_HZ: usize = 32768;
const DUMMY_CLOCK_VALUE: ClockValue = ClockValue::new(0, CLOCK_FREQUENCY_HZ);
// The keep-alive logic in the processing of some commands needs a channel ID to send
// keep-alive packets to.
// In tests where we define a dummy user-presence check that immediately returns, the channel
@@ -1389,9 +1382,9 @@ mod test {
#[test]
fn test_get_info() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let info_reponse =
ctap_state.process_command(&mut env, &[0x04], DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE);
ctap_state.process_command(&mut env, &[0x04], DUMMY_CHANNEL_ID, CtapInstant::new(0));
let expected_cbor = cbor_map_options! {
0x01 => cbor_array_vec![vec![
@@ -1501,7 +1494,7 @@ mod test {
#[test]
fn test_resident_process_make_credential() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let make_credential_params = create_minimal_make_credential_parameters();
let make_credential_response =
@@ -1519,7 +1512,7 @@ mod test {
#[test]
fn test_non_resident_process_make_credential() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let mut make_credential_params = create_minimal_make_credential_parameters();
make_credential_params.options.rk = false;
@@ -1538,7 +1531,7 @@ mod test {
#[test]
fn test_process_make_credential_unsupported_algorithm() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let mut make_credential_params = create_minimal_make_credential_parameters();
make_credential_params.pub_key_cred_params = vec![];
@@ -1555,7 +1548,7 @@ mod test {
fn test_process_make_credential_credential_excluded() {
let mut env = TestEnv::new();
let excluded_private_key = crypto::ecdsa::SecKey::gensk(env.rng());
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let excluded_credential_id = vec![0x01, 0x23, 0x45, 0x67];
let make_credential_params =
@@ -1587,7 +1580,7 @@ mod test {
#[test]
fn test_process_make_credential_credential_with_cred_protect() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let test_policy = CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList;
let make_credential_params =
@@ -1638,7 +1631,7 @@ mod test {
#[test]
fn test_process_make_credential_hmac_secret() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let extensions = MakeCredentialExtensions {
hmac_secret: true,
@@ -1665,7 +1658,7 @@ mod test {
#[test]
fn test_process_make_credential_hmac_secret_resident_key() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let extensions = MakeCredentialExtensions {
hmac_secret: true,
@@ -1691,7 +1684,7 @@ mod test {
#[test]
fn test_process_make_credential_min_pin_length() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
// First part: The extension is ignored, since the RP ID is not on the list.
let extensions = MakeCredentialExtensions {
@@ -1740,7 +1733,7 @@ mod test {
#[test]
fn test_process_make_credential_cred_blob_ok() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let extensions = MakeCredentialExtensions {
cred_blob: Some(vec![0xCB]),
@@ -1772,7 +1765,7 @@ mod test {
#[test]
fn test_process_make_credential_cred_blob_too_big() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let extensions = MakeCredentialExtensions {
cred_blob: Some(vec![0xCB; MAX_CRED_BLOB_LENGTH + 1]),
@@ -1804,7 +1797,7 @@ mod test {
#[test]
fn test_process_make_credential_large_blob_key() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let extensions = MakeCredentialExtensions {
large_blob_key: Some(true),
@@ -1839,7 +1832,7 @@ mod test {
let client_pin =
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol);
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
ctap_state.client_pin = client_pin;
storage::set_pin(&mut env, &[0x88; 16], 4).unwrap();
@@ -1888,7 +1881,7 @@ mod test {
#[test]
fn test_non_resident_process_make_credential_with_pin() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
storage::set_pin(&mut env, &[0x88; 16], 4).unwrap();
let mut make_credential_params = create_minimal_make_credential_parameters();
@@ -1908,7 +1901,7 @@ mod test {
#[test]
fn test_resident_process_make_credential_with_pin() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
storage::set_pin(&mut env, &[0x88; 16], 4).unwrap();
let make_credential_params = create_minimal_make_credential_parameters();
@@ -1923,7 +1916,7 @@ mod test {
#[test]
fn test_process_make_credential_with_pin_always_uv() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
storage::toggle_always_uv(&mut env).unwrap();
let make_credential_params = create_minimal_make_credential_parameters();
@@ -1951,7 +1944,7 @@ mod test {
let mut env = TestEnv::new();
env.user_presence()
.set(|_| Err(Ctap2StatusCode::CTAP2_ERR_KEEPALIVE_CANCEL));
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let make_credential_params = create_minimal_make_credential_parameters();
let make_credential_response =
@@ -2046,7 +2039,7 @@ mod test {
#[test]
fn test_resident_process_get_assertion() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let make_credential_params = create_minimal_make_credential_parameters();
assert!(ctap_state
@@ -2069,7 +2062,7 @@ mod test {
&mut env,
get_assertion_params,
DUMMY_CHANNEL_ID,
DUMMY_CLOCK_VALUE,
CtapInstant::new(0),
);
let signature_counter = storage::global_signature_counter(&mut env).unwrap();
check_assertion_response(get_assertion_response, vec![0x1D], signature_counter, None);
@@ -2131,7 +2124,7 @@ mod test {
fn test_helper_process_get_assertion_hmac_secret(pin_uv_auth_protocol: PinUvAuthProtocol) {
let mut env = TestEnv::new();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let make_extensions = MakeCredentialExtensions {
hmac_secret: true,
@@ -2167,7 +2160,7 @@ mod test {
let key_agreement_response =
ctap_state
.client_pin
.process_command(&mut env, client_pin_params, DUMMY_CLOCK_VALUE);
.process_command(&mut env, client_pin_params, CtapInstant::new(0));
let get_assertion_params = get_assertion_hmac_secret_params(
key_agreement_key,
key_agreement_response.unwrap(),
@@ -2178,7 +2171,7 @@ mod test {
&mut env,
get_assertion_params,
DUMMY_CHANNEL_ID,
DUMMY_CLOCK_VALUE,
CtapInstant::new(0),
);
assert!(get_assertion_response.is_ok());
}
@@ -2198,7 +2191,7 @@ mod test {
) {
let mut env = TestEnv::new();
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let make_extensions = MakeCredentialExtensions {
hmac_secret: true,
@@ -2223,7 +2216,7 @@ mod test {
let key_agreement_response =
ctap_state
.client_pin
.process_command(&mut env, client_pin_params, DUMMY_CLOCK_VALUE);
.process_command(&mut env, client_pin_params, CtapInstant::new(0));
let get_assertion_params = get_assertion_hmac_secret_params(
key_agreement_key,
key_agreement_response.unwrap(),
@@ -2234,7 +2227,7 @@ mod test {
&mut env,
get_assertion_params,
DUMMY_CHANNEL_ID,
DUMMY_CLOCK_VALUE,
CtapInstant::new(0),
);
assert!(get_assertion_response.is_ok());
}
@@ -2254,7 +2247,7 @@ mod test {
let mut env = TestEnv::new();
let private_key = crypto::ecdsa::SecKey::gensk(env.rng());
let credential_id = env.rng().gen_uniform_u8x32().to_vec();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let cred_desc = PublicKeyCredentialDescriptor {
key_type: PublicKeyCredentialType::PublicKey,
@@ -2295,7 +2288,7 @@ mod test {
&mut env,
get_assertion_params,
DUMMY_CHANNEL_ID,
DUMMY_CLOCK_VALUE,
CtapInstant::new(0),
);
assert_eq!(
get_assertion_response,
@@ -2318,7 +2311,7 @@ mod test {
&mut env,
get_assertion_params,
DUMMY_CHANNEL_ID,
DUMMY_CLOCK_VALUE,
CtapInstant::new(0),
);
let signature_counter = storage::global_signature_counter(&mut env).unwrap();
check_assertion_response(get_assertion_response, vec![0x1D], signature_counter, None);
@@ -2355,7 +2348,7 @@ mod test {
&mut env,
get_assertion_params,
DUMMY_CHANNEL_ID,
DUMMY_CLOCK_VALUE,
CtapInstant::new(0),
);
assert_eq!(
get_assertion_response,
@@ -2368,7 +2361,7 @@ mod test {
let mut env = TestEnv::new();
let private_key = crypto::ecdsa::SecKey::gensk(env.rng());
let credential_id = env.rng().gen_uniform_u8x32().to_vec();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let credential = PublicKeyCredentialSource {
key_type: PublicKeyCredentialType::PublicKey,
@@ -2406,7 +2399,7 @@ mod test {
&mut env,
get_assertion_params,
DUMMY_CHANNEL_ID,
DUMMY_CLOCK_VALUE,
CtapInstant::new(0),
);
let signature_counter = storage::global_signature_counter(&mut env).unwrap();
let expected_extension_cbor = [
@@ -2426,7 +2419,7 @@ mod test {
let mut env = TestEnv::new();
let private_key = crypto::ecdsa::SecKey::gensk(env.rng());
let credential_id = env.rng().gen_uniform_u8x32().to_vec();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let credential = PublicKeyCredentialSource {
key_type: PublicKeyCredentialType::PublicKey,
@@ -2464,7 +2457,7 @@ mod test {
&mut env,
get_assertion_params,
DUMMY_CHANNEL_ID,
DUMMY_CLOCK_VALUE,
CtapInstant::new(0),
);
let large_blob_key = match get_assertion_response.unwrap() {
ResponseData::AuthenticatorGetAssertion(get_assertion_response) => {
@@ -2484,7 +2477,7 @@ mod test {
let client_pin =
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol);
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let mut make_credential_params = create_minimal_make_credential_parameters();
let user1 = PublicKeyCredentialUserEntity {
@@ -2535,7 +2528,7 @@ mod test {
&mut env,
get_assertion_params,
DUMMY_CHANNEL_ID,
DUMMY_CLOCK_VALUE,
CtapInstant::new(0),
);
let signature_counter = storage::global_signature_counter(&mut env).unwrap();
check_assertion_response_with_user(
@@ -2548,7 +2541,7 @@ mod test {
);
let get_assertion_response =
ctap_state.process_get_next_assertion(&mut env, DUMMY_CLOCK_VALUE);
ctap_state.process_get_next_assertion(&mut env, CtapInstant::new(0));
check_assertion_response_with_user(
get_assertion_response,
user1,
@@ -2559,7 +2552,7 @@ mod test {
);
let get_assertion_response =
ctap_state.process_get_next_assertion(&mut env, DUMMY_CLOCK_VALUE);
ctap_state.process_get_next_assertion(&mut env, CtapInstant::new(0));
assert_eq!(
get_assertion_response,
Err(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED)
@@ -2579,7 +2572,7 @@ mod test {
#[test]
fn test_process_get_next_assertion_three_credentials_no_uv() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let mut make_credential_params = create_minimal_make_credential_parameters();
make_credential_params.user.user_id = vec![0x01];
@@ -2622,7 +2615,7 @@ mod test {
&mut env,
get_assertion_params,
DUMMY_CHANNEL_ID,
DUMMY_CLOCK_VALUE,
CtapInstant::new(0),
);
let signature_counter = storage::global_signature_counter(&mut env).unwrap();
check_assertion_response(
@@ -2633,15 +2626,15 @@ mod test {
);
let get_assertion_response =
ctap_state.process_get_next_assertion(&mut env, DUMMY_CLOCK_VALUE);
ctap_state.process_get_next_assertion(&mut env, CtapInstant::new(0));
check_assertion_response(get_assertion_response, vec![0x02], signature_counter, None);
let get_assertion_response =
ctap_state.process_get_next_assertion(&mut env, DUMMY_CLOCK_VALUE);
ctap_state.process_get_next_assertion(&mut env, CtapInstant::new(0));
check_assertion_response(get_assertion_response, vec![0x01], signature_counter, None);
let get_assertion_response =
ctap_state.process_get_next_assertion(&mut env, DUMMY_CLOCK_VALUE);
ctap_state.process_get_next_assertion(&mut env, CtapInstant::new(0));
assert_eq!(
get_assertion_response,
Err(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED)
@@ -2651,10 +2644,10 @@ mod test {
#[test]
fn test_process_get_next_assertion_not_allowed() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let get_assertion_response =
ctap_state.process_get_next_assertion(&mut env, DUMMY_CLOCK_VALUE);
ctap_state.process_get_next_assertion(&mut env, CtapInstant::new(0));
assert_eq!(
get_assertion_response,
Err(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED)
@@ -2687,7 +2680,7 @@ mod test {
&mut env,
get_assertion_params,
DUMMY_CHANNEL_ID,
DUMMY_CLOCK_VALUE,
CtapInstant::new(0),
);
assert!(get_assertion_response.is_ok());
@@ -2704,10 +2697,15 @@ mod test {
4 => cbor_array![ES256_CRED_PARAM],
};
assert!(cbor_write(cbor_value, &mut command_cbor).is_ok());
ctap_state.process_command(&mut env, &command_cbor, DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE);
ctap_state.process_command(
&mut env,
&command_cbor,
DUMMY_CHANNEL_ID,
CtapInstant::new(0),
);
let get_assertion_response =
ctap_state.process_get_next_assertion(&mut env, DUMMY_CLOCK_VALUE);
ctap_state.process_get_next_assertion(&mut env, CtapInstant::new(0));
assert_eq!(
get_assertion_response,
Err(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED)
@@ -2718,7 +2716,7 @@ mod test {
fn test_process_reset() {
let mut env = TestEnv::new();
let private_key = crypto::ecdsa::SecKey::gensk(env.rng());
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let credential_id = vec![0x01, 0x23, 0x45, 0x67];
let credential_source = PublicKeyCredentialSource {
@@ -2739,7 +2737,7 @@ mod test {
assert!(storage::count_credentials(&mut env).unwrap() > 0);
let reset_reponse =
ctap_state.process_command(&mut env, &[0x07], DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE);
ctap_state.process_command(&mut env, &[0x07], DUMMY_CHANNEL_ID, CtapInstant::new(0));
let expected_response = vec![0x00];
assert_eq!(reset_reponse, expected_response);
assert!(storage::count_credentials(&mut env).unwrap() == 0);
@@ -2750,9 +2748,10 @@ mod test {
let mut env = TestEnv::new();
env.user_presence()
.set(|_| Err(Ctap2StatusCode::CTAP2_ERR_KEEPALIVE_CANCEL));
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let reset_reponse = ctap_state.process_reset(&mut env, DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE);
let reset_reponse =
ctap_state.process_reset(&mut env, DUMMY_CHANNEL_ID, CtapInstant::new(0));
assert_eq!(
reset_reponse,
@@ -2763,26 +2762,27 @@ mod test {
#[test]
fn test_process_reset_not_first() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
// This is a GetNextAssertion command.
ctap_state.process_command(&mut env, &[0x08], DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE);
ctap_state.process_command(&mut env, &[0x08], DUMMY_CHANNEL_ID, CtapInstant::new(0));
let reset_reponse = ctap_state.process_reset(&mut env, DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE);
let reset_reponse =
ctap_state.process_reset(&mut env, DUMMY_CHANNEL_ID, CtapInstant::new(0));
assert_eq!(reset_reponse, Err(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED));
}
#[test]
fn test_process_credential_management_unknown_subcommand() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
// The subcommand 0xEE does not exist.
let reponse = ctap_state.process_command(
&mut env,
&[0x0A, 0xA1, 0x01, 0x18, 0xEE],
DUMMY_CHANNEL_ID,
DUMMY_CLOCK_VALUE,
CtapInstant::new(0),
);
let expected_response = vec![Ctap2StatusCode::CTAP2_ERR_INVALID_SUBCOMMAND as u8];
assert_eq!(reponse, expected_response);
@@ -2791,11 +2791,11 @@ mod test {
#[test]
fn test_process_unknown_command() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
// This command does not exist.
let reponse =
ctap_state.process_command(&mut env, &[0xDF], DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE);
ctap_state.process_command(&mut env, &[0xDF], DUMMY_CHANNEL_ID, CtapInstant::new(0));
let expected_response = vec![Ctap2StatusCode::CTAP1_ERR_INVALID_COMMAND as u8];
assert_eq!(reponse, expected_response);
}
@@ -2804,7 +2804,7 @@ mod test {
fn test_encrypt_decrypt_credential() {
let mut env = TestEnv::new();
let private_key = crypto::ecdsa::SecKey::gensk(env.rng());
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
// Usually, the relying party ID or its hash is provided by the client.
// We are not testing the correctness of our SHA256 here, only if it is checked.
@@ -2824,7 +2824,7 @@ mod test {
fn test_encrypt_decrypt_bad_hmac() {
let mut env = TestEnv::new();
let private_key = crypto::ecdsa::SecKey::gensk(env.rng());
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
// Same as above.
let rp_id_hash = [0x55; 32];
@@ -2844,7 +2844,7 @@ mod test {
#[test]
fn test_signature_counter() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let mut last_counter = storage::global_signature_counter(&mut env).unwrap();
assert!(last_counter > 0);
@@ -2861,7 +2861,7 @@ mod test {
#[test]
fn test_vendor_configure() {
let mut env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
// Nothing should be configured at the beginning
let response = ctap_state.process_vendor_configure(
@@ -3066,7 +3066,7 @@ mod test {
// The test identifier matches partition B.
let mut env = TestEnv::new();
let private_key = crypto::ecdsa::SecKey::gensk(env.rng());
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
const METADATA_LEN: usize = 40;
let data = vec![0xFF; 0x1000];
@@ -3157,7 +3157,7 @@ mod test {
fn test_vendor_upgrade_no_second_partition() {
let mut env = TestEnv::new();
env.disable_upgrade_storage();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let data = vec![0xFF; 0x1000];
let hash = Sha256::hash(&data).to_vec();
@@ -3176,7 +3176,7 @@ mod test {
#[test]
fn test_vendor_upgrade_info() {
let mut env = TestEnv::new();
let ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
let partition_address = env.upgrade_storage().unwrap().partition_address();
let upgrade_info_reponse = ctap_state.process_vendor_upgrade_info(&mut env);

View File

@@ -12,12 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use libtock_drivers::timer::{ClockValue, Duration};
use super::super::clock::{ClockInt, CtapInstant};
use embedded_time::duration::Milliseconds;
#[derive(Clone, Copy, Debug)]
#[derive(Debug, Copy, Clone)]
pub enum TimedPermission {
Waiting,
Granted(ClockValue),
Granted(CtapInstant),
}
impl TimedPermission {
@@ -25,28 +26,25 @@ impl TimedPermission {
TimedPermission::Waiting
}
pub fn granted(now: ClockValue, grant_duration: Duration<isize>) -> TimedPermission {
TimedPermission::Granted(now.wrapping_add(grant_duration))
pub fn granted(now: CtapInstant, grant_duration: Milliseconds<ClockInt>) -> TimedPermission {
// TODO: Should panic or saturate if the grant duration is too big.
TimedPermission::Granted(now.checked_add(grant_duration).unwrap())
}
// Checks if the timeout is not reached, false for differing ClockValue frequencies.
pub fn is_granted(&self, now: ClockValue) -> bool {
pub fn is_granted(&self, now: CtapInstant) -> bool {
if let TimedPermission::Granted(timeout) = self {
if let Some(remaining_duration) = timeout.wrapping_sub(now) {
return remaining_duration > Duration::from_ms(0);
}
return timeout.checked_duration_since(&now).is_some();
}
false
}
// Consumes the state and returns the current new permission state at time "now".
// Returns a new state for differing ClockValue frequencies.
pub fn check_expiration(self, now: ClockValue) -> TimedPermission {
pub fn check_expiration(self, now: CtapInstant) -> TimedPermission {
if let TimedPermission::Granted(timeout) = self {
if let Some(remaining_duration) = timeout.wrapping_sub(now) {
if remaining_duration > Duration::from_ms(0) {
return TimedPermission::Granted(timeout);
}
if timeout.checked_duration_since(&now).is_some() {
return TimedPermission::Granted(timeout);
}
}
TimedPermission::Waiting
@@ -61,16 +59,16 @@ pub struct U2fUserPresenceState {
// Button touch timeouts, while user presence is requested, are saved here.
has_up: TimedPermission,
// This is the timeout duration of user presence requests.
request_duration: Duration<isize>,
request_duration: Milliseconds<ClockInt>,
// This is the timeout duration of button touches.
presence_duration: Duration<isize>,
presence_duration: Milliseconds<ClockInt>,
}
#[cfg(feature = "with_ctap1")]
impl U2fUserPresenceState {
pub fn new(
request_duration: Duration<isize>,
presence_duration: Duration<isize>,
request_duration: Milliseconds<ClockInt>,
presence_duration: Milliseconds<ClockInt>,
) -> U2fUserPresenceState {
U2fUserPresenceState {
needs_up: TimedPermission::Waiting,
@@ -81,7 +79,7 @@ impl U2fUserPresenceState {
}
// Granting user presence is ignored if it needs activation, but waits. Also cleans up.
pub fn grant_up(&mut self, now: ClockValue) {
pub fn grant_up(&mut self, now: CtapInstant) {
self.check_expiration(now);
if self.needs_up.is_granted(now) {
self.needs_up = TimedPermission::Waiting;
@@ -90,7 +88,7 @@ impl U2fUserPresenceState {
}
// This marks user presence as needed or uses it up if already granted. Also cleans up.
pub fn consume_up(&mut self, now: ClockValue) -> bool {
pub fn consume_up(&mut self, now: CtapInstant) -> bool {
self.check_expiration(now);
if self.has_up.is_granted(now) {
self.has_up = TimedPermission::Waiting;
@@ -102,13 +100,13 @@ impl U2fUserPresenceState {
}
// Returns if user presence was requested. Also cleans up.
pub fn is_up_needed(&mut self, now: ClockValue) -> bool {
pub fn is_up_needed(&mut self, now: CtapInstant) -> bool {
self.check_expiration(now);
self.needs_up.is_granted(now)
}
// If you don't regularly call any other function, not cleaning up leads to overflow problems.
pub fn check_expiration(&mut self, now: ClockValue) {
pub fn check_expiration(&mut self, now: CtapInstant) {
self.needs_up = self.needs_up.check_expiration(now);
self.has_up = self.has_up.check_expiration(now);
}
@@ -118,18 +116,29 @@ impl U2fUserPresenceState {
#[cfg(test)]
mod test {
use super::*;
use core::isize;
const CLOCK_FREQUENCY_HZ: usize = 32768;
const ZERO: ClockValue = ClockValue::new(0, CLOCK_FREQUENCY_HZ);
const BIG_POSITIVE: ClockValue = ClockValue::new(isize::MAX / 1000 - 1, CLOCK_FREQUENCY_HZ);
const NEGATIVE: ClockValue = ClockValue::new(-1, CLOCK_FREQUENCY_HZ);
const SMALL_NEGATIVE: ClockValue = ClockValue::new(isize::MIN / 1000 + 1, CLOCK_FREQUENCY_HZ);
const REQUEST_DURATION: Duration<isize> = Duration::from_ms(1000);
const PRESENCE_DURATION: Duration<isize> = Duration::from_ms(1000);
fn zero() -> CtapInstant {
CtapInstant::new(0)
}
fn grant_up_when_needed(start_time: ClockValue) {
let mut u2f_state = U2fUserPresenceState::new(REQUEST_DURATION, PRESENCE_DURATION);
fn big_positive() -> CtapInstant {
CtapInstant::new(u64::MAX / 1000 - 1)
}
fn request_duration() -> Milliseconds<u64> {
Milliseconds::new(1000)
}
fn presence_duration() -> Milliseconds<u64> {
Milliseconds::new(1000)
}
fn epsilon() -> Milliseconds<u64> {
Milliseconds::new(1)
}
fn grant_up_when_needed(start_time: CtapInstant) {
let mut u2f_state = U2fUserPresenceState::new(request_duration(), presence_duration());
assert!(!u2f_state.consume_up(start_time));
assert!(u2f_state.is_up_needed(start_time));
u2f_state.grant_up(start_time);
@@ -137,52 +146,54 @@ mod test {
assert!(!u2f_state.consume_up(start_time));
}
fn need_up_timeout(start_time: ClockValue) {
let mut u2f_state = U2fUserPresenceState::new(REQUEST_DURATION, PRESENCE_DURATION);
fn need_up_timeout(start_time: CtapInstant) {
let mut u2f_state = U2fUserPresenceState::new(request_duration(), presence_duration());
assert!(!u2f_state.consume_up(start_time));
assert!(u2f_state.is_up_needed(start_time));
// The timeout excludes equality, so it should be over at this instant.
assert!(!u2f_state.is_up_needed(start_time.wrapping_add(REQUEST_DURATION)));
assert!(!u2f_state.is_up_needed(
start_time
.checked_add(presence_duration() + epsilon())
.unwrap()
));
}
fn grant_up_timeout(start_time: ClockValue) {
let mut u2f_state = U2fUserPresenceState::new(REQUEST_DURATION, PRESENCE_DURATION);
fn grant_up_timeout(start_time: CtapInstant) {
let mut u2f_state = U2fUserPresenceState::new(request_duration(), presence_duration());
assert!(!u2f_state.consume_up(start_time));
assert!(u2f_state.is_up_needed(start_time));
u2f_state.grant_up(start_time);
// The timeout excludes equality, so it should be over at this instant.
assert!(!u2f_state.consume_up(start_time.wrapping_add(PRESENCE_DURATION)));
assert!(!u2f_state.consume_up(
start_time
.checked_add(presence_duration() + epsilon())
.unwrap()
));
}
#[test]
fn test_grant_up_timeout() {
grant_up_timeout(ZERO);
grant_up_timeout(BIG_POSITIVE);
grant_up_timeout(NEGATIVE);
grant_up_timeout(SMALL_NEGATIVE);
grant_up_timeout(zero());
grant_up_timeout(big_positive());
}
#[test]
fn test_need_up_timeout() {
need_up_timeout(ZERO);
need_up_timeout(BIG_POSITIVE);
need_up_timeout(NEGATIVE);
need_up_timeout(SMALL_NEGATIVE);
need_up_timeout(zero());
need_up_timeout(big_positive());
}
#[test]
fn test_grant_up_when_needed() {
grant_up_when_needed(ZERO);
grant_up_when_needed(BIG_POSITIVE);
grant_up_when_needed(NEGATIVE);
grant_up_when_needed(SMALL_NEGATIVE);
grant_up_when_needed(zero());
grant_up_when_needed(big_positive());
}
#[test]
fn test_grant_up_without_need() {
let mut u2f_state = U2fUserPresenceState::new(REQUEST_DURATION, PRESENCE_DURATION);
u2f_state.grant_up(ZERO);
assert!(!u2f_state.is_up_needed(ZERO));
assert!(!u2f_state.consume_up(ZERO));
let mut u2f_state = U2fUserPresenceState::new(request_duration(), presence_duration());
u2f_state.grant_up(zero());
assert!(!u2f_state.is_up_needed(zero()));
assert!(!u2f_state.consume_up(zero()));
}
}

View File

@@ -18,14 +18,16 @@ use crate::ctap::timed_permission::TimedPermission;
use alloc::string::String;
use crypto::sha256::Sha256;
use crypto::Hash256;
use libtock_drivers::timer::{ClockValue, Duration};
use embedded_time::duration::Milliseconds;
use crate::clock::{ClockInt, CtapInstant};
/// Timeout for auth tokens.
///
/// This usage time limit is correct for USB, BLE, and internal.
/// NFC only allows 19.8 seconds.
/// TODO(#15) multiplex over transports, add NFC
const INITIAL_USAGE_TIME_LIMIT: Duration<isize> = Duration::from_ms(30000);
const INITIAL_USAGE_TIME_LIMIT: Milliseconds<ClockInt> = Milliseconds(30000 as ClockInt);
/// Implements pinUvAuthToken state from section 6.5.2.1.
///
@@ -111,14 +113,14 @@ impl PinUvAuthTokenState {
}
/// Starts the timer for pinUvAuthToken usage.
pub fn begin_using_pin_uv_auth_token(&mut self, now: ClockValue) {
pub fn begin_using_pin_uv_auth_token(&mut self, now: CtapInstant) {
self.user_verified = true;
self.usage_timer = TimedPermission::granted(now, INITIAL_USAGE_TIME_LIMIT);
self.in_use = true;
}
/// Updates the usage timer, and disables the pinUvAuthToken on timeout.
pub fn pin_uv_auth_token_usage_timer_observer(&mut self, now: ClockValue) {
pub fn pin_uv_auth_token_usage_timer_observer(&mut self, now: CtapInstant) {
if !self.in_use {
return;
}
@@ -158,20 +160,16 @@ mod test {
use super::*;
use enum_iterator::IntoEnumIterator;
const CLOCK_FREQUENCY_HZ: usize = 32768;
const START_CLOCK_VALUE: ClockValue = ClockValue::new(0, CLOCK_FREQUENCY_HZ);
const SMALL_DURATION: Duration<isize> = Duration::from_ms(100);
#[test]
fn test_observer() {
let mut token_state = PinUvAuthTokenState::new();
let mut now = START_CLOCK_VALUE;
let mut now: CtapInstant = CtapInstant::new(0);
token_state.begin_using_pin_uv_auth_token(now);
assert!(token_state.is_in_use());
now = now.wrapping_add(SMALL_DURATION);
now = now + Milliseconds(100_u32);
token_state.pin_uv_auth_token_usage_timer_observer(now);
assert!(token_state.is_in_use());
now = now.wrapping_add(INITIAL_USAGE_TIME_LIMIT);
now = now + INITIAL_USAGE_TIME_LIMIT;
token_state.pin_uv_auth_token_usage_timer_observer(now);
assert!(!token_state.is_in_use());
}
@@ -179,7 +177,8 @@ mod test {
#[test]
fn test_stop() {
let mut token_state = PinUvAuthTokenState::new();
token_state.begin_using_pin_uv_auth_token(START_CLOCK_VALUE);
let now: CtapInstant = CtapInstant::new(0);
token_state.begin_using_pin_uv_auth_token(now);
assert!(token_state.is_in_use());
token_state.stop_using_pin_uv_auth_token();
assert!(!token_state.is_in_use());
@@ -265,11 +264,12 @@ mod test {
fn test_user_verified_flag() {
let mut token_state = PinUvAuthTokenState::new();
assert!(!token_state.get_user_verified_flag_value());
token_state.begin_using_pin_uv_auth_token(START_CLOCK_VALUE);
let now: CtapInstant = CtapInstant::new(0);
token_state.begin_using_pin_uv_auth_token(now);
assert!(token_state.get_user_verified_flag_value());
token_state.clear_user_verified_flag();
assert!(!token_state.get_user_verified_flag_value());
token_state.begin_using_pin_uv_auth_token(START_CLOCK_VALUE);
token_state.begin_using_pin_uv_auth_token(now);
assert!(token_state.get_user_verified_flag_value());
token_state.stop_using_pin_uv_auth_token();
assert!(!token_state.get_user_verified_flag_value());