Clock trait (#596)
* adds generic Env parameters * adds Clock type to Env * use new Clock * TockTimer improvements * new Clock interface * addressed comments * renames constants to milliseconds, other style fixes * removes all cargo fmt artifacts
This commit is contained in:
@@ -12,7 +12,6 @@
|
||||
// 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,
|
||||
@@ -33,7 +32,6 @@ use crypto::sha256::Sha256;
|
||||
use crypto::Hash256;
|
||||
#[cfg(test)]
|
||||
use enum_iterator::IntoEnumIterator;
|
||||
use rng256::Rng256;
|
||||
use subtle::ConstantTimeEq;
|
||||
|
||||
/// The prefix length of the PIN hash that is stored and compared.
|
||||
@@ -106,18 +104,18 @@ pub enum PinPermission {
|
||||
AuthenticatorConfiguration = 0x20,
|
||||
}
|
||||
|
||||
pub struct ClientPin {
|
||||
pub struct ClientPin<E: Env> {
|
||||
pin_protocol_v1: PinProtocol,
|
||||
pin_protocol_v2: PinProtocol,
|
||||
consecutive_pin_mismatches: u8,
|
||||
pin_uv_auth_token_state: PinUvAuthTokenState,
|
||||
pin_uv_auth_token_state: PinUvAuthTokenState<E>,
|
||||
}
|
||||
|
||||
impl ClientPin {
|
||||
pub fn new(rng: &mut impl Rng256) -> ClientPin {
|
||||
impl<E: Env> ClientPin<E> {
|
||||
pub fn new(env: &mut E) -> Self {
|
||||
ClientPin {
|
||||
pin_protocol_v1: PinProtocol::new(rng),
|
||||
pin_protocol_v2: PinProtocol::new(rng),
|
||||
pin_protocol_v1: PinProtocol::new(env.rng()),
|
||||
pin_protocol_v2: PinProtocol::new(env.rng()),
|
||||
consecutive_pin_mismatches: 0,
|
||||
pin_uv_auth_token_state: PinUvAuthTokenState::new(),
|
||||
}
|
||||
@@ -159,7 +157,7 @@ impl ClientPin {
|
||||
/// Also, in case of failure, the key agreement key is randomly reset.
|
||||
fn verify_pin_hash_enc(
|
||||
&mut self,
|
||||
env: &mut impl Env,
|
||||
env: &mut E,
|
||||
pin_uv_auth_protocol: PinUvAuthProtocol,
|
||||
shared_secret: &dyn SharedSecret,
|
||||
pin_hash_enc: Vec<u8>,
|
||||
@@ -197,7 +195,7 @@ impl ClientPin {
|
||||
|
||||
fn process_get_pin_retries(
|
||||
&self,
|
||||
env: &mut impl Env,
|
||||
env: &mut E,
|
||||
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
|
||||
Ok(AuthenticatorClientPinResponse {
|
||||
key_agreement: None,
|
||||
@@ -225,7 +223,7 @@ impl ClientPin {
|
||||
|
||||
fn process_set_pin(
|
||||
&mut self,
|
||||
env: &mut impl Env,
|
||||
env: &mut E,
|
||||
client_pin_params: AuthenticatorClientPinParameters,
|
||||
) -> Result<(), Ctap2StatusCode> {
|
||||
let AuthenticatorClientPinParameters {
|
||||
@@ -252,7 +250,7 @@ impl ClientPin {
|
||||
|
||||
fn process_change_pin(
|
||||
&mut self,
|
||||
env: &mut impl Env,
|
||||
env: &mut E,
|
||||
client_pin_params: AuthenticatorClientPinParameters,
|
||||
) -> Result<(), Ctap2StatusCode> {
|
||||
let AuthenticatorClientPinParameters {
|
||||
@@ -290,9 +288,8 @@ impl ClientPin {
|
||||
|
||||
fn process_get_pin_token(
|
||||
&mut self,
|
||||
env: &mut impl Env,
|
||||
env: &mut E,
|
||||
client_pin_params: AuthenticatorClientPinParameters,
|
||||
now: CtapInstant,
|
||||
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
|
||||
let AuthenticatorClientPinParameters {
|
||||
pin_uv_auth_protocol,
|
||||
@@ -325,7 +322,7 @@ impl ClientPin {
|
||||
self.pin_protocol_v1.reset_pin_uv_auth_token(env.rng());
|
||||
self.pin_protocol_v2.reset_pin_uv_auth_token(env.rng());
|
||||
self.pin_uv_auth_token_state
|
||||
.begin_using_pin_uv_auth_token(now);
|
||||
.begin_using_pin_uv_auth_token(env);
|
||||
self.pin_uv_auth_token_state.set_default_permissions();
|
||||
let pin_uv_auth_token = shared_secret.encrypt(
|
||||
env.rng(),
|
||||
@@ -358,9 +355,8 @@ impl ClientPin {
|
||||
|
||||
fn process_get_pin_uv_auth_token_using_pin_with_permissions(
|
||||
&mut self,
|
||||
env: &mut impl Env,
|
||||
env: &mut E,
|
||||
mut client_pin_params: AuthenticatorClientPinParameters,
|
||||
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.
|
||||
@@ -376,7 +372,7 @@ impl ClientPin {
|
||||
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
let response = self.process_get_pin_token(env, client_pin_params, now)?;
|
||||
let response = self.process_get_pin_token(env, client_pin_params)?;
|
||||
self.pin_uv_auth_token_state.set_permissions(permissions);
|
||||
self.pin_uv_auth_token_state
|
||||
.set_permissions_rp_id(permissions_rp_id);
|
||||
@@ -387,9 +383,8 @@ impl ClientPin {
|
||||
/// Processes the authenticatorClientPin command.
|
||||
pub fn process_command(
|
||||
&mut self,
|
||||
env: &mut impl Env,
|
||||
env: &mut E,
|
||||
client_pin_params: AuthenticatorClientPinParameters,
|
||||
now: CtapInstant,
|
||||
) -> Result<ResponseData, Ctap2StatusCode> {
|
||||
if !env.customization().allows_pin_protocol_v1()
|
||||
&& client_pin_params.pin_uv_auth_protocol == PinUvAuthProtocol::V1
|
||||
@@ -410,7 +405,7 @@ impl ClientPin {
|
||||
None
|
||||
}
|
||||
ClientPinSubCommand::GetPinToken => {
|
||||
Some(self.process_get_pin_token(env, client_pin_params, now)?)
|
||||
Some(self.process_get_pin_token(env, client_pin_params)?)
|
||||
}
|
||||
ClientPinSubCommand::GetPinUvAuthTokenUsingUvWithPermissions => Some(
|
||||
self.process_get_pin_uv_auth_token_using_uv_with_permissions(client_pin_params)?,
|
||||
@@ -420,7 +415,6 @@ impl ClientPin {
|
||||
self.process_get_pin_uv_auth_token_using_pin_with_permissions(
|
||||
env,
|
||||
client_pin_params,
|
||||
now,
|
||||
)?,
|
||||
),
|
||||
};
|
||||
@@ -447,11 +441,11 @@ impl ClientPin {
|
||||
}
|
||||
|
||||
/// Resets all held state.
|
||||
pub fn reset(&mut self, rng: &mut impl Rng256) {
|
||||
self.pin_protocol_v1.regenerate(rng);
|
||||
self.pin_protocol_v1.reset_pin_uv_auth_token(rng);
|
||||
self.pin_protocol_v2.regenerate(rng);
|
||||
self.pin_protocol_v2.reset_pin_uv_auth_token(rng);
|
||||
pub fn reset(&mut self, env: &mut E) {
|
||||
self.pin_protocol_v1.regenerate(env.rng());
|
||||
self.pin_protocol_v1.reset_pin_uv_auth_token(env.rng());
|
||||
self.pin_protocol_v2.regenerate(env.rng());
|
||||
self.pin_protocol_v2.reset_pin_uv_auth_token(env.rng());
|
||||
self.consecutive_pin_mismatches = 0;
|
||||
self.pin_uv_auth_token_state.stop_using_pin_uv_auth_token();
|
||||
}
|
||||
@@ -466,7 +460,7 @@ impl ClientPin {
|
||||
/// 32 byte.
|
||||
pub fn process_hmac_secret(
|
||||
&self,
|
||||
rng: &mut impl Rng256,
|
||||
env: &mut E,
|
||||
hmac_secret_input: GetAssertionHmacSecretInput,
|
||||
cred_random: &[u8; 32],
|
||||
) -> Result<Vec<u8>, Ctap2StatusCode> {
|
||||
@@ -490,7 +484,7 @@ impl ClientPin {
|
||||
let mut output2 = hmac_256::<Sha256>(cred_random, &decrypted_salts[32..]).to_vec();
|
||||
output.append(&mut output2);
|
||||
}
|
||||
shared_secret.encrypt(rng, &output)
|
||||
shared_secret.encrypt(env.rng(), &output)
|
||||
}
|
||||
|
||||
/// Consumes flags and permissions related to the pinUvAuthToken.
|
||||
@@ -501,9 +495,9 @@ impl ClientPin {
|
||||
}
|
||||
|
||||
/// Updates the running timers, triggers timeout events.
|
||||
pub fn update_timeouts(&mut self, now: CtapInstant) {
|
||||
pub fn update_timeouts(&mut self, env: &mut E) {
|
||||
self.pin_uv_auth_token_state
|
||||
.pin_uv_auth_token_usage_timer_observer(now);
|
||||
.pin_uv_auth_token_usage_timer_observer(env);
|
||||
}
|
||||
|
||||
/// Checks if user verification is cached for use of the pinUvAuthToken.
|
||||
@@ -563,19 +557,19 @@ impl ClientPin {
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn new_test(
|
||||
env: &mut E,
|
||||
key_agreement_key: crypto::ecdh::SecKey,
|
||||
pin_uv_auth_token: [u8; PIN_TOKEN_LENGTH],
|
||||
pin_uv_auth_protocol: PinUvAuthProtocol,
|
||||
) -> ClientPin {
|
||||
let mut env = crate::env::test::TestEnv::new();
|
||||
) -> Self {
|
||||
let (key_agreement_key_v1, key_agreement_key_v2) = match pin_uv_auth_protocol {
|
||||
PinUvAuthProtocol::V1 => (key_agreement_key, crypto::ecdh::SecKey::gensk(env.rng())),
|
||||
PinUvAuthProtocol::V2 => (crypto::ecdh::SecKey::gensk(env.rng()), key_agreement_key),
|
||||
};
|
||||
let mut pin_uv_auth_token_state = PinUvAuthTokenState::new();
|
||||
pin_uv_auth_token_state.set_permissions(0xFF);
|
||||
pin_uv_auth_token_state.begin_using_pin_uv_auth_token(CtapInstant::new(0));
|
||||
ClientPin {
|
||||
pin_uv_auth_token_state.begin_using_pin_uv_auth_token(env);
|
||||
Self {
|
||||
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),
|
||||
consecutive_pin_mismatches: 0,
|
||||
@@ -590,7 +584,6 @@ mod test {
|
||||
use super::*;
|
||||
use crate::env::test::TestEnv;
|
||||
use alloc::vec;
|
||||
use embedded_time::duration::Milliseconds;
|
||||
|
||||
/// Stores a PIN hash corresponding to the dummy PIN "1234".
|
||||
fn set_standard_pin(env: &mut TestEnv) {
|
||||
@@ -618,14 +611,18 @@ mod test {
|
||||
/// should fail.
|
||||
fn create_client_pin_and_shared_secret(
|
||||
pin_uv_auth_protocol: PinUvAuthProtocol,
|
||||
) -> (ClientPin, Box<dyn SharedSecret>) {
|
||||
) -> (ClientPin<TestEnv>, Box<dyn SharedSecret>) {
|
||||
let mut env = TestEnv::new();
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||
let pk = key_agreement_key.genpk();
|
||||
let key_agreement = CoseKey::from(pk);
|
||||
let pin_uv_auth_token = [0x91; PIN_TOKEN_LENGTH];
|
||||
let client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol);
|
||||
let client_pin = ClientPin::<TestEnv>::new_test(
|
||||
&mut env,
|
||||
key_agreement_key,
|
||||
pin_uv_auth_token,
|
||||
pin_uv_auth_protocol,
|
||||
);
|
||||
let shared_secret = client_pin
|
||||
.get_pin_protocol(pin_uv_auth_protocol)
|
||||
.decapsulate(key_agreement, pin_uv_auth_protocol)
|
||||
@@ -639,7 +636,7 @@ mod test {
|
||||
fn create_client_pin_and_parameters(
|
||||
pin_uv_auth_protocol: PinUvAuthProtocol,
|
||||
sub_command: ClientPinSubCommand,
|
||||
) -> (ClientPin, AuthenticatorClientPinParameters) {
|
||||
) -> (ClientPin<TestEnv>, AuthenticatorClientPinParameters) {
|
||||
let mut env = TestEnv::new();
|
||||
let (client_pin, shared_secret) = create_client_pin_and_shared_secret(pin_uv_auth_protocol);
|
||||
|
||||
@@ -683,7 +680,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_mix_pin_protocols() {
|
||||
let mut env = TestEnv::new();
|
||||
let client_pin = ClientPin::new(env.rng());
|
||||
let client_pin = ClientPin::<TestEnv>::new(&mut env);
|
||||
let pin_protocol_v1 = client_pin.get_pin_protocol(PinUvAuthProtocol::V1);
|
||||
let pin_protocol_v2 = client_pin.get_pin_protocol(PinUvAuthProtocol::V2);
|
||||
let message = vec![0xAA; 16];
|
||||
@@ -724,7 +721,7 @@ mod test {
|
||||
|
||||
fn test_helper_verify_pin_hash_enc(pin_uv_auth_protocol: PinUvAuthProtocol) {
|
||||
let mut env = TestEnv::new();
|
||||
let mut client_pin = ClientPin::new(env.rng());
|
||||
let mut client_pin = ClientPin::<TestEnv>::new(&mut env);
|
||||
let pin_protocol = client_pin.get_pin_protocol(pin_uv_auth_protocol);
|
||||
let shared_secret = pin_protocol
|
||||
.decapsulate(pin_protocol.get_public_key(), pin_uv_auth_protocol)
|
||||
@@ -823,7 +820,7 @@ mod test {
|
||||
power_cycle_state: Some(false),
|
||||
});
|
||||
assert_eq!(
|
||||
client_pin.process_command(&mut env, params.clone(), CtapInstant::new(0)),
|
||||
client_pin.process_command(&mut env, params.clone()),
|
||||
Ok(ResponseData::AuthenticatorClientPin(expected_response))
|
||||
);
|
||||
|
||||
@@ -835,7 +832,7 @@ mod test {
|
||||
power_cycle_state: Some(true),
|
||||
});
|
||||
assert_eq!(
|
||||
client_pin.process_command(&mut env, params, CtapInstant::new(0)),
|
||||
client_pin.process_command(&mut env, params),
|
||||
Ok(ResponseData::AuthenticatorClientPin(expected_response))
|
||||
);
|
||||
}
|
||||
@@ -863,7 +860,7 @@ mod test {
|
||||
power_cycle_state: None,
|
||||
});
|
||||
assert_eq!(
|
||||
client_pin.process_command(&mut env, params, CtapInstant::new(0)),
|
||||
client_pin.process_command(&mut env, params),
|
||||
Ok(ResponseData::AuthenticatorClientPin(expected_response))
|
||||
);
|
||||
}
|
||||
@@ -887,7 +884,7 @@ mod test {
|
||||
let mut env = TestEnv::new();
|
||||
env.customization_mut().set_allows_pin_protocol_v1(false);
|
||||
assert_eq!(
|
||||
client_pin.process_command(&mut env, params, CtapInstant::new(0)),
|
||||
client_pin.process_command(&mut env, params),
|
||||
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
||||
);
|
||||
}
|
||||
@@ -897,7 +894,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, CtapInstant::new(0)),
|
||||
client_pin.process_command(&mut env, params),
|
||||
Ok(ResponseData::AuthenticatorClientPin(None))
|
||||
);
|
||||
}
|
||||
@@ -930,14 +927,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(), CtapInstant::new(0)),
|
||||
client_pin.process_command(&mut env, params.clone()),
|
||||
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, CtapInstant::new(0)),
|
||||
client_pin.process_command(&mut env, bad_params),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
|
||||
);
|
||||
|
||||
@@ -945,7 +942,7 @@ mod test {
|
||||
storage::decr_pin_retries(&mut env).unwrap();
|
||||
}
|
||||
assert_eq!(
|
||||
client_pin.process_command(&mut env, params, CtapInstant::new(0)),
|
||||
client_pin.process_command(&mut env, params),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_BLOCKED)
|
||||
);
|
||||
}
|
||||
@@ -976,7 +973,7 @@ mod test {
|
||||
set_standard_pin(&mut env);
|
||||
|
||||
let response = client_pin
|
||||
.process_command(&mut env, params.clone(), CtapInstant::new(0))
|
||||
.process_command(&mut env, params.clone())
|
||||
.unwrap();
|
||||
let encrypted_token = match response {
|
||||
ResponseData::AuthenticatorClientPin(Some(response)) => {
|
||||
@@ -1012,7 +1009,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, CtapInstant::new(0)),
|
||||
client_pin.process_command(&mut env, bad_params),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)
|
||||
);
|
||||
}
|
||||
@@ -1037,7 +1034,7 @@ mod test {
|
||||
|
||||
assert_eq!(storage::force_pin_change(&mut env), Ok(()));
|
||||
assert_eq!(
|
||||
client_pin.process_command(&mut env, params, CtapInstant::new(0)),
|
||||
client_pin.process_command(&mut env, params),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID),
|
||||
);
|
||||
}
|
||||
@@ -1070,7 +1067,7 @@ mod test {
|
||||
set_standard_pin(&mut env);
|
||||
|
||||
let response = client_pin
|
||||
.process_command(&mut env, params.clone(), CtapInstant::new(0))
|
||||
.process_command(&mut env, params.clone())
|
||||
.unwrap();
|
||||
let encrypted_token = match response {
|
||||
ResponseData::AuthenticatorClientPin(Some(response)) => {
|
||||
@@ -1106,21 +1103,21 @@ mod test {
|
||||
let mut bad_params = params.clone();
|
||||
bad_params.permissions = Some(0x00);
|
||||
assert_eq!(
|
||||
client_pin.process_command(&mut env, bad_params, CtapInstant::new(0)),
|
||||
client_pin.process_command(&mut env, bad_params),
|
||||
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, CtapInstant::new(0)),
|
||||
client_pin.process_command(&mut env, bad_params),
|
||||
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, CtapInstant::new(0)),
|
||||
client_pin.process_command(&mut env, bad_params),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)
|
||||
);
|
||||
}
|
||||
@@ -1147,7 +1144,7 @@ mod test {
|
||||
|
||||
assert_eq!(storage::force_pin_change(&mut env), Ok(()));
|
||||
assert_eq!(
|
||||
client_pin.process_command(&mut env, params, CtapInstant::new(0)),
|
||||
client_pin.process_command(&mut env, params),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)
|
||||
);
|
||||
}
|
||||
@@ -1281,7 +1278,7 @@ mod test {
|
||||
salt_auth,
|
||||
pin_uv_auth_protocol,
|
||||
};
|
||||
let output = client_pin.process_hmac_secret(env.rng(), hmac_secret_input, cred_random);
|
||||
let output = client_pin.process_hmac_secret(&mut env, hmac_secret_input, cred_random);
|
||||
output.map(|v| shared_secret.as_ref().decrypt(&v).unwrap())
|
||||
}
|
||||
|
||||
@@ -1301,7 +1298,7 @@ mod test {
|
||||
salt_auth,
|
||||
pin_uv_auth_protocol,
|
||||
};
|
||||
let output = client_pin.process_hmac_secret(env.rng(), hmac_secret_input, &cred_random);
|
||||
let output = client_pin.process_hmac_secret(&mut env, hmac_secret_input, &cred_random);
|
||||
assert_eq!(output, Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID));
|
||||
}
|
||||
|
||||
@@ -1403,7 +1400,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_has_permission() {
|
||||
let mut env = TestEnv::new();
|
||||
let mut client_pin = ClientPin::new(env.rng());
|
||||
let mut client_pin = ClientPin::<TestEnv>::new(&mut env);
|
||||
client_pin.pin_uv_auth_token_state.set_permissions(0x7F);
|
||||
for permission in PinPermission::into_enum_iter() {
|
||||
assert_eq!(
|
||||
@@ -1427,7 +1424,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_has_no_rp_id_permission() {
|
||||
let mut env = TestEnv::new();
|
||||
let mut client_pin = ClientPin::new(env.rng());
|
||||
let mut client_pin = ClientPin::<TestEnv>::new(&mut env);
|
||||
assert_eq!(client_pin.has_no_rp_id_permission(), Ok(()));
|
||||
client_pin
|
||||
.pin_uv_auth_token_state
|
||||
@@ -1441,7 +1438,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_has_no_or_rp_id_permission() {
|
||||
let mut env = TestEnv::new();
|
||||
let mut client_pin = ClientPin::new(env.rng());
|
||||
let mut client_pin = ClientPin::<TestEnv>::new(&mut env);
|
||||
assert_eq!(client_pin.has_no_or_rp_id_permission("example.com"), Ok(()));
|
||||
client_pin
|
||||
.pin_uv_auth_token_state
|
||||
@@ -1456,7 +1453,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_has_no_or_rp_id_hash_permission() {
|
||||
let mut env = TestEnv::new();
|
||||
let mut client_pin = ClientPin::new(env.rng());
|
||||
let mut client_pin = ClientPin::<TestEnv>::new(&mut env);
|
||||
let rp_id_hash = Sha256::hash(b"example.com");
|
||||
assert_eq!(
|
||||
client_pin.has_no_or_rp_id_hash_permission(&rp_id_hash),
|
||||
@@ -1478,7 +1475,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_ensure_rp_id_permission() {
|
||||
let mut env = TestEnv::new();
|
||||
let mut client_pin = ClientPin::new(env.rng());
|
||||
let mut client_pin = ClientPin::<TestEnv>::new(&mut env);
|
||||
assert_eq!(client_pin.ensure_rp_id_permission("example.com"), Ok(()));
|
||||
assert_eq!(
|
||||
client_pin
|
||||
@@ -1496,11 +1493,11 @@ mod test {
|
||||
#[test]
|
||||
fn test_verify_pin_uv_auth_token() {
|
||||
let mut env = TestEnv::new();
|
||||
let mut client_pin = ClientPin::new(env.rng());
|
||||
let mut client_pin = ClientPin::<TestEnv>::new(&mut env);
|
||||
let message = [0xAA];
|
||||
client_pin
|
||||
.pin_uv_auth_token_state
|
||||
.begin_using_pin_uv_auth_token(CtapInstant::new(0));
|
||||
.begin_using_pin_uv_auth_token(&mut env);
|
||||
|
||||
let pin_uv_auth_token_v1 = client_pin
|
||||
.get_pin_protocol(PinUvAuthProtocol::V1)
|
||||
@@ -1570,7 +1567,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_verify_pin_uv_auth_token_not_in_use() {
|
||||
let mut env = TestEnv::new();
|
||||
let client_pin = ClientPin::new(env.rng());
|
||||
let client_pin = ClientPin::<TestEnv>::new(&mut env);
|
||||
let message = [0xAA];
|
||||
|
||||
let pin_uv_auth_token_v1 = client_pin
|
||||
@@ -1592,7 +1589,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_reset() {
|
||||
let mut env = TestEnv::new();
|
||||
let mut client_pin = ClientPin::new(env.rng());
|
||||
let mut client_pin = ClientPin::<TestEnv>::new(&mut env);
|
||||
let public_key_v1 = client_pin.pin_protocol_v1.get_public_key();
|
||||
let public_key_v2 = client_pin.pin_protocol_v2.get_public_key();
|
||||
let token_v1 = *client_pin.pin_protocol_v1.get_pin_uv_auth_token();
|
||||
@@ -1601,7 +1598,7 @@ mod test {
|
||||
client_pin
|
||||
.pin_uv_auth_token_state
|
||||
.set_permissions_rp_id(Some(String::from("example.com")));
|
||||
client_pin.reset(env.rng());
|
||||
client_pin.reset(&mut env);
|
||||
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!(
|
||||
@@ -1631,9 +1628,7 @@ mod test {
|
||||
set_standard_pin(&mut env);
|
||||
params.permissions = Some(0xFF);
|
||||
|
||||
assert!(client_pin
|
||||
.process_command(&mut env, params, CtapInstant::new(0))
|
||||
.is_ok());
|
||||
assert!(client_pin.process_command(&mut env, params).is_ok());
|
||||
for permission in PinPermission::into_enum_iter() {
|
||||
assert_eq!(
|
||||
client_pin
|
||||
@@ -1649,8 +1644,8 @@ mod test {
|
||||
Ok(())
|
||||
);
|
||||
|
||||
let timeout = CtapInstant::new(0) + Milliseconds::new(30001_u32);
|
||||
client_pin.update_timeouts(timeout);
|
||||
env.clock().advance(30001);
|
||||
client_pin.update_timeouts(&mut env);
|
||||
for permission in PinPermission::into_enum_iter() {
|
||||
assert_eq!(
|
||||
client_pin
|
||||
@@ -1677,9 +1672,7 @@ mod test {
|
||||
set_standard_pin(&mut env);
|
||||
params.permissions = Some(0xFF);
|
||||
|
||||
assert!(client_pin
|
||||
.process_command(&mut env, params, CtapInstant::new(0))
|
||||
.is_ok());
|
||||
assert!(client_pin.process_command(&mut env, params).is_ok());
|
||||
for permission in PinPermission::into_enum_iter() {
|
||||
assert_eq!(
|
||||
client_pin
|
||||
|
||||
@@ -73,9 +73,9 @@ fn process_set_min_pin_length(
|
||||
}
|
||||
|
||||
/// Processes the AuthenticatorConfig command.
|
||||
pub fn process_config(
|
||||
env: &mut impl Env,
|
||||
client_pin: &mut ClientPin,
|
||||
pub fn process_config<E: Env>(
|
||||
env: &mut E,
|
||||
client_pin: &mut ClientPin<E>,
|
||||
params: AuthenticatorConfigParameters,
|
||||
) -> Result<ResponseData, Ctap2StatusCode> {
|
||||
let AuthenticatorConfigParameters {
|
||||
@@ -133,8 +133,12 @@ mod test {
|
||||
let mut env = TestEnv::new();
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||
&mut env,
|
||||
key_agreement_key,
|
||||
pin_uv_auth_token,
|
||||
PinUvAuthProtocol::V1,
|
||||
);
|
||||
|
||||
let config_params = AuthenticatorConfigParameters {
|
||||
sub_command: ConfigSubCommand::EnableEnterpriseAttestation,
|
||||
@@ -160,8 +164,12 @@ mod test {
|
||||
let mut env = TestEnv::new();
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||
&mut env,
|
||||
key_agreement_key,
|
||||
pin_uv_auth_token,
|
||||
PinUvAuthProtocol::V1,
|
||||
);
|
||||
|
||||
let config_params = AuthenticatorConfigParameters {
|
||||
sub_command: ConfigSubCommand::ToggleAlwaysUv,
|
||||
@@ -195,8 +203,12 @@ mod test {
|
||||
let mut env = TestEnv::new();
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol);
|
||||
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||
&mut env,
|
||||
key_agreement_key,
|
||||
pin_uv_auth_token,
|
||||
pin_uv_auth_protocol,
|
||||
);
|
||||
storage::set_pin(&mut env, &[0x88; 16], 4).unwrap();
|
||||
|
||||
let mut config_data = vec![0xFF; 32];
|
||||
@@ -265,8 +277,12 @@ mod test {
|
||||
let mut env = TestEnv::new();
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||
&mut env,
|
||||
key_agreement_key,
|
||||
pin_uv_auth_token,
|
||||
PinUvAuthProtocol::V1,
|
||||
);
|
||||
|
||||
// First, increase minimum PIN length from 4 to 6 without PIN auth.
|
||||
let min_pin_length = 6;
|
||||
@@ -309,8 +325,12 @@ mod test {
|
||||
let mut env = TestEnv::new();
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||
&mut env,
|
||||
key_agreement_key,
|
||||
pin_uv_auth_token,
|
||||
PinUvAuthProtocol::V1,
|
||||
);
|
||||
|
||||
// First, set RP IDs without PIN auth.
|
||||
let min_pin_length = 6;
|
||||
@@ -385,8 +405,12 @@ mod test {
|
||||
let mut env = TestEnv::new();
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||
&mut env,
|
||||
key_agreement_key,
|
||||
pin_uv_auth_token,
|
||||
PinUvAuthProtocol::V1,
|
||||
);
|
||||
|
||||
storage::set_pin(&mut env, &[0x88; 16], 4).unwrap();
|
||||
// Increase min PIN, force PIN change.
|
||||
@@ -408,8 +432,12 @@ mod test {
|
||||
let mut env = TestEnv::new();
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||
&mut env,
|
||||
key_agreement_key,
|
||||
pin_uv_auth_token,
|
||||
PinUvAuthProtocol::V1,
|
||||
);
|
||||
|
||||
storage::set_pin(&mut env, &[0x88; 16], 4).unwrap();
|
||||
let pin_uv_auth_param = Some(vec![
|
||||
@@ -439,8 +467,12 @@ mod test {
|
||||
let mut env = TestEnv::new();
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||
&mut env,
|
||||
key_agreement_key,
|
||||
pin_uv_auth_token,
|
||||
PinUvAuthProtocol::V1,
|
||||
);
|
||||
|
||||
let config_params = AuthenticatorConfigParameters {
|
||||
sub_command: ConfigSubCommand::VendorPrototype,
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
// 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::{
|
||||
@@ -108,9 +107,9 @@ fn enumerate_credentials_response(
|
||||
/// Check if the token permissions have the correct associated RP ID.
|
||||
///
|
||||
/// Either no RP ID is associated, or the RP ID matches the stored credential.
|
||||
fn check_rp_id_permissions(
|
||||
env: &mut impl Env,
|
||||
client_pin: &mut ClientPin,
|
||||
fn check_rp_id_permissions<E: Env>(
|
||||
env: &mut E,
|
||||
client_pin: &mut ClientPin<E>,
|
||||
credential_id: &[u8],
|
||||
) -> Result<(), Ctap2StatusCode> {
|
||||
// Pre-check a sufficient condition before calling the store.
|
||||
@@ -135,17 +134,16 @@ fn process_get_creds_metadata(
|
||||
}
|
||||
|
||||
/// Processes the subcommand enumerateRPsBegin for CredentialManagement.
|
||||
fn process_enumerate_rps_begin(
|
||||
env: &mut impl Env,
|
||||
stateful_command_permission: &mut StatefulPermission,
|
||||
fn process_enumerate_rps_begin<E: Env>(
|
||||
env: &mut E,
|
||||
stateful_command_permission: &mut StatefulPermission<E>,
|
||||
channel: Channel,
|
||||
now: CtapInstant,
|
||||
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
|
||||
let rp_set = get_stored_rp_ids(env)?;
|
||||
let total_rps = rp_set.len();
|
||||
|
||||
if total_rps > 1 {
|
||||
stateful_command_permission.set_command(now, StatefulCommand::EnumerateRps(1), channel);
|
||||
stateful_command_permission.set_command(env, StatefulCommand::EnumerateRps(1), channel);
|
||||
}
|
||||
// TODO https://github.com/rust-lang/rust/issues/62924 replace with pop_first()
|
||||
let rp_id = rp_set
|
||||
@@ -156,9 +154,9 @@ fn process_enumerate_rps_begin(
|
||||
}
|
||||
|
||||
/// Processes the subcommand enumerateRPsGetNextRP for CredentialManagement.
|
||||
fn process_enumerate_rps_get_next_rp(
|
||||
env: &mut impl Env,
|
||||
stateful_command_permission: &mut StatefulPermission,
|
||||
fn process_enumerate_rps_get_next_rp<E: Env>(
|
||||
env: &mut E,
|
||||
stateful_command_permission: &mut StatefulPermission<E>,
|
||||
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
|
||||
let rp_id_index = stateful_command_permission.next_enumerate_rp()?;
|
||||
let rp_set = get_stored_rp_ids(env)?;
|
||||
@@ -171,13 +169,12 @@ fn process_enumerate_rps_get_next_rp(
|
||||
}
|
||||
|
||||
/// Processes the subcommand enumerateCredentialsBegin for CredentialManagement.
|
||||
fn process_enumerate_credentials_begin(
|
||||
env: &mut impl Env,
|
||||
stateful_command_permission: &mut StatefulPermission,
|
||||
client_pin: &mut ClientPin,
|
||||
fn process_enumerate_credentials_begin<E: Env>(
|
||||
env: &mut E,
|
||||
stateful_command_permission: &mut StatefulPermission<E>,
|
||||
client_pin: &mut ClientPin<E>,
|
||||
sub_command_params: CredentialManagementSubCommandParameters,
|
||||
channel: Channel,
|
||||
now: CtapInstant,
|
||||
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
|
||||
let rp_id_hash = sub_command_params
|
||||
.rp_id_hash
|
||||
@@ -203,7 +200,7 @@ fn process_enumerate_credentials_begin(
|
||||
let credential = storage::get_credential(env, current_key)?;
|
||||
if total_credentials > 1 {
|
||||
stateful_command_permission.set_command(
|
||||
now,
|
||||
env,
|
||||
StatefulCommand::EnumerateCredentials(rp_credentials),
|
||||
channel,
|
||||
);
|
||||
@@ -212,9 +209,9 @@ fn process_enumerate_credentials_begin(
|
||||
}
|
||||
|
||||
/// Processes the subcommand enumerateCredentialsGetNextCredential for CredentialManagement.
|
||||
fn process_enumerate_credentials_get_next_credential(
|
||||
env: &mut impl Env,
|
||||
stateful_command_permission: &mut StatefulPermission,
|
||||
fn process_enumerate_credentials_get_next_credential<E: Env>(
|
||||
env: &mut E,
|
||||
stateful_command_permission: &mut StatefulPermission<E>,
|
||||
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
|
||||
let credential_key = stateful_command_permission.next_enumerate_credential()?;
|
||||
let credential = storage::get_credential(env, credential_key)?;
|
||||
@@ -222,9 +219,9 @@ fn process_enumerate_credentials_get_next_credential(
|
||||
}
|
||||
|
||||
/// Processes the subcommand deleteCredential for CredentialManagement.
|
||||
fn process_delete_credential(
|
||||
env: &mut impl Env,
|
||||
client_pin: &mut ClientPin,
|
||||
fn process_delete_credential<E: Env>(
|
||||
env: &mut E,
|
||||
client_pin: &mut ClientPin<E>,
|
||||
sub_command_params: CredentialManagementSubCommandParameters,
|
||||
) -> Result<(), Ctap2StatusCode> {
|
||||
let credential_id = sub_command_params
|
||||
@@ -236,9 +233,9 @@ fn process_delete_credential(
|
||||
}
|
||||
|
||||
/// Processes the subcommand updateUserInformation for CredentialManagement.
|
||||
fn process_update_user_information(
|
||||
env: &mut impl Env,
|
||||
client_pin: &mut ClientPin,
|
||||
fn process_update_user_information<E: Env>(
|
||||
env: &mut E,
|
||||
client_pin: &mut ClientPin<E>,
|
||||
sub_command_params: CredentialManagementSubCommandParameters,
|
||||
) -> Result<(), Ctap2StatusCode> {
|
||||
let credential_id = sub_command_params
|
||||
@@ -253,13 +250,12 @@ fn process_update_user_information(
|
||||
}
|
||||
|
||||
/// Processes the CredentialManagement command and all its subcommands.
|
||||
pub fn process_credential_management(
|
||||
env: &mut impl Env,
|
||||
stateful_command_permission: &mut StatefulPermission,
|
||||
client_pin: &mut ClientPin,
|
||||
pub fn process_credential_management<E: Env>(
|
||||
env: &mut E,
|
||||
stateful_command_permission: &mut StatefulPermission<E>,
|
||||
client_pin: &mut ClientPin<E>,
|
||||
cred_management_params: AuthenticatorCredentialManagementParameters,
|
||||
channel: Channel,
|
||||
now: CtapInstant,
|
||||
) -> Result<ResponseData, Ctap2StatusCode> {
|
||||
let AuthenticatorCredentialManagementParameters {
|
||||
sub_command,
|
||||
@@ -319,7 +315,6 @@ pub fn process_credential_management(
|
||||
env,
|
||||
stateful_command_permission,
|
||||
channel,
|
||||
now,
|
||||
)?)
|
||||
}
|
||||
CredentialManagementSubCommand::EnumerateRpsGetNextRp => Some(
|
||||
@@ -332,7 +327,6 @@ pub fn process_credential_management(
|
||||
client_pin,
|
||||
sub_command_params.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
||||
channel,
|
||||
now,
|
||||
)?)
|
||||
}
|
||||
CredentialManagementSubCommand::EnumerateCredentialsGetNextCredential => Some(
|
||||
@@ -392,11 +386,15 @@ mod test {
|
||||
let mut env = TestEnv::new();
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol);
|
||||
let client_pin = ClientPin::<TestEnv>::new_test(
|
||||
&mut env,
|
||||
key_agreement_key,
|
||||
pin_uv_auth_token,
|
||||
pin_uv_auth_protocol,
|
||||
);
|
||||
let credential_source = create_credential_source(&mut env);
|
||||
|
||||
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
ctap_state.client_pin = client_pin;
|
||||
|
||||
storage::set_pin(&mut env, &[0u8; 16], 4).unwrap();
|
||||
@@ -419,7 +417,6 @@ mod test {
|
||||
&mut ctap_state.client_pin,
|
||||
cred_management_params,
|
||||
DUMMY_CHANNEL,
|
||||
CtapInstant::new(0),
|
||||
);
|
||||
let initial_capacity = match cred_management_response.unwrap() {
|
||||
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
|
||||
@@ -445,7 +442,6 @@ mod test {
|
||||
&mut ctap_state.client_pin,
|
||||
cred_management_params,
|
||||
DUMMY_CHANNEL,
|
||||
CtapInstant::new(0),
|
||||
);
|
||||
match cred_management_response.unwrap() {
|
||||
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
|
||||
@@ -474,13 +470,17 @@ mod test {
|
||||
let mut env = TestEnv::new();
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let client_pin = ClientPin::<TestEnv>::new_test(
|
||||
&mut env,
|
||||
key_agreement_key,
|
||||
pin_uv_auth_token,
|
||||
PinUvAuthProtocol::V1,
|
||||
);
|
||||
let credential_source1 = create_credential_source(&mut env);
|
||||
let mut credential_source2 = create_credential_source(&mut env);
|
||||
credential_source2.rp_id = "another.example.com".to_string();
|
||||
|
||||
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
ctap_state.client_pin = client_pin;
|
||||
|
||||
storage::store_credential(&mut env, credential_source1).unwrap();
|
||||
@@ -504,7 +504,6 @@ mod test {
|
||||
&mut ctap_state.client_pin,
|
||||
cred_management_params,
|
||||
DUMMY_CHANNEL,
|
||||
CtapInstant::new(0),
|
||||
);
|
||||
let first_rp_id = match cred_management_response.unwrap() {
|
||||
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
|
||||
@@ -529,7 +528,6 @@ mod test {
|
||||
&mut ctap_state.client_pin,
|
||||
cred_management_params,
|
||||
DUMMY_CHANNEL,
|
||||
CtapInstant::new(0),
|
||||
);
|
||||
let second_rp_id = match cred_management_response.unwrap() {
|
||||
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
|
||||
@@ -555,7 +553,6 @@ mod test {
|
||||
&mut ctap_state.client_pin,
|
||||
cred_management_params,
|
||||
DUMMY_CHANNEL,
|
||||
CtapInstant::new(0),
|
||||
);
|
||||
assert_eq!(
|
||||
cred_management_response,
|
||||
@@ -568,11 +565,15 @@ mod test {
|
||||
let mut env = TestEnv::new();
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let client_pin = ClientPin::<TestEnv>::new_test(
|
||||
&mut env,
|
||||
key_agreement_key,
|
||||
pin_uv_auth_token,
|
||||
PinUvAuthProtocol::V1,
|
||||
);
|
||||
let credential_source = create_credential_source(&mut env);
|
||||
|
||||
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
ctap_state.client_pin = client_pin;
|
||||
|
||||
const NUM_CREDENTIALS: usize = 20;
|
||||
@@ -605,7 +606,6 @@ mod test {
|
||||
&mut ctap_state.client_pin,
|
||||
cred_management_params,
|
||||
DUMMY_CHANNEL,
|
||||
CtapInstant::new(0),
|
||||
);
|
||||
match cred_management_response.unwrap() {
|
||||
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
|
||||
@@ -636,7 +636,6 @@ mod test {
|
||||
&mut ctap_state.client_pin,
|
||||
cred_management_params,
|
||||
DUMMY_CHANNEL,
|
||||
CtapInstant::new(0),
|
||||
);
|
||||
assert_eq!(
|
||||
cred_management_response,
|
||||
@@ -649,8 +648,12 @@ mod test {
|
||||
let mut env = TestEnv::new();
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let client_pin = ClientPin::<TestEnv>::new_test(
|
||||
&mut env,
|
||||
key_agreement_key,
|
||||
pin_uv_auth_token,
|
||||
PinUvAuthProtocol::V1,
|
||||
);
|
||||
let credential_source1 = create_credential_source(&mut env);
|
||||
let mut credential_source2 = create_credential_source(&mut env);
|
||||
credential_source2.user_handle = vec![0x02];
|
||||
@@ -658,7 +661,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, CtapInstant::new(0));
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
ctap_state.client_pin = client_pin;
|
||||
|
||||
storage::store_credential(&mut env, credential_source1).unwrap();
|
||||
@@ -689,7 +692,6 @@ mod test {
|
||||
&mut ctap_state.client_pin,
|
||||
cred_management_params,
|
||||
DUMMY_CHANNEL,
|
||||
CtapInstant::new(0),
|
||||
);
|
||||
let first_credential_id = match cred_management_response.unwrap() {
|
||||
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
|
||||
@@ -713,7 +715,6 @@ mod test {
|
||||
&mut ctap_state.client_pin,
|
||||
cred_management_params,
|
||||
DUMMY_CHANNEL,
|
||||
CtapInstant::new(0),
|
||||
);
|
||||
let second_credential_id = match cred_management_response.unwrap() {
|
||||
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
|
||||
@@ -738,7 +739,6 @@ mod test {
|
||||
&mut ctap_state.client_pin,
|
||||
cred_management_params,
|
||||
DUMMY_CHANNEL,
|
||||
CtapInstant::new(0),
|
||||
);
|
||||
assert_eq!(
|
||||
cred_management_response,
|
||||
@@ -751,12 +751,16 @@ mod test {
|
||||
let mut env = TestEnv::new();
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let client_pin = ClientPin::<TestEnv>::new_test(
|
||||
&mut env,
|
||||
key_agreement_key,
|
||||
pin_uv_auth_token,
|
||||
PinUvAuthProtocol::V1,
|
||||
);
|
||||
let mut credential_source = create_credential_source(&mut env);
|
||||
credential_source.credential_id = vec![0x1D; 32];
|
||||
|
||||
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
ctap_state.client_pin = client_pin;
|
||||
|
||||
storage::store_credential(&mut env, credential_source).unwrap();
|
||||
@@ -789,7 +793,6 @@ mod test {
|
||||
&mut ctap_state.client_pin,
|
||||
cred_management_params,
|
||||
DUMMY_CHANNEL,
|
||||
CtapInstant::new(0),
|
||||
);
|
||||
assert_eq!(
|
||||
cred_management_response,
|
||||
@@ -808,7 +811,6 @@ mod test {
|
||||
&mut ctap_state.client_pin,
|
||||
cred_management_params,
|
||||
DUMMY_CHANNEL,
|
||||
CtapInstant::new(0),
|
||||
);
|
||||
assert_eq!(
|
||||
cred_management_response,
|
||||
@@ -821,12 +823,16 @@ mod test {
|
||||
let mut env = TestEnv::new();
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let client_pin = ClientPin::<TestEnv>::new_test(
|
||||
&mut env,
|
||||
key_agreement_key,
|
||||
pin_uv_auth_token,
|
||||
PinUvAuthProtocol::V1,
|
||||
);
|
||||
let mut credential_source = create_credential_source(&mut env);
|
||||
credential_source.credential_id = vec![0x1D; 32];
|
||||
|
||||
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
ctap_state.client_pin = client_pin;
|
||||
|
||||
storage::store_credential(&mut env, credential_source).unwrap();
|
||||
@@ -865,7 +871,6 @@ mod test {
|
||||
&mut ctap_state.client_pin,
|
||||
cred_management_params,
|
||||
DUMMY_CHANNEL,
|
||||
CtapInstant::new(0),
|
||||
);
|
||||
assert_eq!(
|
||||
cred_management_response,
|
||||
@@ -887,7 +892,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, CtapInstant::new(0));
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
|
||||
storage::set_pin(&mut env, &[0u8; 16], 4).unwrap();
|
||||
|
||||
@@ -903,7 +908,6 @@ mod test {
|
||||
&mut ctap_state.client_pin,
|
||||
cred_management_params,
|
||||
DUMMY_CHANNEL,
|
||||
CtapInstant::new(0),
|
||||
);
|
||||
assert_eq!(
|
||||
cred_management_response,
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
// 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::credential_id::{decrypt_credential_id, encrypt_to_credential_id};
|
||||
use super::crypto_wrapper::PrivateKey;
|
||||
@@ -174,11 +173,10 @@ impl Ctap1Command {
|
||||
const VENDOR_SPECIFIC_FIRST: u8 = 0x40;
|
||||
const VENDOR_SPECIFIC_LAST: u8 = 0xBF;
|
||||
|
||||
pub fn process_command(
|
||||
env: &mut impl Env,
|
||||
pub fn process_command<E: Env>(
|
||||
env: &mut E,
|
||||
message: &[u8],
|
||||
ctap_state: &mut CtapState,
|
||||
clock_value: CtapInstant,
|
||||
ctap_state: &mut CtapState<E>,
|
||||
) -> Result<Vec<u8>, Ctap1StatusCode> {
|
||||
if !ctap_state
|
||||
.allows_ctap1(env)
|
||||
@@ -192,7 +190,7 @@ impl Ctap1Command {
|
||||
challenge,
|
||||
application,
|
||||
} => {
|
||||
if !ctap_state.u2f_up_state.consume_up(clock_value) {
|
||||
if !ctap_state.u2f_up_state.consume_up(env) {
|
||||
return Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED);
|
||||
}
|
||||
Ctap1Command::process_register(env, challenge, application)
|
||||
@@ -205,8 +203,7 @@ impl Ctap1Command {
|
||||
flags,
|
||||
} => {
|
||||
// The order is important due to side effects of checking user presence.
|
||||
if flags == Ctap1Flags::EnforceUpAndSign
|
||||
&& !ctap_state.u2f_up_state.consume_up(clock_value)
|
||||
if flags == Ctap1Flags::EnforceUpAndSign && !ctap_state.u2f_up_state.consume_up(env)
|
||||
{
|
||||
return Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED);
|
||||
}
|
||||
@@ -301,13 +298,13 @@ impl Ctap1Command {
|
||||
// +-------------------+---------+--------------+-----------------+
|
||||
// + application (32B) | UP (1B) | Counter (4B) | challenge (32B) |
|
||||
// +-------------------+---------+--------------+-----------------+
|
||||
fn process_authenticate(
|
||||
env: &mut impl Env,
|
||||
fn process_authenticate<E: Env>(
|
||||
env: &mut E,
|
||||
challenge: [u8; 32],
|
||||
application: [u8; 32],
|
||||
key_handle: Vec<u8>,
|
||||
flags: Ctap1Flags,
|
||||
ctap_state: &mut CtapState,
|
||||
ctap_state: &mut CtapState<E>,
|
||||
) -> Result<Vec<u8>, Ctap1StatusCode> {
|
||||
let credential_source = decrypt_credential_id(env, key_handle, &application)
|
||||
.map_err(|_| Ctap1StatusCode::SW_WRONG_DATA)?;
|
||||
@@ -345,9 +342,9 @@ impl Ctap1Command {
|
||||
mod test {
|
||||
use super::super::credential_id::CBOR_CREDENTIAL_ID_SIZE;
|
||||
use super::super::data_formats::SignatureAlgorithm;
|
||||
use super::super::TOUCH_TIMEOUT_MS;
|
||||
use super::*;
|
||||
use crate::api::customization::Customization;
|
||||
use crate::clock::TEST_CLOCK_FREQUENCY_HZ;
|
||||
use crate::ctap::storage;
|
||||
use crate::env::test::TestEnv;
|
||||
use crypto::Hash256;
|
||||
@@ -394,15 +391,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, CtapInstant::new(0));
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
storage::toggle_always_uv(&mut env).unwrap();
|
||||
|
||||
let application = [0x0A; 32];
|
||||
let message = create_register_message(&application);
|
||||
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));
|
||||
ctap_state.u2f_up_state.consume_up(&mut env);
|
||||
ctap_state.u2f_up_state.grant_up(&mut env);
|
||||
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||
assert_eq!(response, Err(Ctap1StatusCode::SW_COMMAND_NOT_ALLOWED));
|
||||
}
|
||||
|
||||
@@ -411,14 +407,13 @@ 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, CtapInstant::new(0));
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
|
||||
let application = [0x0A; 32];
|
||||
let message = create_register_message(&application);
|
||||
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));
|
||||
ctap_state.u2f_up_state.consume_up(&mut env);
|
||||
ctap_state.u2f_up_state.grant_up(&mut env);
|
||||
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||
// Certificate and private key are missing
|
||||
assert_eq!(response, Err(Ctap1StatusCode::SW_INTERNAL_EXCEPTION));
|
||||
|
||||
@@ -429,11 +424,9 @@ mod test {
|
||||
env.attestation_store()
|
||||
.set(&attestation_store::Id::Batch, Some(&attestation))
|
||||
.unwrap();
|
||||
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))
|
||||
.unwrap();
|
||||
ctap_state.u2f_up_state.consume_up(&mut env);
|
||||
ctap_state.u2f_up_state.grant_up(&mut env);
|
||||
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state).unwrap();
|
||||
assert_eq!(response[0], Ctap1Command::LEGACY_BYTE);
|
||||
assert_eq!(response[66], CBOR_CREDENTIAL_ID_SIZE as u8);
|
||||
assert!(decrypt_credential_id(
|
||||
@@ -455,16 +448,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, CtapInstant::new(0));
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
|
||||
let application = [0x0A; 32];
|
||||
let message = create_register_message(&application);
|
||||
let response = Ctap1Command::process_command(
|
||||
&mut env,
|
||||
&message[..message.len() - 1],
|
||||
&mut ctap_state,
|
||||
CtapInstant::new(0),
|
||||
);
|
||||
let response =
|
||||
Ctap1Command::process_command(&mut env, &message[..message.len() - 1], &mut ctap_state);
|
||||
|
||||
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_LENGTH));
|
||||
}
|
||||
@@ -477,14 +466,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, CtapInstant::new(0));
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
|
||||
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);
|
||||
ctap_state.u2f_up_state.consume_up(&mut env);
|
||||
ctap_state.u2f_up_state.grant_up(&mut env);
|
||||
env.clock().advance(TOUCH_TIMEOUT_MS);
|
||||
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||
assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED));
|
||||
}
|
||||
|
||||
@@ -494,15 +481,14 @@ mod test {
|
||||
env.user_presence()
|
||||
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
||||
let sk = PrivateKey::new(&mut env, SignatureAlgorithm::Es256);
|
||||
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
|
||||
let rp_id = "example.com";
|
||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||
let key_handle = encrypt_to_credential_id(&mut env, &sk, &application, None, None).unwrap();
|
||||
let message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
||||
|
||||
let response =
|
||||
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
|
||||
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||
assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED));
|
||||
}
|
||||
|
||||
@@ -512,7 +498,7 @@ mod test {
|
||||
env.user_presence()
|
||||
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
||||
let sk = PrivateKey::new(&mut env, SignatureAlgorithm::Es256);
|
||||
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
|
||||
let rp_id = "example.com";
|
||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||
@@ -520,8 +506,7 @@ mod test {
|
||||
let application = [0x55; 32];
|
||||
let message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
||||
|
||||
let response =
|
||||
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
|
||||
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_DATA));
|
||||
}
|
||||
|
||||
@@ -531,7 +516,7 @@ mod test {
|
||||
env.user_presence()
|
||||
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
||||
let sk = PrivateKey::new(&mut env, SignatureAlgorithm::Es256);
|
||||
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
|
||||
let rp_id = "example.com";
|
||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||
@@ -543,23 +528,19 @@ mod test {
|
||||
);
|
||||
|
||||
message.push(0x00);
|
||||
let response =
|
||||
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
|
||||
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||
assert!(response.is_ok());
|
||||
|
||||
message.push(0x00);
|
||||
let response =
|
||||
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
|
||||
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||
assert!(response.is_ok());
|
||||
|
||||
message.push(0x00);
|
||||
let response =
|
||||
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
|
||||
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||
assert!(response.is_ok());
|
||||
|
||||
message.push(0x00);
|
||||
let response =
|
||||
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
|
||||
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_LENGTH));
|
||||
}
|
||||
|
||||
@@ -569,7 +550,7 @@ mod test {
|
||||
env.user_presence()
|
||||
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
||||
let sk = PrivateKey::new(&mut env, SignatureAlgorithm::Es256);
|
||||
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
|
||||
let rp_id = "example.com";
|
||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||
@@ -578,8 +559,7 @@ mod test {
|
||||
create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
||||
message[0] = 0xEE;
|
||||
|
||||
let response =
|
||||
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
|
||||
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||
assert_eq!(response, Err(Ctap1StatusCode::SW_CLA_INVALID));
|
||||
}
|
||||
|
||||
@@ -589,7 +569,7 @@ mod test {
|
||||
env.user_presence()
|
||||
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
||||
let sk = PrivateKey::new(&mut env, SignatureAlgorithm::Es256);
|
||||
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
|
||||
let rp_id = "example.com";
|
||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||
@@ -598,8 +578,7 @@ mod test {
|
||||
create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
||||
message[1] = 0xEE;
|
||||
|
||||
let response =
|
||||
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
|
||||
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||
assert_eq!(response, Err(Ctap1StatusCode::SW_INS_INVALID));
|
||||
}
|
||||
|
||||
@@ -609,7 +588,7 @@ mod test {
|
||||
env.user_presence()
|
||||
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
||||
let sk = PrivateKey::new(&mut env, SignatureAlgorithm::Es256);
|
||||
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
|
||||
let rp_id = "example.com";
|
||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||
@@ -618,8 +597,7 @@ mod test {
|
||||
create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
||||
message[2] = 0xEE;
|
||||
|
||||
let response =
|
||||
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
|
||||
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_DATA));
|
||||
}
|
||||
|
||||
@@ -637,7 +615,7 @@ mod test {
|
||||
env.user_presence()
|
||||
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
||||
let sk = PrivateKey::new(&mut env, SignatureAlgorithm::Es256);
|
||||
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
|
||||
let rp_id = "example.com";
|
||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||
@@ -645,11 +623,9 @@ mod test {
|
||||
let message =
|
||||
create_authenticate_message(&application, Ctap1Flags::EnforceUpAndSign, &key_handle);
|
||||
|
||||
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))
|
||||
.unwrap();
|
||||
ctap_state.u2f_up_state.consume_up(&mut env);
|
||||
ctap_state.u2f_up_state.grant_up(&mut env);
|
||||
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state).unwrap();
|
||||
assert_eq!(response[0], 0x01);
|
||||
let global_signature_counter = storage::global_signature_counter(&mut env).unwrap();
|
||||
check_signature_counter(
|
||||
@@ -665,7 +641,7 @@ mod test {
|
||||
env.user_presence()
|
||||
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
||||
let sk = PrivateKey::new(&mut env, SignatureAlgorithm::Es256);
|
||||
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
|
||||
let rp_id = "example.com";
|
||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||
@@ -676,13 +652,8 @@ mod test {
|
||||
&key_handle,
|
||||
);
|
||||
|
||||
let response = Ctap1Command::process_command(
|
||||
&mut env,
|
||||
&message,
|
||||
&mut ctap_state,
|
||||
CtapInstant::new((30001 * TEST_CLOCK_FREQUENCY_HZ as u64) / 1000),
|
||||
)
|
||||
.unwrap();
|
||||
env.clock().advance(TOUCH_TIMEOUT_MS);
|
||||
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state).unwrap();
|
||||
assert_eq!(response[0], 0x01);
|
||||
let global_signature_counter = storage::global_signature_counter(&mut env).unwrap();
|
||||
check_signature_counter(
|
||||
@@ -702,12 +673,11 @@ 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, CtapInstant::new(0));
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
|
||||
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));
|
||||
ctap_state.u2f_up_state.consume_up(&mut env);
|
||||
ctap_state.u2f_up_state.grant_up(&mut env);
|
||||
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_DATA));
|
||||
}
|
||||
|
||||
@@ -721,16 +691,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, CtapInstant::new(0));
|
||||
let mut ctap_state = CtapState::new(&mut env);
|
||||
|
||||
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),
|
||||
);
|
||||
ctap_state.u2f_up_state.consume_up(&mut env);
|
||||
ctap_state.u2f_up_state.grant_up(&mut env);
|
||||
env.clock().advance(TOUCH_TIMEOUT_MS);
|
||||
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||
assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,16 +21,29 @@ pub use self::receive::MessageAssembler;
|
||||
#[cfg(not(feature = "std"))]
|
||||
use self::receive::MessageAssembler;
|
||||
pub use self::send::HidPacketIterator;
|
||||
use super::super::clock::{ClockInt, CtapInstant};
|
||||
use super::status_code::Ctap2StatusCode;
|
||||
#[cfg(test)]
|
||||
use crate::env::test::TestEnv;
|
||||
use crate::env::Env;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use arrayref::{array_ref, array_refs};
|
||||
use embedded_time::duration::Milliseconds;
|
||||
#[cfg(test)]
|
||||
use enum_iterator::IntoEnumIterator;
|
||||
|
||||
// We implement CTAP 2.1 from 2021-06-15. Please see section
|
||||
// 11.2. USB Human Interface Device (USB HID)
|
||||
const CHANNEL_RESERVED: ChannelID = [0, 0, 0, 0];
|
||||
const CHANNEL_BROADCAST: ChannelID = [0xFF, 0xFF, 0xFF, 0xFF];
|
||||
const PACKET_TYPE_MASK: u8 = 0x80;
|
||||
|
||||
// See section 11.2.9.1.3. CTAPHID_INIT (0x06).
|
||||
const PROTOCOL_VERSION: u8 = 2;
|
||||
// The device version number is vendor-defined.
|
||||
const DEVICE_VERSION_MAJOR: u8 = 1;
|
||||
const DEVICE_VERSION_MINOR: u8 = 0;
|
||||
const DEVICE_VERSION_BUILD: u8 = 0;
|
||||
|
||||
pub type HidPacket = [u8; 64];
|
||||
pub type ChannelID = [u8; 4];
|
||||
|
||||
@@ -164,8 +177,8 @@ pub enum KeepaliveStatus {
|
||||
/// 2. If you didn't receive any message or preprocessing discarded it, stop.
|
||||
/// 3. Handles all CTAP protocol interactions.
|
||||
/// 4. `split_message` creates packets out of the response message.
|
||||
pub struct CtapHid {
|
||||
assembler: MessageAssembler,
|
||||
pub struct CtapHid<E: Env> {
|
||||
assembler: MessageAssembler<E>,
|
||||
// The specification only requires unique CIDs, the allocation algorithm is vendor specific.
|
||||
// We allocate them incrementally, that is all `cid` such that 1 <= cid <= allocated_cids are
|
||||
// allocated.
|
||||
@@ -176,34 +189,17 @@ pub struct CtapHid {
|
||||
capabilities: u8,
|
||||
}
|
||||
|
||||
impl CtapHid {
|
||||
// We implement CTAP 2.1 from 2021-06-15. Please see section
|
||||
// 11.2. USB Human Interface Device (USB HID)
|
||||
const CHANNEL_RESERVED: ChannelID = [0, 0, 0, 0];
|
||||
const CHANNEL_BROADCAST: ChannelID = [0xFF, 0xFF, 0xFF, 0xFF];
|
||||
const TYPE_INIT_BIT: u8 = 0x80;
|
||||
const PACKET_TYPE_MASK: u8 = 0x80;
|
||||
|
||||
// See section 11.2.9.1.3. CTAPHID_INIT (0x06).
|
||||
const PROTOCOL_VERSION: u8 = 2;
|
||||
// The device version number is vendor-defined.
|
||||
const DEVICE_VERSION_MAJOR: u8 = 1;
|
||||
const DEVICE_VERSION_MINOR: u8 = 0;
|
||||
const DEVICE_VERSION_BUILD: u8 = 0;
|
||||
|
||||
impl<E: Env> CtapHid<E> {
|
||||
pub const CAPABILITY_WINK: u8 = 0x01;
|
||||
pub const CAPABILITY_CBOR: u8 = 0x04;
|
||||
#[cfg(any(not(feature = "with_ctap1"), feature = "vendor_hid"))]
|
||||
pub const CAPABILITY_NMSG: u8 = 0x08;
|
||||
|
||||
// TODO: Is this timeout duration specified?
|
||||
const TIMEOUT_DURATION: Milliseconds<ClockInt> = Milliseconds(100 as ClockInt);
|
||||
|
||||
/// Creates a new CTAP HID packet parser.
|
||||
///
|
||||
/// The capabilities passed in are reported to the client in Init.
|
||||
pub fn new(capabilities: u8) -> CtapHid {
|
||||
CtapHid {
|
||||
pub fn new(capabilities: u8) -> CtapHid<E> {
|
||||
Self {
|
||||
assembler: MessageAssembler::new(),
|
||||
allocated_cids: 0,
|
||||
capabilities,
|
||||
@@ -228,14 +224,9 @@ impl CtapHid {
|
||||
/// You may ignore PING, it's behaving correctly by default (input == output).
|
||||
/// Ignoring the others is incorrect behavior. You have to at least replace them with an error
|
||||
/// message:
|
||||
/// `CtapHid::error_message(message.cid, CtapHid::ERR_INVALID_CMD)`
|
||||
pub fn parse_packet(
|
||||
&mut self,
|
||||
env: &mut impl Env,
|
||||
packet: &HidPacket,
|
||||
clock_value: CtapInstant,
|
||||
) -> Option<Message> {
|
||||
match self.assembler.parse_packet(env, packet, clock_value) {
|
||||
/// `Self::error_message(message.cid, CtapHidError::InvalidCmd)`
|
||||
pub fn parse_packet(&mut self, env: &mut E, packet: &HidPacket) -> Option<Message> {
|
||||
match self.assembler.parse_packet(env, packet) {
|
||||
Ok(Some(message)) => {
|
||||
debug_ctap!(env, "Received message: {:02x?}", message);
|
||||
self.preprocess_message(message)
|
||||
@@ -248,9 +239,9 @@ impl CtapHid {
|
||||
if matches!(error, CtapHidError::UnexpectedContinuation) {
|
||||
None
|
||||
} else if !self.is_allocated_channel(cid) {
|
||||
Some(CtapHid::error_message(cid, CtapHidError::InvalidChannel))
|
||||
Some(Self::error_message(cid, CtapHidError::InvalidChannel))
|
||||
} else {
|
||||
Some(CtapHid::error_message(cid, error))
|
||||
Some(Self::error_message(cid, error))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -267,7 +258,7 @@ impl CtapHid {
|
||||
fn preprocess_message(&mut self, message: Message) -> Option<Message> {
|
||||
let cid = message.cid;
|
||||
if !self.has_valid_channel(&message) {
|
||||
return Some(CtapHid::error_message(cid, CtapHidError::InvalidChannel));
|
||||
return Some(Self::error_message(cid, CtapHidError::InvalidChannel));
|
||||
}
|
||||
|
||||
match message.cmd {
|
||||
@@ -276,10 +267,10 @@ impl CtapHid {
|
||||
// CTAP 2.1 from 2021-06-15, section 11.2.9.1.3.
|
||||
CtapHidCommand::Init => {
|
||||
if message.payload.len() != 8 {
|
||||
return Some(CtapHid::error_message(cid, CtapHidError::InvalidLen));
|
||||
return Some(Self::error_message(cid, CtapHidError::InvalidLen));
|
||||
}
|
||||
|
||||
let new_cid = if cid == CtapHid::CHANNEL_BROADCAST {
|
||||
let new_cid = if cid == CHANNEL_BROADCAST {
|
||||
// TODO: Prevent allocating 2^32 channels.
|
||||
self.allocated_cids += 1;
|
||||
(self.allocated_cids as u32).to_be_bytes()
|
||||
@@ -291,10 +282,10 @@ impl CtapHid {
|
||||
let mut payload = vec![0; 17];
|
||||
payload[..8].copy_from_slice(&message.payload);
|
||||
payload[8..12].copy_from_slice(&new_cid);
|
||||
payload[12] = CtapHid::PROTOCOL_VERSION;
|
||||
payload[13] = CtapHid::DEVICE_VERSION_MAJOR;
|
||||
payload[14] = CtapHid::DEVICE_VERSION_MINOR;
|
||||
payload[15] = CtapHid::DEVICE_VERSION_BUILD;
|
||||
payload[12] = PROTOCOL_VERSION;
|
||||
payload[13] = DEVICE_VERSION_MAJOR;
|
||||
payload[14] = DEVICE_VERSION_MINOR;
|
||||
payload[15] = DEVICE_VERSION_BUILD;
|
||||
payload[16] = self.capabilities;
|
||||
|
||||
Some(Message {
|
||||
@@ -317,7 +308,7 @@ impl CtapHid {
|
||||
CtapHidCommand::Wink => Some(message),
|
||||
_ => {
|
||||
// Unknown or unsupported command.
|
||||
Some(CtapHid::error_message(cid, CtapHidError::InvalidCmd))
|
||||
Some(Self::error_message(cid, CtapHidError::InvalidCmd))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -325,14 +316,14 @@ impl CtapHid {
|
||||
fn has_valid_channel(&self, message: &Message) -> bool {
|
||||
match message.cid {
|
||||
// Only INIT commands use the broadcast channel.
|
||||
CtapHid::CHANNEL_BROADCAST => message.cmd == CtapHidCommand::Init,
|
||||
CHANNEL_BROADCAST => message.cmd == CtapHidCommand::Init,
|
||||
// Check that the channel is allocated.
|
||||
_ => self.is_allocated_channel(message.cid),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_allocated_channel(&self, cid: ChannelID) -> bool {
|
||||
cid != CtapHid::CHANNEL_RESERVED && u32::from_be_bytes(cid) as usize <= self.allocated_cids
|
||||
cid != CHANNEL_RESERVED && u32::from_be_bytes(cid) as usize <= self.allocated_cids
|
||||
}
|
||||
|
||||
pub fn error_message(cid: ChannelID, error_code: CtapHidError) -> Message {
|
||||
@@ -346,8 +337,8 @@ impl CtapHid {
|
||||
/// Helper function to parse a raw packet.
|
||||
pub fn process_single_packet(packet: &HidPacket) -> (ChannelID, ProcessedPacket) {
|
||||
let (&cid, rest) = array_refs![packet, 4, 60];
|
||||
if rest[0] & CtapHid::PACKET_TYPE_MASK != 0 {
|
||||
let cmd = rest[0] & !CtapHid::PACKET_TYPE_MASK;
|
||||
if rest[0] & PACKET_TYPE_MASK != 0 {
|
||||
let cmd = rest[0] & !PACKET_TYPE_MASK;
|
||||
let len = (rest[1] as usize) << 8 | (rest[2] as usize);
|
||||
(
|
||||
cid,
|
||||
@@ -394,7 +385,7 @@ impl CtapHid {
|
||||
|
||||
/// Generates the HID response packets for a keepalive status.
|
||||
pub fn keepalive(cid: ChannelID, status: KeepaliveStatus) -> HidPacketIterator {
|
||||
CtapHid::split_message(Message {
|
||||
Self::split_message(Message {
|
||||
cid,
|
||||
cmd: CtapHidCommand::Keepalive,
|
||||
payload: vec![status as u8],
|
||||
@@ -402,9 +393,9 @@ impl CtapHid {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn new_initialized() -> (CtapHid, ChannelID) {
|
||||
pub fn new_initialized() -> (Self, ChannelID) {
|
||||
(
|
||||
CtapHid {
|
||||
Self {
|
||||
assembler: MessageAssembler::new(),
|
||||
allocated_cids: 1,
|
||||
capabilities: 0x0D,
|
||||
@@ -417,7 +408,6 @@ impl CtapHid {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::env::test::TestEnv;
|
||||
|
||||
#[test]
|
||||
fn test_split_assemble() {
|
||||
@@ -430,9 +420,9 @@ mod test {
|
||||
};
|
||||
|
||||
let mut messages = Vec::new();
|
||||
let mut assembler = MessageAssembler::new();
|
||||
let mut assembler = MessageAssembler::<TestEnv>::new();
|
||||
for packet in HidPacketIterator::new(message.clone()).unwrap() {
|
||||
match assembler.parse_packet(&mut env, &packet, CtapInstant::new(0)) {
|
||||
match assembler.parse_packet(&mut env, &packet) {
|
||||
Ok(Some(msg)) => messages.push(msg),
|
||||
Ok(None) => (),
|
||||
Err(_) => panic!("Couldn't assemble packet: {:02x?}", &packet as &[u8]),
|
||||
@@ -446,21 +436,18 @@ mod test {
|
||||
#[test]
|
||||
fn test_spurious_continuation_packet() {
|
||||
let mut env = TestEnv::new();
|
||||
let mut ctap_hid = CtapHid::new(0x0D);
|
||||
let mut ctap_hid = CtapHid::<TestEnv>::new(0x0D);
|
||||
let mut packet = [0x00; 64];
|
||||
packet[0..7].copy_from_slice(&[0xC1, 0xC1, 0xC1, 0xC1, 0x00, 0x51, 0x51]);
|
||||
// Continuation packets are silently ignored.
|
||||
assert_eq!(
|
||||
ctap_hid.parse_packet(&mut env, &packet, CtapInstant::new(0)),
|
||||
None
|
||||
);
|
||||
assert_eq!(ctap_hid.parse_packet(&mut env, &packet), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_command_init() {
|
||||
let mut ctap_hid = CtapHid::new(0x0D);
|
||||
let mut ctap_hid = CtapHid::<TestEnv>::new(0x0D);
|
||||
let init_message = Message {
|
||||
cid: CtapHid::CHANNEL_BROADCAST,
|
||||
cid: CHANNEL_BROADCAST,
|
||||
cmd: CtapHidCommand::Init,
|
||||
payload: vec![0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0],
|
||||
};
|
||||
@@ -468,7 +455,7 @@ mod test {
|
||||
assert_eq!(
|
||||
reply,
|
||||
Some(Message {
|
||||
cid: CtapHid::CHANNEL_BROADCAST,
|
||||
cid: CHANNEL_BROADCAST,
|
||||
cmd: CtapHidCommand::Init,
|
||||
payload: vec![
|
||||
0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, // Nonce
|
||||
@@ -484,7 +471,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_command_init_for_sync() {
|
||||
let mut env = TestEnv::new();
|
||||
let (mut ctap_hid, cid) = CtapHid::new_initialized();
|
||||
let (mut ctap_hid, cid) = CtapHid::<TestEnv>::new_initialized();
|
||||
|
||||
// Ping packet with a length longer than one packet.
|
||||
let mut packet1 = [0x51; 64];
|
||||
@@ -496,12 +483,9 @@ mod test {
|
||||
packet2[4..15].copy_from_slice(&[
|
||||
0x86, 0x00, 0x08, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0,
|
||||
]);
|
||||
assert_eq!(ctap_hid.parse_packet(&mut env, &packet1), None);
|
||||
assert_eq!(
|
||||
ctap_hid.parse_packet(&mut env, &packet1, CtapInstant::new(0)),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
ctap_hid.parse_packet(&mut env, &packet2, CtapInstant::new(0)),
|
||||
ctap_hid.parse_packet(&mut env, &packet2),
|
||||
Some(Message {
|
||||
cid,
|
||||
cmd: CtapHidCommand::Init,
|
||||
@@ -519,13 +503,13 @@ mod test {
|
||||
#[test]
|
||||
fn test_command_ping() {
|
||||
let mut env = TestEnv::new();
|
||||
let (mut ctap_hid, cid) = CtapHid::new_initialized();
|
||||
let (mut ctap_hid, cid) = CtapHid::<TestEnv>::new_initialized();
|
||||
|
||||
let mut ping_packet = [0x00; 64];
|
||||
ping_packet[..4].copy_from_slice(&cid);
|
||||
ping_packet[4..9].copy_from_slice(&[0x81, 0x00, 0x02, 0x99, 0x99]);
|
||||
assert_eq!(
|
||||
ctap_hid.parse_packet(&mut env, &ping_packet, CtapInstant::new(0)),
|
||||
ctap_hid.parse_packet(&mut env, &ping_packet),
|
||||
Some(Message {
|
||||
cid,
|
||||
cmd: CtapHidCommand::Ping,
|
||||
@@ -537,13 +521,13 @@ mod test {
|
||||
#[test]
|
||||
fn test_command_cancel() {
|
||||
let mut env = TestEnv::new();
|
||||
let (mut ctap_hid, cid) = CtapHid::new_initialized();
|
||||
let (mut ctap_hid, cid) = CtapHid::<TestEnv>::new_initialized();
|
||||
|
||||
let mut cancel_packet = [0x00; 64];
|
||||
cancel_packet[..4].copy_from_slice(&cid);
|
||||
cancel_packet[4..7].copy_from_slice(&[0x91, 0x00, 0x00]);
|
||||
|
||||
let response = ctap_hid.parse_packet(&mut env, &cancel_packet, CtapInstant::new(0));
|
||||
let response = ctap_hid.parse_packet(&mut env, &cancel_packet);
|
||||
assert_eq!(response, None);
|
||||
}
|
||||
|
||||
@@ -554,7 +538,7 @@ mod test {
|
||||
cmd: CtapHidCommand::Ping,
|
||||
payload: vec![0x99, 0x99],
|
||||
};
|
||||
let mut response = CtapHid::split_message(message);
|
||||
let mut response = CtapHid::<TestEnv>::split_message(message);
|
||||
let mut expected_packet = [0x00; 64];
|
||||
expected_packet[..9]
|
||||
.copy_from_slice(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x02, 0x99, 0x99]);
|
||||
@@ -570,7 +554,7 @@ mod test {
|
||||
cmd: CtapHidCommand::Cbor,
|
||||
payload,
|
||||
};
|
||||
let mut response = CtapHid::split_message(message);
|
||||
let mut response = CtapHid::<TestEnv>::split_message(message);
|
||||
let mut expected_packet = [0x00; 64];
|
||||
expected_packet[..8].copy_from_slice(&[0x12, 0x34, 0x56, 0x78, 0x90, 0x00, 0x01, 0xF2]);
|
||||
assert_eq!(response.next(), Some(expected_packet));
|
||||
@@ -581,7 +565,7 @@ mod test {
|
||||
fn test_keepalive() {
|
||||
for &status in [KeepaliveStatus::Processing, KeepaliveStatus::UpNeeded].iter() {
|
||||
let cid = [0x12, 0x34, 0x56, 0x78];
|
||||
let mut response = CtapHid::keepalive(cid, status);
|
||||
let mut response = CtapHid::<TestEnv>::keepalive(cid, status);
|
||||
let mut expected_packet = [0x00; 64];
|
||||
expected_packet[..8].copy_from_slice(&[
|
||||
0x12,
|
||||
@@ -604,7 +588,7 @@ mod test {
|
||||
let mut packet = [0x00; 64];
|
||||
packet[..4].copy_from_slice(&cid);
|
||||
packet[4..9].copy_from_slice(&[0x81, 0x00, 0x02, 0x99, 0x99]);
|
||||
let (processed_cid, processed_packet) = CtapHid::process_single_packet(&packet);
|
||||
let (processed_cid, processed_packet) = CtapHid::<TestEnv>::process_single_packet(&packet);
|
||||
assert_eq!(processed_cid, cid);
|
||||
let expected_packet = ProcessedPacket::InitPacket {
|
||||
cmd: CtapHidCommand::Ping as u8,
|
||||
@@ -627,7 +611,7 @@ mod test {
|
||||
fn test_error_message() {
|
||||
let cid = [0x12, 0x34, 0x56, 0x78];
|
||||
assert_eq!(
|
||||
CtapHid::error_message(cid, CtapHidError::InvalidCmd),
|
||||
CtapHid::<TestEnv>::error_message(cid, CtapHidError::InvalidCmd),
|
||||
Message {
|
||||
cid,
|
||||
cmd: CtapHidCommand::Error,
|
||||
|
||||
@@ -15,20 +15,23 @@
|
||||
use super::{
|
||||
ChannelID, CtapHid, CtapHidCommand, CtapHidError, HidPacket, Message, ProcessedPacket,
|
||||
};
|
||||
use crate::api::clock::Clock;
|
||||
use crate::api::customization::Customization;
|
||||
use crate::clock::CtapInstant;
|
||||
use crate::env::Env;
|
||||
use alloc::vec::Vec;
|
||||
use core::mem::swap;
|
||||
|
||||
// TODO: Is this timeout duration specified?
|
||||
const TIMEOUT_DURATION_MS: usize = 100;
|
||||
|
||||
/// A structure to assemble CTAPHID commands from a series of incoming USB HID packets.
|
||||
pub struct MessageAssembler {
|
||||
pub struct MessageAssembler<E: Env> {
|
||||
// Whether this is waiting to receive an initialization packet.
|
||||
idle: bool,
|
||||
// Current channel ID.
|
||||
cid: ChannelID,
|
||||
// Timestamp of the last packet received on the current channel.
|
||||
last_timestamp: CtapInstant,
|
||||
timer: <E::Clock as Clock>::Timer,
|
||||
// Current command.
|
||||
cmd: u8,
|
||||
// Sequence number expected for the next packet.
|
||||
@@ -39,12 +42,12 @@ pub struct MessageAssembler {
|
||||
payload: Vec<u8>,
|
||||
}
|
||||
|
||||
impl MessageAssembler {
|
||||
pub fn new() -> MessageAssembler {
|
||||
impl<E: Env> MessageAssembler<E> {
|
||||
pub fn new() -> MessageAssembler<E> {
|
||||
MessageAssembler {
|
||||
idle: true,
|
||||
cid: [0, 0, 0, 0],
|
||||
last_timestamp: CtapInstant::new(0),
|
||||
timer: <E::Clock as Clock>::Timer::default(),
|
||||
cmd: 0,
|
||||
seq: 0,
|
||||
remaining_payload_len: 0,
|
||||
@@ -57,7 +60,7 @@ impl MessageAssembler {
|
||||
fn reset(&mut self) {
|
||||
self.idle = true;
|
||||
self.cid = [0, 0, 0, 0];
|
||||
self.last_timestamp = CtapInstant::new(0);
|
||||
self.timer = <E::Clock as Clock>::Timer::default();
|
||||
self.cmd = 0;
|
||||
self.seq = 0;
|
||||
self.remaining_payload_len = 0;
|
||||
@@ -69,19 +72,16 @@ impl MessageAssembler {
|
||||
// full message was assembled after this packet, or None if more packets are needed to fill the
|
||||
// message.
|
||||
// - An Err() result if there was a parsing error.
|
||||
// TODO: Implement timeouts. For example, have the caller pass us a timestamp of when this
|
||||
// packet was received.
|
||||
pub fn parse_packet(
|
||||
&mut self,
|
||||
env: &mut impl Env,
|
||||
env: &mut E,
|
||||
packet: &HidPacket,
|
||||
timestamp: CtapInstant,
|
||||
) -> Result<Option<Message>, (ChannelID, CtapHidError)> {
|
||||
// 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);
|
||||
let (cid, processed_packet) = CtapHid::<E>::process_single_packet(packet);
|
||||
|
||||
if !self.idle && timestamp >= self.last_timestamp + CtapHid::TIMEOUT_DURATION {
|
||||
if !self.idle && env.clock().is_elapsed(&self.timer) {
|
||||
// The current channel timed out.
|
||||
// Save the channel ID and reset the state.
|
||||
let current_cid = self.cid;
|
||||
@@ -98,7 +98,7 @@ impl MessageAssembler {
|
||||
// Expecting an initialization packet.
|
||||
match processed_packet {
|
||||
ProcessedPacket::InitPacket { cmd, len, data } => {
|
||||
self.parse_init_packet(env, cid, cmd, len, data, timestamp)
|
||||
self.parse_init_packet(env, cid, cmd, len, data)
|
||||
}
|
||||
ProcessedPacket::ContinuationPacket { .. } => {
|
||||
// CTAP specification (version 20190130) section 8.1.5.4
|
||||
@@ -120,7 +120,7 @@ impl MessageAssembler {
|
||||
ProcessedPacket::InitPacket { cmd, len, data } => {
|
||||
self.reset();
|
||||
if cmd == CtapHidCommand::Init as u8 {
|
||||
self.parse_init_packet(env, cid, cmd, len, data, timestamp)
|
||||
self.parse_init_packet(env, cid, cmd, len, data)
|
||||
} else {
|
||||
Err((cid, CtapHidError::InvalidSeq))
|
||||
}
|
||||
@@ -132,7 +132,7 @@ impl MessageAssembler {
|
||||
Err((cid, CtapHidError::InvalidSeq))
|
||||
} else {
|
||||
// Update the last timestamp.
|
||||
self.last_timestamp = timestamp;
|
||||
self.timer = env.clock().make_timer(TIMEOUT_DURATION_MS);
|
||||
// Increment the sequence number for the next packet.
|
||||
self.seq += 1;
|
||||
Ok(self.append_payload(data))
|
||||
@@ -144,12 +144,11 @@ impl MessageAssembler {
|
||||
|
||||
fn parse_init_packet(
|
||||
&mut self,
|
||||
env: &mut impl Env,
|
||||
env: &mut E,
|
||||
cid: ChannelID,
|
||||
cmd: u8,
|
||||
len: usize,
|
||||
data: &[u8],
|
||||
timestamp: CtapInstant,
|
||||
) -> Result<Option<Message>, (ChannelID, CtapHidError)> {
|
||||
// Reject invalid lengths early to reduce the risk of running out of memory.
|
||||
// TODO: also reject invalid commands early?
|
||||
@@ -157,7 +156,7 @@ impl MessageAssembler {
|
||||
return Err((cid, CtapHidError::InvalidLen));
|
||||
}
|
||||
self.cid = cid;
|
||||
self.last_timestamp = timestamp;
|
||||
self.timer = env.clock().make_timer(TIMEOUT_DURATION_MS);
|
||||
self.cmd = cmd;
|
||||
self.seq = 0;
|
||||
self.remaining_payload_len = len;
|
||||
@@ -187,9 +186,7 @@ impl MessageAssembler {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::ctap::hid::CtapHid;
|
||||
use crate::env::test::TestEnv;
|
||||
use embedded_time::duration::Milliseconds;
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -212,14 +209,8 @@ mod test {
|
||||
fn test_empty_payload() {
|
||||
let mut env = TestEnv::new();
|
||||
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(
|
||||
&mut env,
|
||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x90]),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x90])),
|
||||
Ok(Some(Message {
|
||||
cid: [0x12, 0x34, 0x56, 0x78],
|
||||
cmd: CtapHidCommand::Cbor,
|
||||
@@ -236,7 +227,6 @@ mod test {
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x90, 0x00, 0x10]),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
Ok(Some(Message {
|
||||
cid: [0x12, 0x34, 0x56, 0x78],
|
||||
@@ -257,7 +247,6 @@ mod test {
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x90, 0x00, 0x10], 0xFF),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
Ok(Some(Message {
|
||||
cid: [0x12, 0x34, 0x56, 0x78],
|
||||
@@ -275,16 +264,11 @@ mod test {
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
Ok(None)
|
||||
);
|
||||
assert_eq!(
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00])),
|
||||
Ok(Some(Message {
|
||||
cid: [0x12, 0x34, 0x56, 0x78],
|
||||
cmd: CtapHidCommand::Ping,
|
||||
@@ -301,24 +285,15 @@ mod test {
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x80]),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
Ok(None)
|
||||
);
|
||||
assert_eq!(
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00])),
|
||||
Ok(None)
|
||||
);
|
||||
assert_eq!(
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x01]),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x01])),
|
||||
Ok(Some(Message {
|
||||
cid: [0x12, 0x34, 0x56, 0x78],
|
||||
cmd: CtapHidCommand::Ping,
|
||||
@@ -335,26 +310,17 @@ mod test {
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x1D, 0xB9]),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
Ok(None)
|
||||
);
|
||||
for seq in 0..0x7F {
|
||||
assert_eq!(
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, seq]),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, seq])),
|
||||
Ok(None)
|
||||
);
|
||||
}
|
||||
assert_eq!(
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x7F]),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x7F])),
|
||||
Ok(Some(Message {
|
||||
cid: [0x12, 0x34, 0x56, 0x78],
|
||||
cmd: CtapHidCommand::Ping,
|
||||
@@ -380,7 +346,6 @@ mod test {
|
||||
&[0x12, 0x34, 0x56, 0x78, 0x80 | cmd as u8, 0x00, 0x80],
|
||||
byte
|
||||
),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
Ok(None)
|
||||
);
|
||||
@@ -388,7 +353,6 @@ mod test {
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x00], byte),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
Ok(None)
|
||||
);
|
||||
@@ -396,7 +360,6 @@ mod test {
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x01], byte),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
Ok(Some(Message {
|
||||
cid: [0x12, 0x34, 0x56, 0x78],
|
||||
@@ -422,24 +385,17 @@ mod test {
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&byte_extend(&[0x12, 0x34, 0x56, cid, 0x80 | cmd as u8, 0x00, 0x80], byte),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
Ok(None)
|
||||
);
|
||||
assert_eq!(
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&byte_extend(&[0x12, 0x34, 0x56, cid, 0x00], byte),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
assembler
|
||||
.parse_packet(&mut env, &byte_extend(&[0x12, 0x34, 0x56, cid, 0x00], byte)),
|
||||
Ok(None)
|
||||
);
|
||||
assert_eq!(
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&byte_extend(&[0x12, 0x34, 0x56, cid, 0x01], byte),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
assembler
|
||||
.parse_packet(&mut env, &byte_extend(&[0x12, 0x34, 0x56, cid, 0x01], byte)),
|
||||
Ok(Some(Message {
|
||||
cid: [0x12, 0x34, 0x56, cid],
|
||||
cmd,
|
||||
@@ -457,7 +413,6 @@ mod test {
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
Ok(None)
|
||||
);
|
||||
@@ -470,7 +425,6 @@ mod test {
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&byte_extend(&[0x12, 0x34, 0x56, 0x9A, cmd as u8, 0x00], byte),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
Err(([0x12, 0x34, 0x56, 0x9A], CtapHidError::ChannelBusy))
|
||||
);
|
||||
@@ -478,11 +432,7 @@ mod test {
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00])),
|
||||
Ok(Some(Message {
|
||||
cid: [0x12, 0x34, 0x56, 0x78],
|
||||
cmd: CtapHidCommand::Ping,
|
||||
@@ -505,7 +455,6 @@ mod test {
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x10], byte),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
Ok(Some(Message {
|
||||
cid: [0x12, 0x34, 0x56, 0x78],
|
||||
@@ -517,11 +466,7 @@ mod test {
|
||||
// Spurious continuation packet.
|
||||
let seq = i;
|
||||
assert_eq!(
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, seq]),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, seq])),
|
||||
Err((
|
||||
[0x12, 0x34, 0x56, 0x78],
|
||||
CtapHidError::UnexpectedContinuation
|
||||
@@ -538,16 +483,11 @@ mod test {
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
Ok(None)
|
||||
);
|
||||
assert_eq!(
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x80]),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x80])),
|
||||
Err(([0x12, 0x34, 0x56, 0x78], CtapHidError::InvalidSeq))
|
||||
);
|
||||
}
|
||||
@@ -560,16 +500,11 @@ mod test {
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
Ok(None)
|
||||
);
|
||||
assert_eq!(
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x01]),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x01])),
|
||||
Err(([0x12, 0x34, 0x56, 0x78], CtapHidError::InvalidSeq))
|
||||
);
|
||||
}
|
||||
@@ -582,16 +517,12 @@ mod test {
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
Ok(None)
|
||||
);
|
||||
env.clock().advance(TIMEOUT_DURATION_MS);
|
||||
assert_eq!(
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]),
|
||||
CtapInstant::new(0) + CtapHid::TIMEOUT_DURATION
|
||||
),
|
||||
assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00])),
|
||||
Err(([0x12, 0x34, 0x56, 0x78], CtapHidError::MsgTimeout))
|
||||
);
|
||||
}
|
||||
@@ -599,37 +530,27 @@ mod test {
|
||||
#[test]
|
||||
fn test_just_in_time_packets() {
|
||||
let mut env = TestEnv::new();
|
||||
let mut timestamp: CtapInstant = CtapInstant::new(0);
|
||||
// Delay between each packet is just below the threshold.
|
||||
let delay = CtapHid::TIMEOUT_DURATION - Milliseconds(1_u32);
|
||||
let delay = TIMEOUT_DURATION_MS - 1;
|
||||
|
||||
let mut assembler = MessageAssembler::new();
|
||||
assert_eq!(
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x1D, 0xB9]),
|
||||
timestamp
|
||||
),
|
||||
Ok(None)
|
||||
);
|
||||
for seq in 0..0x7F {
|
||||
timestamp = timestamp + delay;
|
||||
env.clock().advance(delay);
|
||||
assert_eq!(
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, seq]),
|
||||
timestamp
|
||||
),
|
||||
assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, seq])),
|
||||
Ok(None)
|
||||
);
|
||||
}
|
||||
timestamp = timestamp + delay;
|
||||
env.clock().advance(delay);
|
||||
assert_eq!(
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x7F]),
|
||||
timestamp
|
||||
),
|
||||
assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x7F])),
|
||||
Ok(Some(Message {
|
||||
cid: [0x12, 0x34, 0x56, 0x78],
|
||||
cmd: CtapHidCommand::Ping,
|
||||
@@ -647,7 +568,6 @@ mod test {
|
||||
assembler.parse_packet(
|
||||
&mut env,
|
||||
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x02, 0x00], 0x51),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
Ok(None)
|
||||
);
|
||||
@@ -659,7 +579,6 @@ mod test {
|
||||
0x12, 0x34, 0x56, 0x78, 0x86, 0x00, 0x08, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC,
|
||||
0xDE, 0xF0
|
||||
]),
|
||||
CtapInstant::new(0)
|
||||
),
|
||||
Ok(Some(Message {
|
||||
cid: [0x12, 0x34, 0x56, 0x78],
|
||||
|
||||
@@ -12,7 +12,9 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::{CtapHid, HidPacket, Message};
|
||||
use super::{HidPacket, Message};
|
||||
|
||||
const TYPE_INIT_BIT: u8 = 0x80;
|
||||
|
||||
/// Iterator for HID packets.
|
||||
///
|
||||
@@ -121,7 +123,7 @@ impl Iterator for MessageSplitter {
|
||||
match self.seq {
|
||||
None => {
|
||||
// First, send an initialization packet.
|
||||
self.packet[4] = self.message.cmd as u8 | CtapHid::TYPE_INIT_BIT;
|
||||
self.packet[4] = self.message.cmd as u8 | TYPE_INIT_BIT;
|
||||
self.packet[5] = (payload_len >> 8) as u8;
|
||||
self.packet[6] = payload_len as u8;
|
||||
|
||||
|
||||
@@ -45,10 +45,10 @@ impl LargeBlobs {
|
||||
}
|
||||
|
||||
/// Process the large blob command.
|
||||
pub fn process_command(
|
||||
pub fn process_command<E: Env>(
|
||||
&mut self,
|
||||
env: &mut impl Env,
|
||||
client_pin: &mut ClientPin,
|
||||
env: &mut E,
|
||||
client_pin: &mut ClientPin<E>,
|
||||
large_blobs_params: AuthenticatorLargeBlobsParameters,
|
||||
) -> Result<ResponseData, Ctap2StatusCode> {
|
||||
let AuthenticatorLargeBlobsParameters {
|
||||
@@ -147,8 +147,12 @@ mod test {
|
||||
let mut env = TestEnv::new();
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||
&mut env,
|
||||
key_agreement_key,
|
||||
pin_uv_auth_token,
|
||||
PinUvAuthProtocol::V1,
|
||||
);
|
||||
let mut large_blobs = LargeBlobs::new();
|
||||
|
||||
let large_blob = vec![
|
||||
@@ -178,8 +182,12 @@ mod test {
|
||||
let mut env = TestEnv::new();
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||
&mut env,
|
||||
key_agreement_key,
|
||||
pin_uv_auth_token,
|
||||
PinUvAuthProtocol::V1,
|
||||
);
|
||||
let mut large_blobs = LargeBlobs::new();
|
||||
|
||||
const BLOB_LEN: usize = 200;
|
||||
@@ -240,8 +248,12 @@ mod test {
|
||||
let mut env = TestEnv::new();
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||
&mut env,
|
||||
key_agreement_key,
|
||||
pin_uv_auth_token,
|
||||
PinUvAuthProtocol::V1,
|
||||
);
|
||||
let mut large_blobs = LargeBlobs::new();
|
||||
|
||||
const BLOB_LEN: usize = 200;
|
||||
@@ -286,8 +298,12 @@ mod test {
|
||||
let mut env = TestEnv::new();
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||
&mut env,
|
||||
key_agreement_key,
|
||||
pin_uv_auth_token,
|
||||
PinUvAuthProtocol::V1,
|
||||
);
|
||||
let mut large_blobs = LargeBlobs::new();
|
||||
|
||||
const BLOB_LEN: usize = 200;
|
||||
@@ -332,8 +348,12 @@ mod test {
|
||||
let mut env = TestEnv::new();
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||
&mut env,
|
||||
key_agreement_key,
|
||||
pin_uv_auth_token,
|
||||
PinUvAuthProtocol::V1,
|
||||
);
|
||||
let mut large_blobs = LargeBlobs::new();
|
||||
|
||||
let large_blobs_params = AuthenticatorLargeBlobsParameters {
|
||||
@@ -355,8 +375,12 @@ mod test {
|
||||
let mut env = TestEnv::new();
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||
&mut env,
|
||||
key_agreement_key,
|
||||
pin_uv_auth_token,
|
||||
PinUvAuthProtocol::V1,
|
||||
);
|
||||
let mut large_blobs = LargeBlobs::new();
|
||||
|
||||
const BLOB_LEN: usize = 20;
|
||||
@@ -383,8 +407,12 @@ mod test {
|
||||
let mut env = TestEnv::new();
|
||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||
let pin_uv_auth_token = [0x55; 32];
|
||||
let mut client_pin =
|
||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol);
|
||||
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||
&mut env,
|
||||
key_agreement_key,
|
||||
pin_uv_auth_token,
|
||||
pin_uv_auth_protocol,
|
||||
);
|
||||
let mut large_blobs = LargeBlobs::new();
|
||||
|
||||
const BLOB_LEN: usize = 20;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::clock::{ClockInt, CtapInstant};
|
||||
use crate::api::clock::Clock;
|
||||
#[cfg(feature = "with_ctap1")]
|
||||
use crate::ctap::ctap1;
|
||||
#[cfg(feature = "with_ctap1")]
|
||||
@@ -20,29 +20,29 @@ use crate::ctap::hid::ChannelID;
|
||||
use crate::ctap::hid::{
|
||||
CtapHid, CtapHidCommand, CtapHidError, HidPacket, HidPacketIterator, Message,
|
||||
};
|
||||
use crate::ctap::{Channel, CtapState, TimedPermission};
|
||||
use crate::ctap::{Channel, CtapState};
|
||||
use crate::env::Env;
|
||||
use embedded_time::duration::Milliseconds;
|
||||
|
||||
const WINK_TIMEOUT_DURATION_MS: usize = 5000;
|
||||
|
||||
/// Implements the standard CTAP command processing for HID.
|
||||
pub struct MainHid {
|
||||
hid: CtapHid,
|
||||
wink_permission: TimedPermission,
|
||||
pub struct MainHid<E: Env> {
|
||||
hid: CtapHid<E>,
|
||||
wink_permission: <E::Clock as Clock>::Timer,
|
||||
}
|
||||
|
||||
impl MainHid {
|
||||
const WINK_TIMEOUT_DURATION: Milliseconds<ClockInt> = Milliseconds(5000 as ClockInt);
|
||||
|
||||
impl<E: Env> MainHid<E> {
|
||||
/// Instantiates a HID handler for CTAP1, CTAP2 and Wink.
|
||||
pub fn new() -> Self {
|
||||
#[cfg(feature = "with_ctap1")]
|
||||
let capabilities = CtapHid::CAPABILITY_WINK | CtapHid::CAPABILITY_CBOR;
|
||||
let capabilities = CtapHid::<E>::CAPABILITY_WINK | CtapHid::<E>::CAPABILITY_CBOR;
|
||||
#[cfg(not(feature = "with_ctap1"))]
|
||||
let capabilities =
|
||||
CtapHid::CAPABILITY_WINK | CtapHid::CAPABILITY_CBOR | CtapHid::CAPABILITY_NMSG;
|
||||
let capabilities = CtapHid::<E>::CAPABILITY_WINK
|
||||
| CtapHid::<E>::CAPABILITY_CBOR
|
||||
| CtapHid::<E>::CAPABILITY_NMSG;
|
||||
|
||||
let hid = CtapHid::new(capabilities);
|
||||
let wink_permission = TimedPermission::waiting();
|
||||
let wink_permission = <E::Clock as Clock>::Timer::default();
|
||||
MainHid {
|
||||
hid,
|
||||
wink_permission,
|
||||
@@ -52,15 +52,14 @@ impl MainHid {
|
||||
/// Processes an incoming USB HID packet, and returns an iterator for all outgoing packets.
|
||||
pub fn process_hid_packet(
|
||||
&mut self,
|
||||
env: &mut impl Env,
|
||||
env: &mut E,
|
||||
packet: &HidPacket,
|
||||
now: CtapInstant,
|
||||
ctap_state: &mut CtapState,
|
||||
ctap_state: &mut CtapState<E>,
|
||||
) -> HidPacketIterator {
|
||||
if let Some(message) = self.hid.parse_packet(env, packet, now) {
|
||||
let processed_message = self.process_message(env, message, now, ctap_state);
|
||||
if let Some(message) = self.hid.parse_packet(env, packet) {
|
||||
let processed_message = self.process_message(env, message, ctap_state);
|
||||
debug_ctap!(env, "Sending message: {:02x?}", processed_message);
|
||||
CtapHid::split_message(processed_message)
|
||||
CtapHid::<E>::split_message(processed_message)
|
||||
} else {
|
||||
HidPacketIterator::none()
|
||||
}
|
||||
@@ -69,13 +68,12 @@ impl MainHid {
|
||||
/// Processes a message's commands that affect the protocol outside HID.
|
||||
pub fn process_message(
|
||||
&mut self,
|
||||
env: &mut impl Env,
|
||||
env: &mut E,
|
||||
message: Message,
|
||||
now: CtapInstant,
|
||||
ctap_state: &mut CtapState,
|
||||
ctap_state: &mut CtapState<E>,
|
||||
) -> Message {
|
||||
// If another command arrives, stop winking to prevent accidential button touches.
|
||||
self.wink_permission = TimedPermission::waiting();
|
||||
self.wink_permission = <E::Clock as Clock>::Timer::default();
|
||||
|
||||
let cid = message.cid;
|
||||
match message.cmd {
|
||||
@@ -83,12 +81,12 @@ impl MainHid {
|
||||
CtapHidCommand::Msg => {
|
||||
// If we don't have CTAP1 backward compatibilty, this command is invalid.
|
||||
#[cfg(not(feature = "with_ctap1"))]
|
||||
return CtapHid::error_message(cid, CtapHidError::InvalidCmd);
|
||||
return CtapHid::<E>::error_message(cid, CtapHidError::InvalidCmd);
|
||||
|
||||
#[cfg(feature = "with_ctap1")]
|
||||
match ctap1::Ctap1Command::process_command(env, &message.payload, ctap_state, now) {
|
||||
Ok(payload) => MainHid::ctap1_success_message(cid, &payload),
|
||||
Err(ctap1_status_code) => MainHid::ctap1_error_message(cid, ctap1_status_code),
|
||||
match ctap1::Ctap1Command::process_command(env, &message.payload, ctap_state) {
|
||||
Ok(payload) => Self::ctap1_success_message(cid, &payload),
|
||||
Err(ctap1_status_code) => Self::ctap1_error_message(cid, ctap1_status_code),
|
||||
}
|
||||
}
|
||||
// CTAP 2.1 from 2021-06-15, section 11.2.9.1.2.
|
||||
@@ -97,7 +95,7 @@ impl MainHid {
|
||||
// don't handle any other packet in the meantime.
|
||||
// TODO: Send "Processing" type keep-alive packets in the meantime.
|
||||
let response =
|
||||
ctap_state.process_command(env, &message.payload, Channel::MainHid(cid), now);
|
||||
ctap_state.process_command(env, &message.payload, Channel::MainHid(cid));
|
||||
Message {
|
||||
cid,
|
||||
cmd: CtapHidCommand::Cbor,
|
||||
@@ -107,12 +105,11 @@ impl MainHid {
|
||||
// CTAP 2.1 from 2021-06-15, section 11.2.9.2.1.
|
||||
CtapHidCommand::Wink => {
|
||||
if message.payload.is_empty() {
|
||||
self.wink_permission =
|
||||
TimedPermission::granted(now, Self::WINK_TIMEOUT_DURATION);
|
||||
self.wink_permission = env.clock().make_timer(WINK_TIMEOUT_DURATION_MS);
|
||||
// The response is empty like the request.
|
||||
message
|
||||
} else {
|
||||
CtapHid::error_message(cid, CtapHidError::InvalidLen)
|
||||
CtapHid::<E>::error_message(cid, CtapHidError::InvalidLen)
|
||||
}
|
||||
}
|
||||
// All other commands have already been processed, keep them as is.
|
||||
@@ -121,13 +118,8 @@ impl MainHid {
|
||||
}
|
||||
|
||||
/// Returns whether a wink permission is currently granted.
|
||||
pub fn should_wink(&self, now: CtapInstant) -> bool {
|
||||
self.wink_permission.is_granted(now)
|
||||
}
|
||||
|
||||
/// Updates the timeout for the wink permission.
|
||||
pub fn update_wink_timeout(&mut self, now: CtapInstant) {
|
||||
self.wink_permission = self.wink_permission.check_expiration(now);
|
||||
pub fn should_wink(&self, env: &mut E) -> bool {
|
||||
!env.clock().is_elapsed(&self.wink_permission)
|
||||
}
|
||||
|
||||
#[cfg(feature = "with_ctap1")]
|
||||
@@ -159,11 +151,11 @@ mod test {
|
||||
use crate::ctap::hid::ChannelID;
|
||||
use crate::env::test::TestEnv;
|
||||
|
||||
fn new_initialized() -> (MainHid, ChannelID) {
|
||||
fn new_initialized() -> (MainHid<TestEnv>, ChannelID) {
|
||||
let (hid, cid) = CtapHid::new_initialized();
|
||||
let wink_permission = TimedPermission::waiting();
|
||||
let wink_permission = <<TestEnv as Env>::Clock as Clock>::Timer::default();
|
||||
(
|
||||
MainHid {
|
||||
MainHid::<TestEnv> {
|
||||
hid,
|
||||
wink_permission,
|
||||
},
|
||||
@@ -174,19 +166,14 @@ mod test {
|
||||
#[test]
|
||||
fn test_process_hid_packet() {
|
||||
let mut env = TestEnv::new();
|
||||
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
||||
let mut ctap_state = CtapState::<TestEnv>::new(&mut env);
|
||||
let (mut main_hid, cid) = new_initialized();
|
||||
|
||||
let mut ping_packet = [0x00; 64];
|
||||
ping_packet[..4].copy_from_slice(&cid);
|
||||
ping_packet[4..9].copy_from_slice(&[0x81, 0x00, 0x02, 0x99, 0x99]);
|
||||
|
||||
let mut response = main_hid.process_hid_packet(
|
||||
&mut env,
|
||||
&ping_packet,
|
||||
CtapInstant::new(0),
|
||||
&mut ctap_state,
|
||||
);
|
||||
let mut response = main_hid.process_hid_packet(&mut env, &ping_packet, &mut ctap_state);
|
||||
assert_eq!(response.next(), Some(ping_packet));
|
||||
assert_eq!(response.next(), None);
|
||||
}
|
||||
@@ -194,42 +181,33 @@ mod test {
|
||||
#[test]
|
||||
fn test_process_hid_packet_empty() {
|
||||
let mut env = TestEnv::new();
|
||||
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
||||
let mut ctap_state = CtapState::<TestEnv>::new(&mut env);
|
||||
let (mut main_hid, cid) = new_initialized();
|
||||
|
||||
let mut cancel_packet = [0x00; 64];
|
||||
cancel_packet[..4].copy_from_slice(&cid);
|
||||
cancel_packet[4..7].copy_from_slice(&[0x91, 0x00, 0x00]);
|
||||
|
||||
let mut response = main_hid.process_hid_packet(
|
||||
&mut env,
|
||||
&cancel_packet,
|
||||
CtapInstant::new(0),
|
||||
&mut ctap_state,
|
||||
);
|
||||
let mut response = main_hid.process_hid_packet(&mut env, &cancel_packet, &mut ctap_state);
|
||||
assert_eq!(response.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wink() {
|
||||
let mut env = TestEnv::new();
|
||||
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
||||
let mut ctap_state = CtapState::<TestEnv>::new(&mut env);
|
||||
let (mut main_hid, cid) = new_initialized();
|
||||
assert!(!main_hid.should_wink(CtapInstant::new(0)));
|
||||
assert!(!main_hid.should_wink(&mut env));
|
||||
|
||||
let mut wink_packet = [0x00; 64];
|
||||
wink_packet[..4].copy_from_slice(&cid);
|
||||
wink_packet[4..7].copy_from_slice(&[0x88, 0x00, 0x00]);
|
||||
|
||||
let mut response = main_hid.process_hid_packet(
|
||||
&mut env,
|
||||
&wink_packet,
|
||||
CtapInstant::new(0),
|
||||
&mut ctap_state,
|
||||
);
|
||||
let mut response = main_hid.process_hid_packet(&mut env, &wink_packet, &mut ctap_state);
|
||||
assert_eq!(response.next(), Some(wink_packet));
|
||||
assert_eq!(response.next(), None);
|
||||
assert!(main_hid.should_wink(CtapInstant::new(0)));
|
||||
assert!(!main_hid.should_wink(CtapInstant::new(1) + MainHid::WINK_TIMEOUT_DURATION));
|
||||
assert!(main_hid.should_wink(&mut env));
|
||||
env.clock().advance(WINK_TIMEOUT_DURATION_MS);
|
||||
assert!(!main_hid.should_wink(&mut env));
|
||||
}
|
||||
}
|
||||
|
||||
480
src/ctap/mod.rs
480
src/ctap/mod.rs
File diff suppressed because it is too large
Load Diff
@@ -1,199 +0,0 @@
|
||||
// Copyright 2019-2021 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2 (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
|
||||
//
|
||||
// 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.
|
||||
|
||||
use super::super::clock::{ClockInt, CtapInstant};
|
||||
use embedded_time::duration::Milliseconds;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum TimedPermission {
|
||||
Waiting,
|
||||
Granted(CtapInstant),
|
||||
}
|
||||
|
||||
impl TimedPermission {
|
||||
pub fn waiting() -> TimedPermission {
|
||||
TimedPermission::Waiting
|
||||
}
|
||||
|
||||
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: CtapInstant) -> bool {
|
||||
if let TimedPermission::Granted(timeout) = self {
|
||||
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: CtapInstant) -> TimedPermission {
|
||||
if let TimedPermission::Granted(timeout) = self {
|
||||
if timeout.checked_duration_since(&now).is_some() {
|
||||
return TimedPermission::Granted(timeout);
|
||||
}
|
||||
}
|
||||
TimedPermission::Waiting
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "with_ctap1")]
|
||||
#[derive(Debug)]
|
||||
pub struct U2fUserPresenceState {
|
||||
// If user presence was recently requested, its timeout is saved here.
|
||||
needs_up: TimedPermission,
|
||||
// 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: Milliseconds<ClockInt>,
|
||||
// This is the timeout duration of button touches.
|
||||
presence_duration: Milliseconds<ClockInt>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "with_ctap1")]
|
||||
impl U2fUserPresenceState {
|
||||
pub fn new(
|
||||
request_duration: Milliseconds<ClockInt>,
|
||||
presence_duration: Milliseconds<ClockInt>,
|
||||
) -> U2fUserPresenceState {
|
||||
U2fUserPresenceState {
|
||||
needs_up: TimedPermission::Waiting,
|
||||
has_up: TimedPermission::Waiting,
|
||||
request_duration,
|
||||
presence_duration,
|
||||
}
|
||||
}
|
||||
|
||||
// Granting user presence is ignored if it needs activation, but waits. Also cleans up.
|
||||
pub fn grant_up(&mut self, now: CtapInstant) {
|
||||
self.check_expiration(now);
|
||||
if self.needs_up.is_granted(now) {
|
||||
self.needs_up = TimedPermission::Waiting;
|
||||
self.has_up = TimedPermission::granted(now, self.presence_duration);
|
||||
}
|
||||
}
|
||||
|
||||
// This marks user presence as needed or uses it up if already granted. Also cleans up.
|
||||
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;
|
||||
true
|
||||
} else {
|
||||
self.needs_up = TimedPermission::granted(now, self.request_duration);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// Returns if user presence was requested. Also cleans up.
|
||||
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: CtapInstant) {
|
||||
self.needs_up = self.needs_up.check_expiration(now);
|
||||
self.has_up = self.has_up.check_expiration(now);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "with_ctap1")]
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
fn zero() -> CtapInstant {
|
||||
CtapInstant::new(0)
|
||||
}
|
||||
|
||||
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);
|
||||
assert!(u2f_state.consume_up(start_time));
|
||||
assert!(!u2f_state.consume_up(start_time));
|
||||
}
|
||||
|
||||
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
|
||||
.checked_add(presence_duration() + epsilon())
|
||||
.unwrap()
|
||||
));
|
||||
}
|
||||
|
||||
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
|
||||
.checked_add(presence_duration() + epsilon())
|
||||
.unwrap()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_grant_up_timeout() {
|
||||
grant_up_timeout(zero());
|
||||
grant_up_timeout(big_positive());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_need_up_timeout() {
|
||||
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());
|
||||
}
|
||||
|
||||
#[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()));
|
||||
}
|
||||
}
|
||||
@@ -12,22 +12,20 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::api::clock::Clock;
|
||||
use crate::ctap::client_pin::PinPermission;
|
||||
use crate::ctap::status_code::Ctap2StatusCode;
|
||||
use crate::ctap::timed_permission::TimedPermission;
|
||||
use crate::env::Env;
|
||||
use alloc::string::String;
|
||||
use crypto::sha256::Sha256;
|
||||
use crypto::Hash256;
|
||||
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: Milliseconds<ClockInt> = Milliseconds(30000 as ClockInt);
|
||||
const INITIAL_USAGE_TIME_LIMIT_MS: usize = 30000;
|
||||
|
||||
/// Implements pinUvAuthToken state from section 6.5.2.1.
|
||||
///
|
||||
@@ -35,22 +33,22 @@ const INITIAL_USAGE_TIME_LIMIT: Milliseconds<ClockInt> = Milliseconds(30000 as C
|
||||
/// built-in user verification. Therefore, we never cache user presence.
|
||||
///
|
||||
/// This implementation does not use a rolling timer.
|
||||
pub struct PinUvAuthTokenState {
|
||||
pub struct PinUvAuthTokenState<E: Env> {
|
||||
// Relies on the fact that all permissions are represented by powers of two.
|
||||
permissions_set: u8,
|
||||
permissions_rp_id: Option<String>,
|
||||
usage_timer: TimedPermission,
|
||||
usage_timer: <E::Clock as Clock>::Timer,
|
||||
user_verified: bool,
|
||||
in_use: bool,
|
||||
}
|
||||
|
||||
impl PinUvAuthTokenState {
|
||||
impl<E: Env> PinUvAuthTokenState<E> {
|
||||
/// Creates a pinUvAuthToken state without permissions.
|
||||
pub fn new() -> PinUvAuthTokenState {
|
||||
pub fn new() -> Self {
|
||||
PinUvAuthTokenState {
|
||||
permissions_set: 0,
|
||||
permissions_rp_id: None,
|
||||
usage_timer: TimedPermission::waiting(),
|
||||
usage_timer: <E::Clock as Clock>::Timer::default(),
|
||||
user_verified: false,
|
||||
in_use: false,
|
||||
}
|
||||
@@ -113,19 +111,18 @@ impl PinUvAuthTokenState {
|
||||
}
|
||||
|
||||
/// Starts the timer for pinUvAuthToken usage.
|
||||
pub fn begin_using_pin_uv_auth_token(&mut self, now: CtapInstant) {
|
||||
pub fn begin_using_pin_uv_auth_token(&mut self, env: &mut E) {
|
||||
self.user_verified = true;
|
||||
self.usage_timer = TimedPermission::granted(now, INITIAL_USAGE_TIME_LIMIT);
|
||||
self.usage_timer = env.clock().make_timer(INITIAL_USAGE_TIME_LIMIT_MS);
|
||||
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: CtapInstant) {
|
||||
pub fn pin_uv_auth_token_usage_timer_observer(&mut self, env: &mut E) {
|
||||
if !self.in_use {
|
||||
return;
|
||||
}
|
||||
self.usage_timer = self.usage_timer.check_expiration(now);
|
||||
if !self.usage_timer.is_granted(now) {
|
||||
if env.clock().is_elapsed(&self.usage_timer) {
|
||||
self.stop_using_pin_uv_auth_token();
|
||||
}
|
||||
}
|
||||
@@ -149,7 +146,7 @@ impl PinUvAuthTokenState {
|
||||
pub fn stop_using_pin_uv_auth_token(&mut self) {
|
||||
self.permissions_rp_id = None;
|
||||
self.permissions_set = 0;
|
||||
self.usage_timer = TimedPermission::waiting();
|
||||
self.usage_timer = <E::Clock as Clock>::Timer::default();
|
||||
self.user_verified = false;
|
||||
self.in_use = false;
|
||||
}
|
||||
@@ -158,27 +155,28 @@ impl PinUvAuthTokenState {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::env::test::TestEnv;
|
||||
use enum_iterator::IntoEnumIterator;
|
||||
|
||||
#[test]
|
||||
fn test_observer() {
|
||||
let mut token_state = PinUvAuthTokenState::new();
|
||||
let mut now: CtapInstant = CtapInstant::new(0);
|
||||
token_state.begin_using_pin_uv_auth_token(now);
|
||||
let mut env = TestEnv::new();
|
||||
let mut token_state = PinUvAuthTokenState::<TestEnv>::new();
|
||||
token_state.begin_using_pin_uv_auth_token(&mut env);
|
||||
assert!(token_state.is_in_use());
|
||||
now = now + Milliseconds(100_u32);
|
||||
token_state.pin_uv_auth_token_usage_timer_observer(now);
|
||||
env.clock().advance(100);
|
||||
token_state.pin_uv_auth_token_usage_timer_observer(&mut env);
|
||||
assert!(token_state.is_in_use());
|
||||
now = now + INITIAL_USAGE_TIME_LIMIT;
|
||||
token_state.pin_uv_auth_token_usage_timer_observer(now);
|
||||
env.clock().advance(INITIAL_USAGE_TIME_LIMIT_MS);
|
||||
token_state.pin_uv_auth_token_usage_timer_observer(&mut env);
|
||||
assert!(!token_state.is_in_use());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stop() {
|
||||
let mut token_state = PinUvAuthTokenState::new();
|
||||
let now: CtapInstant = CtapInstant::new(0);
|
||||
token_state.begin_using_pin_uv_auth_token(now);
|
||||
let mut env = TestEnv::new();
|
||||
let mut token_state = PinUvAuthTokenState::<TestEnv>::new();
|
||||
token_state.begin_using_pin_uv_auth_token(&mut env);
|
||||
assert!(token_state.is_in_use());
|
||||
token_state.stop_using_pin_uv_auth_token();
|
||||
assert!(!token_state.is_in_use());
|
||||
@@ -186,7 +184,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_permissions() {
|
||||
let mut token_state = PinUvAuthTokenState::new();
|
||||
let mut token_state = PinUvAuthTokenState::<TestEnv>::new();
|
||||
token_state.set_permissions(0xFF);
|
||||
for permission in PinPermission::into_enum_iter() {
|
||||
assert_eq!(token_state.has_permission(permission), Ok(()));
|
||||
@@ -211,7 +209,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_permissions_rp_id_none() {
|
||||
let mut token_state = PinUvAuthTokenState::new();
|
||||
let mut token_state = PinUvAuthTokenState::<TestEnv>::new();
|
||||
let example_hash = Sha256::hash(b"example.com");
|
||||
token_state.set_permissions_rp_id(None);
|
||||
assert_eq!(token_state.has_no_permissions_rp_id(), Ok(()));
|
||||
@@ -227,7 +225,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_permissions_rp_id_some() {
|
||||
let mut token_state = PinUvAuthTokenState::new();
|
||||
let mut token_state = PinUvAuthTokenState::<TestEnv>::new();
|
||||
let example_hash = Sha256::hash(b"example.com");
|
||||
token_state.set_permissions_rp_id(Some(String::from("example.com")));
|
||||
|
||||
@@ -262,14 +260,14 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_user_verified_flag() {
|
||||
let mut token_state = PinUvAuthTokenState::new();
|
||||
let mut env = TestEnv::new();
|
||||
let mut token_state = PinUvAuthTokenState::<TestEnv>::new();
|
||||
assert!(!token_state.get_user_verified_flag_value());
|
||||
let now: CtapInstant = CtapInstant::new(0);
|
||||
token_state.begin_using_pin_uv_auth_token(now);
|
||||
token_state.begin_using_pin_uv_auth_token(&mut env);
|
||||
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(now);
|
||||
token_state.begin_using_pin_uv_auth_token(&mut env);
|
||||
assert!(token_state.get_user_verified_flag_value());
|
||||
token_state.stop_using_pin_uv_auth_token();
|
||||
assert!(!token_state.get_user_verified_flag_value());
|
||||
|
||||
137
src/ctap/u2f_up.rs
Normal file
137
src/ctap/u2f_up.rs
Normal file
@@ -0,0 +1,137 @@
|
||||
// Copyright 2019-2021 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2 (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
|
||||
//
|
||||
// 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.
|
||||
|
||||
use super::TOUCH_TIMEOUT_MS;
|
||||
use crate::api::clock::Clock;
|
||||
use crate::env::Env;
|
||||
|
||||
const U2F_UP_PROMPT_TIMEOUT_MS: usize = 10000;
|
||||
|
||||
pub struct U2fUserPresenceState<E: Env> {
|
||||
/// If user presence was recently requested, its timeout is saved here.
|
||||
needs_up: <E::Clock as Clock>::Timer,
|
||||
|
||||
/// Button touch timeouts, while user presence is requested, are saved here.
|
||||
has_up: <E::Clock as Clock>::Timer,
|
||||
}
|
||||
|
||||
impl<E: Env> U2fUserPresenceState<E> {
|
||||
pub fn new() -> U2fUserPresenceState<E> {
|
||||
U2fUserPresenceState {
|
||||
needs_up: <E::Clock as Clock>::Timer::default(),
|
||||
has_up: <E::Clock as Clock>::Timer::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows consuming user presence until timeout, if it was needed.
|
||||
///
|
||||
/// If user presence was not requested, granting user presence does nothing.
|
||||
pub fn grant_up(&mut self, env: &mut E) {
|
||||
if !env.clock().is_elapsed(&self.needs_up) {
|
||||
self.needs_up = <E::Clock as Clock>::Timer::default();
|
||||
self.has_up = env.clock().make_timer(TOUCH_TIMEOUT_MS);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether user presence was granted within the timeout and not yet consumed.
|
||||
pub fn consume_up(&mut self, env: &mut E) -> bool {
|
||||
if !env.clock().is_elapsed(&self.has_up) {
|
||||
self.has_up = <E::Clock as Clock>::Timer::default();
|
||||
true
|
||||
} else {
|
||||
self.needs_up = env.clock().make_timer(U2F_UP_PROMPT_TIMEOUT_MS);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether user presence was requested.
|
||||
///
|
||||
/// This function doesn't represent interaction with the environment, and does not change the
|
||||
/// state, i.e. neither grants nor consumes user presence.
|
||||
pub fn is_up_needed(&mut self, env: &mut E) -> bool {
|
||||
!env.clock().is_elapsed(&self.needs_up)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "with_ctap1")]
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::env::test::TestEnv;
|
||||
|
||||
fn big_positive() -> usize {
|
||||
1000000
|
||||
}
|
||||
|
||||
fn grant_up_when_needed(env: &mut TestEnv) {
|
||||
let mut u2f_state = U2fUserPresenceState::new();
|
||||
assert!(!u2f_state.consume_up(env));
|
||||
assert!(u2f_state.is_up_needed(env));
|
||||
u2f_state.grant_up(env);
|
||||
assert!(u2f_state.consume_up(env));
|
||||
assert!(!u2f_state.consume_up(env));
|
||||
}
|
||||
|
||||
fn need_up_timeout(env: &mut TestEnv) {
|
||||
let mut u2f_state = U2fUserPresenceState::new();
|
||||
assert!(!u2f_state.consume_up(env));
|
||||
assert!(u2f_state.is_up_needed(env));
|
||||
env.clock().advance(U2F_UP_PROMPT_TIMEOUT_MS);
|
||||
// The timeout excludes equality, so it should be over at this instant.
|
||||
assert!(!u2f_state.is_up_needed(env));
|
||||
}
|
||||
|
||||
fn grant_up_timeout(env: &mut TestEnv) {
|
||||
let mut u2f_state = U2fUserPresenceState::new();
|
||||
assert!(!u2f_state.consume_up(env));
|
||||
assert!(u2f_state.is_up_needed(env));
|
||||
u2f_state.grant_up(env);
|
||||
env.clock().advance(TOUCH_TIMEOUT_MS);
|
||||
// The timeout excludes equality, so it should be over at this instant.
|
||||
assert!(!u2f_state.consume_up(env));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_grant_up_timeout() {
|
||||
let mut env = TestEnv::new();
|
||||
grant_up_timeout(&mut env);
|
||||
env.clock().advance(big_positive());
|
||||
grant_up_timeout(&mut env);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_need_up_timeout() {
|
||||
let mut env = TestEnv::new();
|
||||
need_up_timeout(&mut env);
|
||||
env.clock().advance(big_positive());
|
||||
need_up_timeout(&mut env);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_grant_up_when_needed() {
|
||||
let mut env = TestEnv::new();
|
||||
grant_up_when_needed(&mut env);
|
||||
env.clock().advance(big_positive());
|
||||
grant_up_when_needed(&mut env);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_grant_up_without_need() {
|
||||
let mut env = TestEnv::new();
|
||||
let mut u2f_state = U2fUserPresenceState::new();
|
||||
u2f_state.grant_up(&mut env);
|
||||
assert!(!u2f_state.is_up_needed(&mut env));
|
||||
assert!(!u2f_state.consume_up(&mut env));
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::clock::CtapInstant;
|
||||
use crate::ctap::hid::{
|
||||
CtapHid, CtapHidCommand, CtapHidError, HidPacket, HidPacketIterator, Message,
|
||||
};
|
||||
@@ -22,33 +21,32 @@ use crate::env::Env;
|
||||
/// Implements the non-standard command processing for HID.
|
||||
///
|
||||
/// Outside of the pure HID commands like INIT, only PING and CBOR commands are allowed.
|
||||
pub struct VendorHid {
|
||||
hid: CtapHid,
|
||||
pub struct VendorHid<E: Env> {
|
||||
hid: CtapHid<E>,
|
||||
}
|
||||
|
||||
impl VendorHid {
|
||||
impl<E: Env> VendorHid<E> {
|
||||
/// Instantiates a HID handler for CTAP1, CTAP2 and Wink.
|
||||
pub fn new() -> Self {
|
||||
let hid = CtapHid::new(CtapHid::CAPABILITY_CBOR | CtapHid::CAPABILITY_NMSG);
|
||||
let hid = CtapHid::<E>::new(CtapHid::<E>::CAPABILITY_CBOR | CtapHid::<E>::CAPABILITY_NMSG);
|
||||
VendorHid { hid }
|
||||
}
|
||||
|
||||
/// Processes an incoming USB HID packet, and returns an iterator for all outgoing packets.
|
||||
pub fn process_hid_packet(
|
||||
&mut self,
|
||||
env: &mut impl Env,
|
||||
env: &mut E,
|
||||
packet: &HidPacket,
|
||||
now: CtapInstant,
|
||||
ctap_state: &mut CtapState,
|
||||
ctap_state: &mut CtapState<E>,
|
||||
) -> HidPacketIterator {
|
||||
if let Some(message) = self.hid.parse_packet(env, packet, now) {
|
||||
let processed_message = self.process_message(env, message, now, ctap_state);
|
||||
if let Some(message) = self.hid.parse_packet(env, packet) {
|
||||
let processed_message = self.process_message(env, message, ctap_state);
|
||||
debug_ctap!(
|
||||
env,
|
||||
"Sending message through the second usage page: {:02x?}",
|
||||
processed_message
|
||||
);
|
||||
CtapHid::split_message(processed_message)
|
||||
CtapHid::<E>::split_message(processed_message)
|
||||
} else {
|
||||
HidPacketIterator::none()
|
||||
}
|
||||
@@ -57,19 +55,18 @@ impl VendorHid {
|
||||
/// Processes a message's commands that affect the protocol outside HID.
|
||||
pub fn process_message(
|
||||
&mut self,
|
||||
env: &mut impl Env,
|
||||
env: &mut E,
|
||||
message: Message,
|
||||
now: CtapInstant,
|
||||
ctap_state: &mut CtapState,
|
||||
ctap_state: &mut CtapState<E>,
|
||||
) -> Message {
|
||||
let cid = message.cid;
|
||||
match message.cmd {
|
||||
// There are no custom CTAP1 commands.
|
||||
CtapHidCommand::Msg => CtapHid::error_message(cid, CtapHidError::InvalidCmd),
|
||||
CtapHidCommand::Msg => CtapHid::<E>::error_message(cid, CtapHidError::InvalidCmd),
|
||||
// The CTAP2 processing function multiplexes internally.
|
||||
CtapHidCommand::Cbor => {
|
||||
let response =
|
||||
ctap_state.process_command(env, &message.payload, Channel::VendorHid(cid), now);
|
||||
ctap_state.process_command(env, &message.payload, Channel::VendorHid(cid));
|
||||
Message {
|
||||
cid,
|
||||
cmd: CtapHidCommand::Cbor,
|
||||
@@ -77,7 +74,7 @@ impl VendorHid {
|
||||
}
|
||||
}
|
||||
// Call Wink over the main HID.
|
||||
CtapHidCommand::Wink => CtapHid::error_message(cid, CtapHidError::InvalidCmd),
|
||||
CtapHidCommand::Wink => CtapHid::<E>::error_message(cid, CtapHidError::InvalidCmd),
|
||||
// All other commands have already been processed, keep them as is.
|
||||
_ => message,
|
||||
}
|
||||
@@ -90,27 +87,22 @@ mod test {
|
||||
use crate::ctap::hid::ChannelID;
|
||||
use crate::env::test::TestEnv;
|
||||
|
||||
fn new_initialized() -> (VendorHid, ChannelID) {
|
||||
fn new_initialized() -> (VendorHid<TestEnv>, ChannelID) {
|
||||
let (hid, cid) = CtapHid::new_initialized();
|
||||
(VendorHid { hid }, cid)
|
||||
(VendorHid::<TestEnv> { hid }, cid)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_hid_packet() {
|
||||
let mut env = TestEnv::new();
|
||||
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
||||
let mut ctap_state = CtapState::<TestEnv>::new(&mut env);
|
||||
let (mut vendor_hid, cid) = new_initialized();
|
||||
|
||||
let mut ping_packet = [0x00; 64];
|
||||
ping_packet[..4].copy_from_slice(&cid);
|
||||
ping_packet[4..9].copy_from_slice(&[0x81, 0x00, 0x02, 0x99, 0x99]);
|
||||
|
||||
let mut response = vendor_hid.process_hid_packet(
|
||||
&mut env,
|
||||
&ping_packet,
|
||||
CtapInstant::new(0),
|
||||
&mut ctap_state,
|
||||
);
|
||||
let mut response = vendor_hid.process_hid_packet(&mut env, &ping_packet, &mut ctap_state);
|
||||
assert_eq!(response.next(), Some(ping_packet));
|
||||
assert_eq!(response.next(), None);
|
||||
}
|
||||
@@ -118,26 +110,21 @@ mod test {
|
||||
#[test]
|
||||
fn test_process_hid_packet_empty() {
|
||||
let mut env = TestEnv::new();
|
||||
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
||||
let mut ctap_state = CtapState::<TestEnv>::new(&mut env);
|
||||
let (mut vendor_hid, cid) = new_initialized();
|
||||
|
||||
let mut cancel_packet = [0x00; 64];
|
||||
cancel_packet[..4].copy_from_slice(&cid);
|
||||
cancel_packet[4..7].copy_from_slice(&[0x91, 0x00, 0x00]);
|
||||
|
||||
let mut response = vendor_hid.process_hid_packet(
|
||||
&mut env,
|
||||
&cancel_packet,
|
||||
CtapInstant::new(0),
|
||||
&mut ctap_state,
|
||||
);
|
||||
let mut response = vendor_hid.process_hid_packet(&mut env, &cancel_packet, &mut ctap_state);
|
||||
assert_eq!(response.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blocked_commands() {
|
||||
let mut env = TestEnv::new();
|
||||
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
||||
let mut ctap_state = CtapState::<TestEnv>::new(&mut env);
|
||||
let (mut vendor_hid, cid) = new_initialized();
|
||||
|
||||
// Usually longer, but we don't parse them anyway.
|
||||
@@ -153,21 +140,11 @@ mod test {
|
||||
error_packet[..4].copy_from_slice(&cid);
|
||||
error_packet[4..8].copy_from_slice(&[0xBF, 0x00, 0x01, 0x01]);
|
||||
|
||||
let mut response = vendor_hid.process_hid_packet(
|
||||
&mut env,
|
||||
&msg_packet,
|
||||
CtapInstant::new(0),
|
||||
&mut ctap_state,
|
||||
);
|
||||
let mut response = vendor_hid.process_hid_packet(&mut env, &msg_packet, &mut ctap_state);
|
||||
assert_eq!(response.next(), Some(error_packet));
|
||||
assert_eq!(response.next(), None);
|
||||
|
||||
let mut response = vendor_hid.process_hid_packet(
|
||||
&mut env,
|
||||
&wink_packet,
|
||||
CtapInstant::new(0),
|
||||
&mut ctap_state,
|
||||
);
|
||||
let mut response = vendor_hid.process_hid_packet(&mut env, &wink_packet, &mut ctap_state);
|
||||
assert_eq!(response.next(), Some(error_packet));
|
||||
assert_eq!(response.next(), None);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user