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:
73
Cargo.lock
generated
73
Cargo.lock
generated
@@ -105,7 +105,6 @@ dependencies = [
|
|||||||
"byteorder",
|
"byteorder",
|
||||||
"crypto",
|
"crypto",
|
||||||
"ed25519-compact",
|
"ed25519-compact",
|
||||||
"embedded-time",
|
|
||||||
"enum-iterator",
|
"enum-iterator",
|
||||||
"lang_items",
|
"lang_items",
|
||||||
"libtock_core",
|
"libtock_core",
|
||||||
@@ -136,15 +135,6 @@ version = "1.0.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bee9df587982575886a8682edcee11877894349a805f25629c27f63abe3e9ae8"
|
checksum = "bee9df587982575886a8682edcee11877894349a805f25629c27f63abe3e9ae8"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embedded-time"
|
|
||||||
version = "0.12.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d7a4b4d10ac48d08bfe3db7688c402baadb244721f30a77ce360bd24c3dffe58"
|
|
||||||
dependencies = [
|
|
||||||
"num",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "enum-iterator"
|
name = "enum-iterator"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
@@ -277,69 +267,6 @@ version = "2.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f"
|
|
||||||
dependencies = [
|
|
||||||
"num-complex",
|
|
||||||
"num-integer",
|
|
||||||
"num-iter",
|
|
||||||
"num-rational",
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-complex"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5"
|
|
||||||
dependencies = [
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-integer"
|
|
||||||
version = "0.1.45"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg 1.1.0",
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-iter"
|
|
||||||
version = "0.1.43"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg 1.1.0",
|
|
||||||
"num-integer",
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-rational"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg 1.1.0",
|
|
||||||
"num-integer",
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-traits"
|
|
||||||
version = "0.2.15"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg 1.1.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.14.0"
|
version = "1.14.0"
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ persistent_store = { path = "libraries/persistent_store" }
|
|||||||
byteorder = { version = "1", default-features = false }
|
byteorder = { version = "1", default-features = false }
|
||||||
arrayref = "0.3.6"
|
arrayref = "0.3.6"
|
||||||
subtle = { version = "2.2", default-features = false, features = ["nightly"] }
|
subtle = { version = "2.2", default-features = false, features = ["nightly"] }
|
||||||
embedded-time = "0.12.1"
|
|
||||||
arbitrary = { version = "0.4.7", features = ["derive"], optional = true }
|
arbitrary = { version = "0.4.7", features = ["derive"], optional = true }
|
||||||
rand = { version = "0.8.4", optional = true }
|
rand = { version = "0.8.4", optional = true }
|
||||||
ed25519-compact = { version = "1", default-features = false, optional = true }
|
ed25519-compact = { version = "1", default-features = false, optional = true }
|
||||||
|
|||||||
1
fuzz/Cargo.lock
generated
1
fuzz/Cargo.lock
generated
@@ -104,7 +104,6 @@ dependencies = [
|
|||||||
"arrayref",
|
"arrayref",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"crypto",
|
"crypto",
|
||||||
"embedded-time",
|
|
||||||
"lang_items",
|
"lang_items",
|
||||||
"libtock_core",
|
"libtock_core",
|
||||||
"libtock_drivers",
|
"libtock_drivers",
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ use arbitrary::{Arbitrary, Unstructured};
|
|||||||
use arrayref::array_ref;
|
use arrayref::array_ref;
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
use ctap2::api::customization::is_valid;
|
use ctap2::api::customization::is_valid;
|
||||||
use ctap2::clock::CtapInstant;
|
|
||||||
use ctap2::ctap::command::{
|
use ctap2::ctap::command::{
|
||||||
AuthenticatorClientPinParameters, AuthenticatorGetAssertionParameters,
|
AuthenticatorClientPinParameters, AuthenticatorGetAssertionParameters,
|
||||||
AuthenticatorMakeCredentialParameters, Command,
|
AuthenticatorMakeCredentialParameters, Command,
|
||||||
@@ -89,12 +88,8 @@ fn initialize(ctap: &mut Ctap<TestEnv>) -> ChannelID {
|
|||||||
let mut assembler_reply = MessageAssembler::new();
|
let mut assembler_reply = MessageAssembler::new();
|
||||||
let mut result_cid: ChannelID = Default::default();
|
let mut result_cid: ChannelID = Default::default();
|
||||||
for pkt_request in HidPacketIterator::new(message).unwrap() {
|
for pkt_request in HidPacketIterator::new(message).unwrap() {
|
||||||
for pkt_reply in
|
for pkt_reply in ctap.process_hid_packet(&pkt_request, Transport::MainHid) {
|
||||||
ctap.process_hid_packet(&pkt_request, Transport::MainHid, CtapInstant::new(0))
|
if let Ok(Some(result)) = assembler_reply.parse_packet(ctap.env(), &pkt_reply) {
|
||||||
{
|
|
||||||
if let Ok(Some(result)) =
|
|
||||||
assembler_reply.parse_packet(ctap.env(), &pkt_reply, CtapInstant::new(0))
|
|
||||||
{
|
|
||||||
result_cid.copy_from_slice(&result.payload[8..12]);
|
result_cid.copy_from_slice(&result.payload[8..12]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -131,11 +126,9 @@ fn process_message(data: &[u8], ctap: &mut Ctap<TestEnv>) {
|
|||||||
if let Some(hid_packet_iterator) = HidPacketIterator::new(message) {
|
if let Some(hid_packet_iterator) = HidPacketIterator::new(message) {
|
||||||
let mut assembler_reply = MessageAssembler::new();
|
let mut assembler_reply = MessageAssembler::new();
|
||||||
for pkt_request in hid_packet_iterator {
|
for pkt_request in hid_packet_iterator {
|
||||||
for pkt_reply in
|
for pkt_reply in ctap.process_hid_packet(&pkt_request, Transport::MainHid) {
|
||||||
ctap.process_hid_packet(&pkt_request, Transport::MainHid, CtapInstant::new(0))
|
|
||||||
{
|
|
||||||
// Only checks for assembling crashes, not for semantics.
|
// Only checks for assembling crashes, not for semantics.
|
||||||
let _ = assembler_reply.parse_packet(ctap.env(), &pkt_reply, CtapInstant::new(0));
|
let _ = assembler_reply.parse_packet(ctap.env(), &pkt_reply);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -152,7 +145,7 @@ pub fn process_ctap_any_type(data: &[u8]) -> arbitrary::Result<()> {
|
|||||||
|
|
||||||
let data = unstructured.take_rest();
|
let data = unstructured.take_rest();
|
||||||
// Initialize ctap state and hid and get the allocated cid.
|
// Initialize ctap state and hid and get the allocated cid.
|
||||||
let mut ctap = Ctap::new(env, CtapInstant::new(0));
|
let mut ctap = Ctap::new(env);
|
||||||
let cid = initialize(&mut ctap);
|
let cid = initialize(&mut ctap);
|
||||||
// Wrap input as message with the allocated cid.
|
// Wrap input as message with the allocated cid.
|
||||||
let mut command = cid.to_vec();
|
let mut command = cid.to_vec();
|
||||||
@@ -179,7 +172,7 @@ fn setup_customization(
|
|||||||
|
|
||||||
fn setup_state(
|
fn setup_state(
|
||||||
unstructured: &mut Unstructured,
|
unstructured: &mut Unstructured,
|
||||||
state: &mut CtapState,
|
state: &mut CtapState<TestEnv>,
|
||||||
env: &mut TestEnv,
|
env: &mut TestEnv,
|
||||||
) -> FuzzResult<()> {
|
) -> FuzzResult<()> {
|
||||||
if bool::arbitrary(unstructured)? {
|
if bool::arbitrary(unstructured)? {
|
||||||
@@ -202,7 +195,7 @@ pub fn process_ctap_specific_type(data: &[u8], input_type: InputType) -> arbitra
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
// Initialize ctap state and hid and get the allocated cid.
|
// Initialize ctap state and hid and get the allocated cid.
|
||||||
let mut ctap = Ctap::new(env, CtapInstant::new(0));
|
let mut ctap = Ctap::new(env);
|
||||||
let cid = initialize(&mut ctap);
|
let cid = initialize(&mut ctap);
|
||||||
// Wrap input as message with allocated cid and command type.
|
// Wrap input as message with allocated cid and command type.
|
||||||
let mut command = cid.to_vec();
|
let mut command = cid.to_vec();
|
||||||
@@ -232,7 +225,7 @@ pub fn process_ctap_structured(data: &[u8], input_type: InputType) -> FuzzResult
|
|||||||
env.rng().seed_from_u64(u64::arbitrary(unstructured)?);
|
env.rng().seed_from_u64(u64::arbitrary(unstructured)?);
|
||||||
setup_customization(unstructured, env.customization_mut())?;
|
setup_customization(unstructured, env.customization_mut())?;
|
||||||
|
|
||||||
let mut state = CtapState::new(&mut env, CtapInstant::new(0));
|
let mut state = CtapState::new(&mut env);
|
||||||
setup_state(unstructured, &mut state, &mut env)?;
|
setup_state(unstructured, &mut state, &mut env)?;
|
||||||
|
|
||||||
let command = match input_type {
|
let command = match input_type {
|
||||||
@@ -255,7 +248,6 @@ pub fn process_ctap_structured(data: &[u8], input_type: InputType) -> FuzzResult
|
|||||||
&mut env,
|
&mut env,
|
||||||
command,
|
command,
|
||||||
Channel::MainHid(ChannelID::arbitrary(unstructured)?),
|
Channel::MainHid(ChannelID::arbitrary(unstructured)?),
|
||||||
CtapInstant::new(0),
|
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
@@ -276,13 +268,10 @@ pub fn split_assemble_hid_packets(data: &[u8]) -> arbitrary::Result<()> {
|
|||||||
let packets: Vec<HidPacket> = hid_packet_iterator.collect();
|
let packets: Vec<HidPacket> = hid_packet_iterator.collect();
|
||||||
if let Some((last_packet, first_packets)) = packets.split_last() {
|
if let Some((last_packet, first_packets)) = packets.split_last() {
|
||||||
for packet in first_packets {
|
for packet in first_packets {
|
||||||
assert_eq!(
|
assert_eq!(assembler.parse_packet(&mut env, packet), Ok(None));
|
||||||
assembler.parse_packet(&mut env, packet, CtapInstant::new(0)),
|
|
||||||
Ok(None)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
assembler.parse_packet(&mut env, last_packet, CtapInstant::new(0)),
|
assembler.parse_packet(&mut env, last_packet),
|
||||||
Ok(Some(message))
|
Ok(Some(message))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
39
src/api/clock.rs
Normal file
39
src/api/clock.rs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// Copyright 2022-2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
pub trait Clock {
|
||||||
|
/// Stores data for the clock to recognize if this timer is elapsed or not.
|
||||||
|
///
|
||||||
|
/// The Clock does not keep track of the timers it creates. Therefore, they should not wrap
|
||||||
|
/// unexpectedly. A timer that is elapsed may never return to a non-elapsed state.
|
||||||
|
///
|
||||||
|
/// A default Timer should return `true` when checked with `is_elapsed`.
|
||||||
|
type Timer: Default;
|
||||||
|
|
||||||
|
/// Creates a new timer that expires after the given time in ms.
|
||||||
|
fn make_timer(&mut self, milliseconds: usize) -> Self::Timer;
|
||||||
|
|
||||||
|
/// Checks whether a given timer is expired.
|
||||||
|
///
|
||||||
|
/// Until a timer expires, this function consistently returns false. Once it expires, this
|
||||||
|
/// function consistently returns true. In particular, it is valid to continue calling this
|
||||||
|
/// function after the first time it returns true.
|
||||||
|
fn is_elapsed(&mut self, timer: &Self::Timer) -> bool;
|
||||||
|
|
||||||
|
/// Timestamp in microseconds.
|
||||||
|
///
|
||||||
|
/// Normal operation only needs relative time, absolute timestamps are useful for debugging.
|
||||||
|
#[cfg(feature = "debug_ctap")]
|
||||||
|
fn timestamp_us(&mut self) -> usize;
|
||||||
|
}
|
||||||
@@ -12,8 +12,6 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::clock::ClockInt;
|
|
||||||
use embedded_time::duration::Milliseconds;
|
|
||||||
use libtock_drivers::usb_ctap_hid::UsbEndpoint;
|
use libtock_drivers::usb_ctap_hid::UsbEndpoint;
|
||||||
|
|
||||||
pub enum SendOrRecvStatus {
|
pub enum SendOrRecvStatus {
|
||||||
@@ -27,9 +25,5 @@ pub struct SendOrRecvError;
|
|||||||
pub type SendOrRecvResult = Result<SendOrRecvStatus, SendOrRecvError>;
|
pub type SendOrRecvResult = Result<SendOrRecvStatus, SendOrRecvError>;
|
||||||
|
|
||||||
pub trait HidConnection {
|
pub trait HidConnection {
|
||||||
fn send_and_maybe_recv(
|
fn send_and_maybe_recv(&mut self, buf: &mut [u8; 64], timeout_ms: usize) -> SendOrRecvResult;
|
||||||
&mut self,
|
|
||||||
buf: &mut [u8; 64],
|
|
||||||
timeout: Milliseconds<ClockInt>,
|
|
||||||
) -> SendOrRecvResult;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
//! by a trait. This module gathers the API of those components.
|
//! by a trait. This module gathers the API of those components.
|
||||||
|
|
||||||
pub mod attestation_store;
|
pub mod attestation_store;
|
||||||
|
pub mod clock;
|
||||||
pub mod connection;
|
pub mod connection;
|
||||||
pub mod customization;
|
pub mod customization;
|
||||||
pub mod firmware_protection;
|
pub mod firmware_protection;
|
||||||
|
|||||||
@@ -12,9 +12,6 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::clock::ClockInt;
|
|
||||||
use embedded_time::duration::Milliseconds;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum UserPresenceError {
|
pub enum UserPresenceError {
|
||||||
/// User explicitly declined user presence check.
|
/// User explicitly declined user presence check.
|
||||||
@@ -36,7 +33,7 @@ pub trait UserPresence {
|
|||||||
/// Waits until user presence is confirmed, rejected, or the given timeout expires.
|
/// Waits until user presence is confirmed, rejected, or the given timeout expires.
|
||||||
///
|
///
|
||||||
/// Must be called between calls to [`Self::check_init`] and [`Self::check_complete`].
|
/// Must be called between calls to [`Self::check_init`] and [`Self::check_complete`].
|
||||||
fn wait_with_timeout(&mut self, timeout: Milliseconds<ClockInt>) -> UserPresenceResult;
|
fn wait_with_timeout(&mut self, timeout_ms: usize) -> UserPresenceResult;
|
||||||
|
|
||||||
/// Finalizes a user presence check.
|
/// Finalizes a user presence check.
|
||||||
///
|
///
|
||||||
|
|||||||
170
src/clock.rs
170
src/clock.rs
@@ -1,170 +0,0 @@
|
|||||||
// Copyright 2022 Google LLC
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
use alloc::fmt;
|
|
||||||
use embedded_time::duration::Milliseconds;
|
|
||||||
pub use embedded_time::Clock;
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
use libtock_drivers::result::FlexUnwrap;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
pub struct LibtockClock<const CLOCK_FREQUENCY: u32>(libtock_drivers::timer::Timer<'static>);
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
impl<const CLOCK_FREQUENCY: u32> LibtockClock<CLOCK_FREQUENCY> {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let boxed_cb = alloc::boxed::Box::new(libtock_drivers::timer::with_callback(|_, _| {}));
|
|
||||||
let timer = alloc::boxed::Box::leak(boxed_cb).init().flex_unwrap();
|
|
||||||
Self(timer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
impl<const CLOCK_FREQUENCY: u32> fmt::Debug for LibtockClock<CLOCK_FREQUENCY> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("LibtockClock")
|
|
||||||
.field("CLOCK_FREQUENCY", &CLOCK_FREQUENCY)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const KEEPALIVE_DELAY_MS: ClockInt = 100;
|
|
||||||
pub const KEEPALIVE_DELAY: Milliseconds<ClockInt> = Milliseconds(KEEPALIVE_DELAY_MS);
|
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
|
||||||
pub type ClockInt = u32;
|
|
||||||
#[cfg(target_pointer_width = "64")]
|
|
||||||
pub type ClockInt = u64;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
impl<const CLOCK_FREQUENCY: u32> embedded_time::Clock for LibtockClock<CLOCK_FREQUENCY> {
|
|
||||||
// TODO: Implement and use a 24-bits TimeInt for Nordic
|
|
||||||
type T = ClockInt;
|
|
||||||
|
|
||||||
const SCALING_FACTOR: embedded_time::fraction::Fraction =
|
|
||||||
<embedded_time::fraction::Fraction>::new(1, CLOCK_FREQUENCY);
|
|
||||||
|
|
||||||
fn try_now(&self) -> Result<embedded_time::Instant<Self>, embedded_time::clock::Error> {
|
|
||||||
let timer = &self.0;
|
|
||||||
let now = timer.get_current_clock().flex_unwrap();
|
|
||||||
Ok(embedded_time::Instant::new(now.num_ticks() as Self::T))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
pub type CtapClock = LibtockClock<32768>;
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub type CtapClock = TestClock;
|
|
||||||
|
|
||||||
pub fn new_clock() -> CtapClock {
|
|
||||||
CtapClock::new()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type CtapInstant = embedded_time::Instant<CtapClock>;
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub const TEST_CLOCK_FREQUENCY_HZ: u32 = 32768;
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
#[derive(Default, Clone, Copy, Debug)]
|
|
||||||
pub struct TestClock;
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
impl TestClock {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
TestClock
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
impl embedded_time::Clock for TestClock {
|
|
||||||
type T = u64;
|
|
||||||
const SCALING_FACTOR: embedded_time::fraction::Fraction =
|
|
||||||
<embedded_time::fraction::Fraction>::new(1, TEST_CLOCK_FREQUENCY_HZ);
|
|
||||||
|
|
||||||
fn try_now(&self) -> Result<embedded_time::Instant<Self>, embedded_time::clock::Error> {
|
|
||||||
Ok(embedded_time::Instant::new(0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
use embedded_time::duration::{Milliseconds, Seconds};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_checked_add() {
|
|
||||||
let now = CtapInstant::new(0);
|
|
||||||
assert_eq!(
|
|
||||||
now.checked_add(Seconds::new(1 as ClockInt)),
|
|
||||||
Some(CtapInstant::new(TEST_CLOCK_FREQUENCY_HZ as ClockInt))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
now.checked_add(Seconds::new(1 as ClockInt)),
|
|
||||||
now.checked_add(Milliseconds::new(1000 as ClockInt))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_checked_add_overflow() {
|
|
||||||
assert_eq!(
|
|
||||||
CtapInstant::new(u64::MAX).checked_add(Seconds::new(1 as ClockInt)),
|
|
||||||
Some(CtapInstant::new(TEST_CLOCK_FREQUENCY_HZ as u64 - 1))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_checked_add_error() {
|
|
||||||
assert!(CtapInstant::new(0)
|
|
||||||
.checked_add(Seconds::new(u64::MAX / TEST_CLOCK_FREQUENCY_HZ as ClockInt))
|
|
||||||
.is_none());
|
|
||||||
assert!(CtapInstant::new(0)
|
|
||||||
.checked_add(Seconds::new(
|
|
||||||
u64::MAX / TEST_CLOCK_FREQUENCY_HZ as ClockInt / 2
|
|
||||||
))
|
|
||||||
.is_some());
|
|
||||||
assert!(CtapInstant::new(0)
|
|
||||||
.checked_add(Seconds::new(
|
|
||||||
u64::MAX / TEST_CLOCK_FREQUENCY_HZ as ClockInt / 2 + 1
|
|
||||||
))
|
|
||||||
.is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_duration_since() {
|
|
||||||
let early = CtapInstant::new(0);
|
|
||||||
let later = CtapInstant::new(1000);
|
|
||||||
assert_eq!(
|
|
||||||
later.checked_duration_since(&early).unwrap().integer(),
|
|
||||||
1000
|
|
||||||
);
|
|
||||||
assert_eq!(early.checked_duration_since(&later), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_duration_since_overflow() {
|
|
||||||
let early = CtapInstant::new(u64::MAX);
|
|
||||||
let later = CtapInstant::new(1000);
|
|
||||||
assert_eq!(
|
|
||||||
later.checked_duration_since(&early).unwrap().integer(),
|
|
||||||
1001
|
|
||||||
);
|
|
||||||
assert_eq!(early.checked_duration_since(&later), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
|
||||||
fn add_panic() {
|
|
||||||
let _ =
|
|
||||||
CtapInstant::new(0) + Seconds(u64::MAX / TEST_CLOCK_FREQUENCY_HZ as ClockInt / 2 + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,7 +12,6 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use super::super::clock::CtapInstant;
|
|
||||||
use super::command::AuthenticatorClientPinParameters;
|
use super::command::AuthenticatorClientPinParameters;
|
||||||
use super::data_formats::{
|
use super::data_formats::{
|
||||||
ok_or_missing, ClientPinSubCommand, CoseKey, GetAssertionHmacSecretInput, PinUvAuthProtocol,
|
ok_or_missing, ClientPinSubCommand, CoseKey, GetAssertionHmacSecretInput, PinUvAuthProtocol,
|
||||||
@@ -33,7 +32,6 @@ use crypto::sha256::Sha256;
|
|||||||
use crypto::Hash256;
|
use crypto::Hash256;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use enum_iterator::IntoEnumIterator;
|
use enum_iterator::IntoEnumIterator;
|
||||||
use rng256::Rng256;
|
|
||||||
use subtle::ConstantTimeEq;
|
use subtle::ConstantTimeEq;
|
||||||
|
|
||||||
/// The prefix length of the PIN hash that is stored and compared.
|
/// The prefix length of the PIN hash that is stored and compared.
|
||||||
@@ -106,18 +104,18 @@ pub enum PinPermission {
|
|||||||
AuthenticatorConfiguration = 0x20,
|
AuthenticatorConfiguration = 0x20,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ClientPin {
|
pub struct ClientPin<E: Env> {
|
||||||
pin_protocol_v1: PinProtocol,
|
pin_protocol_v1: PinProtocol,
|
||||||
pin_protocol_v2: PinProtocol,
|
pin_protocol_v2: PinProtocol,
|
||||||
consecutive_pin_mismatches: u8,
|
consecutive_pin_mismatches: u8,
|
||||||
pin_uv_auth_token_state: PinUvAuthTokenState,
|
pin_uv_auth_token_state: PinUvAuthTokenState<E>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientPin {
|
impl<E: Env> ClientPin<E> {
|
||||||
pub fn new(rng: &mut impl Rng256) -> ClientPin {
|
pub fn new(env: &mut E) -> Self {
|
||||||
ClientPin {
|
ClientPin {
|
||||||
pin_protocol_v1: PinProtocol::new(rng),
|
pin_protocol_v1: PinProtocol::new(env.rng()),
|
||||||
pin_protocol_v2: PinProtocol::new(rng),
|
pin_protocol_v2: PinProtocol::new(env.rng()),
|
||||||
consecutive_pin_mismatches: 0,
|
consecutive_pin_mismatches: 0,
|
||||||
pin_uv_auth_token_state: PinUvAuthTokenState::new(),
|
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.
|
/// Also, in case of failure, the key agreement key is randomly reset.
|
||||||
fn verify_pin_hash_enc(
|
fn verify_pin_hash_enc(
|
||||||
&mut self,
|
&mut self,
|
||||||
env: &mut impl Env,
|
env: &mut E,
|
||||||
pin_uv_auth_protocol: PinUvAuthProtocol,
|
pin_uv_auth_protocol: PinUvAuthProtocol,
|
||||||
shared_secret: &dyn SharedSecret,
|
shared_secret: &dyn SharedSecret,
|
||||||
pin_hash_enc: Vec<u8>,
|
pin_hash_enc: Vec<u8>,
|
||||||
@@ -197,7 +195,7 @@ impl ClientPin {
|
|||||||
|
|
||||||
fn process_get_pin_retries(
|
fn process_get_pin_retries(
|
||||||
&self,
|
&self,
|
||||||
env: &mut impl Env,
|
env: &mut E,
|
||||||
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
|
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
|
||||||
Ok(AuthenticatorClientPinResponse {
|
Ok(AuthenticatorClientPinResponse {
|
||||||
key_agreement: None,
|
key_agreement: None,
|
||||||
@@ -225,7 +223,7 @@ impl ClientPin {
|
|||||||
|
|
||||||
fn process_set_pin(
|
fn process_set_pin(
|
||||||
&mut self,
|
&mut self,
|
||||||
env: &mut impl Env,
|
env: &mut E,
|
||||||
client_pin_params: AuthenticatorClientPinParameters,
|
client_pin_params: AuthenticatorClientPinParameters,
|
||||||
) -> Result<(), Ctap2StatusCode> {
|
) -> Result<(), Ctap2StatusCode> {
|
||||||
let AuthenticatorClientPinParameters {
|
let AuthenticatorClientPinParameters {
|
||||||
@@ -252,7 +250,7 @@ impl ClientPin {
|
|||||||
|
|
||||||
fn process_change_pin(
|
fn process_change_pin(
|
||||||
&mut self,
|
&mut self,
|
||||||
env: &mut impl Env,
|
env: &mut E,
|
||||||
client_pin_params: AuthenticatorClientPinParameters,
|
client_pin_params: AuthenticatorClientPinParameters,
|
||||||
) -> Result<(), Ctap2StatusCode> {
|
) -> Result<(), Ctap2StatusCode> {
|
||||||
let AuthenticatorClientPinParameters {
|
let AuthenticatorClientPinParameters {
|
||||||
@@ -290,9 +288,8 @@ impl ClientPin {
|
|||||||
|
|
||||||
fn process_get_pin_token(
|
fn process_get_pin_token(
|
||||||
&mut self,
|
&mut self,
|
||||||
env: &mut impl Env,
|
env: &mut E,
|
||||||
client_pin_params: AuthenticatorClientPinParameters,
|
client_pin_params: AuthenticatorClientPinParameters,
|
||||||
now: CtapInstant,
|
|
||||||
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
|
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
|
||||||
let AuthenticatorClientPinParameters {
|
let AuthenticatorClientPinParameters {
|
||||||
pin_uv_auth_protocol,
|
pin_uv_auth_protocol,
|
||||||
@@ -325,7 +322,7 @@ impl ClientPin {
|
|||||||
self.pin_protocol_v1.reset_pin_uv_auth_token(env.rng());
|
self.pin_protocol_v1.reset_pin_uv_auth_token(env.rng());
|
||||||
self.pin_protocol_v2.reset_pin_uv_auth_token(env.rng());
|
self.pin_protocol_v2.reset_pin_uv_auth_token(env.rng());
|
||||||
self.pin_uv_auth_token_state
|
self.pin_uv_auth_token_state
|
||||||
.begin_using_pin_uv_auth_token(now);
|
.begin_using_pin_uv_auth_token(env);
|
||||||
self.pin_uv_auth_token_state.set_default_permissions();
|
self.pin_uv_auth_token_state.set_default_permissions();
|
||||||
let pin_uv_auth_token = shared_secret.encrypt(
|
let pin_uv_auth_token = shared_secret.encrypt(
|
||||||
env.rng(),
|
env.rng(),
|
||||||
@@ -358,9 +355,8 @@ impl ClientPin {
|
|||||||
|
|
||||||
fn process_get_pin_uv_auth_token_using_pin_with_permissions(
|
fn process_get_pin_uv_auth_token_using_pin_with_permissions(
|
||||||
&mut self,
|
&mut self,
|
||||||
env: &mut impl Env,
|
env: &mut E,
|
||||||
mut client_pin_params: AuthenticatorClientPinParameters,
|
mut client_pin_params: AuthenticatorClientPinParameters,
|
||||||
now: CtapInstant,
|
|
||||||
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
|
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
|
||||||
// Mutating client_pin_params is just an optimization to move it into
|
// Mutating client_pin_params is just an optimization to move it into
|
||||||
// process_get_pin_token, without cloning permissions_rp_id here.
|
// process_get_pin_token, without cloning permissions_rp_id here.
|
||||||
@@ -376,7 +372,7 @@ impl ClientPin {
|
|||||||
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
|
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(permissions);
|
||||||
self.pin_uv_auth_token_state
|
self.pin_uv_auth_token_state
|
||||||
.set_permissions_rp_id(permissions_rp_id);
|
.set_permissions_rp_id(permissions_rp_id);
|
||||||
@@ -387,9 +383,8 @@ impl ClientPin {
|
|||||||
/// Processes the authenticatorClientPin command.
|
/// Processes the authenticatorClientPin command.
|
||||||
pub fn process_command(
|
pub fn process_command(
|
||||||
&mut self,
|
&mut self,
|
||||||
env: &mut impl Env,
|
env: &mut E,
|
||||||
client_pin_params: AuthenticatorClientPinParameters,
|
client_pin_params: AuthenticatorClientPinParameters,
|
||||||
now: CtapInstant,
|
|
||||||
) -> Result<ResponseData, Ctap2StatusCode> {
|
) -> Result<ResponseData, Ctap2StatusCode> {
|
||||||
if !env.customization().allows_pin_protocol_v1()
|
if !env.customization().allows_pin_protocol_v1()
|
||||||
&& client_pin_params.pin_uv_auth_protocol == PinUvAuthProtocol::V1
|
&& client_pin_params.pin_uv_auth_protocol == PinUvAuthProtocol::V1
|
||||||
@@ -410,7 +405,7 @@ impl ClientPin {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
ClientPinSubCommand::GetPinToken => {
|
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(
|
ClientPinSubCommand::GetPinUvAuthTokenUsingUvWithPermissions => Some(
|
||||||
self.process_get_pin_uv_auth_token_using_uv_with_permissions(client_pin_params)?,
|
self.process_get_pin_uv_auth_token_using_uv_with_permissions(client_pin_params)?,
|
||||||
@@ -420,7 +415,6 @@ impl ClientPin {
|
|||||||
self.process_get_pin_uv_auth_token_using_pin_with_permissions(
|
self.process_get_pin_uv_auth_token_using_pin_with_permissions(
|
||||||
env,
|
env,
|
||||||
client_pin_params,
|
client_pin_params,
|
||||||
now,
|
|
||||||
)?,
|
)?,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
@@ -447,11 +441,11 @@ impl ClientPin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Resets all held state.
|
/// Resets all held state.
|
||||||
pub fn reset(&mut self, rng: &mut impl Rng256) {
|
pub fn reset(&mut self, env: &mut E) {
|
||||||
self.pin_protocol_v1.regenerate(rng);
|
self.pin_protocol_v1.regenerate(env.rng());
|
||||||
self.pin_protocol_v1.reset_pin_uv_auth_token(rng);
|
self.pin_protocol_v1.reset_pin_uv_auth_token(env.rng());
|
||||||
self.pin_protocol_v2.regenerate(rng);
|
self.pin_protocol_v2.regenerate(env.rng());
|
||||||
self.pin_protocol_v2.reset_pin_uv_auth_token(rng);
|
self.pin_protocol_v2.reset_pin_uv_auth_token(env.rng());
|
||||||
self.consecutive_pin_mismatches = 0;
|
self.consecutive_pin_mismatches = 0;
|
||||||
self.pin_uv_auth_token_state.stop_using_pin_uv_auth_token();
|
self.pin_uv_auth_token_state.stop_using_pin_uv_auth_token();
|
||||||
}
|
}
|
||||||
@@ -466,7 +460,7 @@ impl ClientPin {
|
|||||||
/// 32 byte.
|
/// 32 byte.
|
||||||
pub fn process_hmac_secret(
|
pub fn process_hmac_secret(
|
||||||
&self,
|
&self,
|
||||||
rng: &mut impl Rng256,
|
env: &mut E,
|
||||||
hmac_secret_input: GetAssertionHmacSecretInput,
|
hmac_secret_input: GetAssertionHmacSecretInput,
|
||||||
cred_random: &[u8; 32],
|
cred_random: &[u8; 32],
|
||||||
) -> Result<Vec<u8>, Ctap2StatusCode> {
|
) -> Result<Vec<u8>, Ctap2StatusCode> {
|
||||||
@@ -490,7 +484,7 @@ impl ClientPin {
|
|||||||
let mut output2 = hmac_256::<Sha256>(cred_random, &decrypted_salts[32..]).to_vec();
|
let mut output2 = hmac_256::<Sha256>(cred_random, &decrypted_salts[32..]).to_vec();
|
||||||
output.append(&mut output2);
|
output.append(&mut output2);
|
||||||
}
|
}
|
||||||
shared_secret.encrypt(rng, &output)
|
shared_secret.encrypt(env.rng(), &output)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consumes flags and permissions related to the pinUvAuthToken.
|
/// Consumes flags and permissions related to the pinUvAuthToken.
|
||||||
@@ -501,9 +495,9 @@ impl ClientPin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the running timers, triggers timeout events.
|
/// 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
|
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.
|
/// Checks if user verification is cached for use of the pinUvAuthToken.
|
||||||
@@ -563,19 +557,19 @@ impl ClientPin {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn new_test(
|
pub fn new_test(
|
||||||
|
env: &mut E,
|
||||||
key_agreement_key: crypto::ecdh::SecKey,
|
key_agreement_key: crypto::ecdh::SecKey,
|
||||||
pin_uv_auth_token: [u8; PIN_TOKEN_LENGTH],
|
pin_uv_auth_token: [u8; PIN_TOKEN_LENGTH],
|
||||||
pin_uv_auth_protocol: PinUvAuthProtocol,
|
pin_uv_auth_protocol: PinUvAuthProtocol,
|
||||||
) -> ClientPin {
|
) -> Self {
|
||||||
let mut env = crate::env::test::TestEnv::new();
|
|
||||||
let (key_agreement_key_v1, key_agreement_key_v2) = match pin_uv_auth_protocol {
|
let (key_agreement_key_v1, key_agreement_key_v2) = match pin_uv_auth_protocol {
|
||||||
PinUvAuthProtocol::V1 => (key_agreement_key, crypto::ecdh::SecKey::gensk(env.rng())),
|
PinUvAuthProtocol::V1 => (key_agreement_key, crypto::ecdh::SecKey::gensk(env.rng())),
|
||||||
PinUvAuthProtocol::V2 => (crypto::ecdh::SecKey::gensk(env.rng()), key_agreement_key),
|
PinUvAuthProtocol::V2 => (crypto::ecdh::SecKey::gensk(env.rng()), key_agreement_key),
|
||||||
};
|
};
|
||||||
let mut pin_uv_auth_token_state = PinUvAuthTokenState::new();
|
let mut pin_uv_auth_token_state = PinUvAuthTokenState::new();
|
||||||
pin_uv_auth_token_state.set_permissions(0xFF);
|
pin_uv_auth_token_state.set_permissions(0xFF);
|
||||||
pin_uv_auth_token_state.begin_using_pin_uv_auth_token(CtapInstant::new(0));
|
pin_uv_auth_token_state.begin_using_pin_uv_auth_token(env);
|
||||||
ClientPin {
|
Self {
|
||||||
pin_protocol_v1: PinProtocol::new_test(key_agreement_key_v1, pin_uv_auth_token),
|
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),
|
pin_protocol_v2: PinProtocol::new_test(key_agreement_key_v2, pin_uv_auth_token),
|
||||||
consecutive_pin_mismatches: 0,
|
consecutive_pin_mismatches: 0,
|
||||||
@@ -590,7 +584,6 @@ mod test {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::env::test::TestEnv;
|
use crate::env::test::TestEnv;
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
use embedded_time::duration::Milliseconds;
|
|
||||||
|
|
||||||
/// Stores a PIN hash corresponding to the dummy PIN "1234".
|
/// Stores a PIN hash corresponding to the dummy PIN "1234".
|
||||||
fn set_standard_pin(env: &mut TestEnv) {
|
fn set_standard_pin(env: &mut TestEnv) {
|
||||||
@@ -618,14 +611,18 @@ mod test {
|
|||||||
/// should fail.
|
/// should fail.
|
||||||
fn create_client_pin_and_shared_secret(
|
fn create_client_pin_and_shared_secret(
|
||||||
pin_uv_auth_protocol: PinUvAuthProtocol,
|
pin_uv_auth_protocol: PinUvAuthProtocol,
|
||||||
) -> (ClientPin, Box<dyn SharedSecret>) {
|
) -> (ClientPin<TestEnv>, Box<dyn SharedSecret>) {
|
||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pk = key_agreement_key.genpk();
|
let pk = key_agreement_key.genpk();
|
||||||
let key_agreement = CoseKey::from(pk);
|
let key_agreement = CoseKey::from(pk);
|
||||||
let pin_uv_auth_token = [0x91; PIN_TOKEN_LENGTH];
|
let pin_uv_auth_token = [0x91; PIN_TOKEN_LENGTH];
|
||||||
let client_pin =
|
let client_pin = ClientPin::<TestEnv>::new_test(
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol);
|
&mut env,
|
||||||
|
key_agreement_key,
|
||||||
|
pin_uv_auth_token,
|
||||||
|
pin_uv_auth_protocol,
|
||||||
|
);
|
||||||
let shared_secret = client_pin
|
let shared_secret = client_pin
|
||||||
.get_pin_protocol(pin_uv_auth_protocol)
|
.get_pin_protocol(pin_uv_auth_protocol)
|
||||||
.decapsulate(key_agreement, pin_uv_auth_protocol)
|
.decapsulate(key_agreement, pin_uv_auth_protocol)
|
||||||
@@ -639,7 +636,7 @@ mod test {
|
|||||||
fn create_client_pin_and_parameters(
|
fn create_client_pin_and_parameters(
|
||||||
pin_uv_auth_protocol: PinUvAuthProtocol,
|
pin_uv_auth_protocol: PinUvAuthProtocol,
|
||||||
sub_command: ClientPinSubCommand,
|
sub_command: ClientPinSubCommand,
|
||||||
) -> (ClientPin, AuthenticatorClientPinParameters) {
|
) -> (ClientPin<TestEnv>, AuthenticatorClientPinParameters) {
|
||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let (client_pin, shared_secret) = create_client_pin_and_shared_secret(pin_uv_auth_protocol);
|
let (client_pin, shared_secret) = create_client_pin_and_shared_secret(pin_uv_auth_protocol);
|
||||||
|
|
||||||
@@ -683,7 +680,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_mix_pin_protocols() {
|
fn test_mix_pin_protocols() {
|
||||||
let mut env = TestEnv::new();
|
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_v1 = client_pin.get_pin_protocol(PinUvAuthProtocol::V1);
|
||||||
let pin_protocol_v2 = client_pin.get_pin_protocol(PinUvAuthProtocol::V2);
|
let pin_protocol_v2 = client_pin.get_pin_protocol(PinUvAuthProtocol::V2);
|
||||||
let message = vec![0xAA; 16];
|
let message = vec![0xAA; 16];
|
||||||
@@ -724,7 +721,7 @@ mod test {
|
|||||||
|
|
||||||
fn test_helper_verify_pin_hash_enc(pin_uv_auth_protocol: PinUvAuthProtocol) {
|
fn test_helper_verify_pin_hash_enc(pin_uv_auth_protocol: PinUvAuthProtocol) {
|
||||||
let mut env = TestEnv::new();
|
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 pin_protocol = client_pin.get_pin_protocol(pin_uv_auth_protocol);
|
||||||
let shared_secret = pin_protocol
|
let shared_secret = pin_protocol
|
||||||
.decapsulate(pin_protocol.get_public_key(), pin_uv_auth_protocol)
|
.decapsulate(pin_protocol.get_public_key(), pin_uv_auth_protocol)
|
||||||
@@ -823,7 +820,7 @@ mod test {
|
|||||||
power_cycle_state: Some(false),
|
power_cycle_state: Some(false),
|
||||||
});
|
});
|
||||||
assert_eq!(
|
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))
|
Ok(ResponseData::AuthenticatorClientPin(expected_response))
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -835,7 +832,7 @@ mod test {
|
|||||||
power_cycle_state: Some(true),
|
power_cycle_state: Some(true),
|
||||||
});
|
});
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.process_command(&mut env, params, CtapInstant::new(0)),
|
client_pin.process_command(&mut env, params),
|
||||||
Ok(ResponseData::AuthenticatorClientPin(expected_response))
|
Ok(ResponseData::AuthenticatorClientPin(expected_response))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -863,7 +860,7 @@ mod test {
|
|||||||
power_cycle_state: None,
|
power_cycle_state: None,
|
||||||
});
|
});
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.process_command(&mut env, params, CtapInstant::new(0)),
|
client_pin.process_command(&mut env, params),
|
||||||
Ok(ResponseData::AuthenticatorClientPin(expected_response))
|
Ok(ResponseData::AuthenticatorClientPin(expected_response))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -887,7 +884,7 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
env.customization_mut().set_allows_pin_protocol_v1(false);
|
env.customization_mut().set_allows_pin_protocol_v1(false);
|
||||||
assert_eq!(
|
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)
|
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -897,7 +894,7 @@ mod test {
|
|||||||
create_client_pin_and_parameters(pin_uv_auth_protocol, ClientPinSubCommand::SetPin);
|
create_client_pin_and_parameters(pin_uv_auth_protocol, ClientPinSubCommand::SetPin);
|
||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.process_command(&mut env, params, CtapInstant::new(0)),
|
client_pin.process_command(&mut env, params),
|
||||||
Ok(ResponseData::AuthenticatorClientPin(None))
|
Ok(ResponseData::AuthenticatorClientPin(None))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -930,14 +927,14 @@ mod test {
|
|||||||
let pin_uv_auth_param = shared_secret.authenticate(&auth_param_data);
|
let pin_uv_auth_param = shared_secret.authenticate(&auth_param_data);
|
||||||
params.pin_uv_auth_param = Some(pin_uv_auth_param);
|
params.pin_uv_auth_param = Some(pin_uv_auth_param);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.process_command(&mut env, params.clone(), CtapInstant::new(0)),
|
client_pin.process_command(&mut env, params.clone()),
|
||||||
Ok(ResponseData::AuthenticatorClientPin(None))
|
Ok(ResponseData::AuthenticatorClientPin(None))
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut bad_params = params.clone();
|
let mut bad_params = params.clone();
|
||||||
bad_params.pin_hash_enc = Some(vec![0xEE; 16]);
|
bad_params.pin_hash_enc = Some(vec![0xEE; 16]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.process_command(&mut env, bad_params, CtapInstant::new(0)),
|
client_pin.process_command(&mut env, bad_params),
|
||||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
|
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -945,7 +942,7 @@ mod test {
|
|||||||
storage::decr_pin_retries(&mut env).unwrap();
|
storage::decr_pin_retries(&mut env).unwrap();
|
||||||
}
|
}
|
||||||
assert_eq!(
|
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)
|
Err(Ctap2StatusCode::CTAP2_ERR_PIN_BLOCKED)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -976,7 +973,7 @@ mod test {
|
|||||||
set_standard_pin(&mut env);
|
set_standard_pin(&mut env);
|
||||||
|
|
||||||
let response = client_pin
|
let response = client_pin
|
||||||
.process_command(&mut env, params.clone(), CtapInstant::new(0))
|
.process_command(&mut env, params.clone())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let encrypted_token = match response {
|
let encrypted_token = match response {
|
||||||
ResponseData::AuthenticatorClientPin(Some(response)) => {
|
ResponseData::AuthenticatorClientPin(Some(response)) => {
|
||||||
@@ -1012,7 +1009,7 @@ mod test {
|
|||||||
let mut bad_params = params;
|
let mut bad_params = params;
|
||||||
bad_params.pin_hash_enc = Some(vec![0xEE; 16]);
|
bad_params.pin_hash_enc = Some(vec![0xEE; 16]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.process_command(&mut env, bad_params, CtapInstant::new(0)),
|
client_pin.process_command(&mut env, bad_params),
|
||||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)
|
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1037,7 +1034,7 @@ mod test {
|
|||||||
|
|
||||||
assert_eq!(storage::force_pin_change(&mut env), Ok(()));
|
assert_eq!(storage::force_pin_change(&mut env), Ok(()));
|
||||||
assert_eq!(
|
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),
|
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1070,7 +1067,7 @@ mod test {
|
|||||||
set_standard_pin(&mut env);
|
set_standard_pin(&mut env);
|
||||||
|
|
||||||
let response = client_pin
|
let response = client_pin
|
||||||
.process_command(&mut env, params.clone(), CtapInstant::new(0))
|
.process_command(&mut env, params.clone())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let encrypted_token = match response {
|
let encrypted_token = match response {
|
||||||
ResponseData::AuthenticatorClientPin(Some(response)) => {
|
ResponseData::AuthenticatorClientPin(Some(response)) => {
|
||||||
@@ -1106,21 +1103,21 @@ mod test {
|
|||||||
let mut bad_params = params.clone();
|
let mut bad_params = params.clone();
|
||||||
bad_params.permissions = Some(0x00);
|
bad_params.permissions = Some(0x00);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.process_command(&mut env, bad_params, CtapInstant::new(0)),
|
client_pin.process_command(&mut env, bad_params),
|
||||||
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut bad_params = params.clone();
|
let mut bad_params = params.clone();
|
||||||
bad_params.permissions_rp_id = None;
|
bad_params.permissions_rp_id = None;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.process_command(&mut env, bad_params, CtapInstant::new(0)),
|
client_pin.process_command(&mut env, bad_params),
|
||||||
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut bad_params = params;
|
let mut bad_params = params;
|
||||||
bad_params.pin_hash_enc = Some(vec![0xEE; 16]);
|
bad_params.pin_hash_enc = Some(vec![0xEE; 16]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.process_command(&mut env, bad_params, CtapInstant::new(0)),
|
client_pin.process_command(&mut env, bad_params),
|
||||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)
|
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1147,7 +1144,7 @@ mod test {
|
|||||||
|
|
||||||
assert_eq!(storage::force_pin_change(&mut env), Ok(()));
|
assert_eq!(storage::force_pin_change(&mut env), Ok(()));
|
||||||
assert_eq!(
|
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)
|
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1281,7 +1278,7 @@ mod test {
|
|||||||
salt_auth,
|
salt_auth,
|
||||||
pin_uv_auth_protocol,
|
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())
|
output.map(|v| shared_secret.as_ref().decrypt(&v).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1301,7 +1298,7 @@ mod test {
|
|||||||
salt_auth,
|
salt_auth,
|
||||||
pin_uv_auth_protocol,
|
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));
|
assert_eq!(output, Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1403,7 +1400,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_has_permission() {
|
fn test_has_permission() {
|
||||||
let mut env = TestEnv::new();
|
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);
|
client_pin.pin_uv_auth_token_state.set_permissions(0x7F);
|
||||||
for permission in PinPermission::into_enum_iter() {
|
for permission in PinPermission::into_enum_iter() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -1427,7 +1424,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_has_no_rp_id_permission() {
|
fn test_has_no_rp_id_permission() {
|
||||||
let mut env = TestEnv::new();
|
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(()));
|
assert_eq!(client_pin.has_no_rp_id_permission(), Ok(()));
|
||||||
client_pin
|
client_pin
|
||||||
.pin_uv_auth_token_state
|
.pin_uv_auth_token_state
|
||||||
@@ -1441,7 +1438,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_has_no_or_rp_id_permission() {
|
fn test_has_no_or_rp_id_permission() {
|
||||||
let mut env = TestEnv::new();
|
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(()));
|
assert_eq!(client_pin.has_no_or_rp_id_permission("example.com"), Ok(()));
|
||||||
client_pin
|
client_pin
|
||||||
.pin_uv_auth_token_state
|
.pin_uv_auth_token_state
|
||||||
@@ -1456,7 +1453,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_has_no_or_rp_id_hash_permission() {
|
fn test_has_no_or_rp_id_hash_permission() {
|
||||||
let mut env = TestEnv::new();
|
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");
|
let rp_id_hash = Sha256::hash(b"example.com");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.has_no_or_rp_id_hash_permission(&rp_id_hash),
|
client_pin.has_no_or_rp_id_hash_permission(&rp_id_hash),
|
||||||
@@ -1478,7 +1475,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_ensure_rp_id_permission() {
|
fn test_ensure_rp_id_permission() {
|
||||||
let mut env = TestEnv::new();
|
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.ensure_rp_id_permission("example.com"), Ok(()));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin
|
client_pin
|
||||||
@@ -1496,11 +1493,11 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_verify_pin_uv_auth_token() {
|
fn test_verify_pin_uv_auth_token() {
|
||||||
let mut env = TestEnv::new();
|
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];
|
let message = [0xAA];
|
||||||
client_pin
|
client_pin
|
||||||
.pin_uv_auth_token_state
|
.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
|
let pin_uv_auth_token_v1 = client_pin
|
||||||
.get_pin_protocol(PinUvAuthProtocol::V1)
|
.get_pin_protocol(PinUvAuthProtocol::V1)
|
||||||
@@ -1570,7 +1567,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_verify_pin_uv_auth_token_not_in_use() {
|
fn test_verify_pin_uv_auth_token_not_in_use() {
|
||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let client_pin = ClientPin::new(env.rng());
|
let client_pin = ClientPin::<TestEnv>::new(&mut env);
|
||||||
let message = [0xAA];
|
let message = [0xAA];
|
||||||
|
|
||||||
let pin_uv_auth_token_v1 = client_pin
|
let pin_uv_auth_token_v1 = client_pin
|
||||||
@@ -1592,7 +1589,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_reset() {
|
fn test_reset() {
|
||||||
let mut env = TestEnv::new();
|
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_v1 = client_pin.pin_protocol_v1.get_public_key();
|
||||||
let public_key_v2 = client_pin.pin_protocol_v2.get_public_key();
|
let public_key_v2 = client_pin.pin_protocol_v2.get_public_key();
|
||||||
let token_v1 = *client_pin.pin_protocol_v1.get_pin_uv_auth_token();
|
let token_v1 = *client_pin.pin_protocol_v1.get_pin_uv_auth_token();
|
||||||
@@ -1601,7 +1598,7 @@ mod test {
|
|||||||
client_pin
|
client_pin
|
||||||
.pin_uv_auth_token_state
|
.pin_uv_auth_token_state
|
||||||
.set_permissions_rp_id(Some(String::from("example.com")));
|
.set_permissions_rp_id(Some(String::from("example.com")));
|
||||||
client_pin.reset(env.rng());
|
client_pin.reset(&mut env);
|
||||||
assert_ne!(public_key_v1, client_pin.pin_protocol_v1.get_public_key());
|
assert_ne!(public_key_v1, client_pin.pin_protocol_v1.get_public_key());
|
||||||
assert_ne!(public_key_v2, client_pin.pin_protocol_v2.get_public_key());
|
assert_ne!(public_key_v2, client_pin.pin_protocol_v2.get_public_key());
|
||||||
assert_ne!(
|
assert_ne!(
|
||||||
@@ -1631,9 +1628,7 @@ mod test {
|
|||||||
set_standard_pin(&mut env);
|
set_standard_pin(&mut env);
|
||||||
params.permissions = Some(0xFF);
|
params.permissions = Some(0xFF);
|
||||||
|
|
||||||
assert!(client_pin
|
assert!(client_pin.process_command(&mut env, params).is_ok());
|
||||||
.process_command(&mut env, params, CtapInstant::new(0))
|
|
||||||
.is_ok());
|
|
||||||
for permission in PinPermission::into_enum_iter() {
|
for permission in PinPermission::into_enum_iter() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin
|
client_pin
|
||||||
@@ -1649,8 +1644,8 @@ mod test {
|
|||||||
Ok(())
|
Ok(())
|
||||||
);
|
);
|
||||||
|
|
||||||
let timeout = CtapInstant::new(0) + Milliseconds::new(30001_u32);
|
env.clock().advance(30001);
|
||||||
client_pin.update_timeouts(timeout);
|
client_pin.update_timeouts(&mut env);
|
||||||
for permission in PinPermission::into_enum_iter() {
|
for permission in PinPermission::into_enum_iter() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin
|
client_pin
|
||||||
@@ -1677,9 +1672,7 @@ mod test {
|
|||||||
set_standard_pin(&mut env);
|
set_standard_pin(&mut env);
|
||||||
params.permissions = Some(0xFF);
|
params.permissions = Some(0xFF);
|
||||||
|
|
||||||
assert!(client_pin
|
assert!(client_pin.process_command(&mut env, params).is_ok());
|
||||||
.process_command(&mut env, params, CtapInstant::new(0))
|
|
||||||
.is_ok());
|
|
||||||
for permission in PinPermission::into_enum_iter() {
|
for permission in PinPermission::into_enum_iter() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin
|
client_pin
|
||||||
|
|||||||
@@ -73,9 +73,9 @@ fn process_set_min_pin_length(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Processes the AuthenticatorConfig command.
|
/// Processes the AuthenticatorConfig command.
|
||||||
pub fn process_config(
|
pub fn process_config<E: Env>(
|
||||||
env: &mut impl Env,
|
env: &mut E,
|
||||||
client_pin: &mut ClientPin,
|
client_pin: &mut ClientPin<E>,
|
||||||
params: AuthenticatorConfigParameters,
|
params: AuthenticatorConfigParameters,
|
||||||
) -> Result<ResponseData, Ctap2StatusCode> {
|
) -> Result<ResponseData, Ctap2StatusCode> {
|
||||||
let AuthenticatorConfigParameters {
|
let AuthenticatorConfigParameters {
|
||||||
@@ -133,8 +133,12 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
&mut env,
|
||||||
|
key_agreement_key,
|
||||||
|
pin_uv_auth_token,
|
||||||
|
PinUvAuthProtocol::V1,
|
||||||
|
);
|
||||||
|
|
||||||
let config_params = AuthenticatorConfigParameters {
|
let config_params = AuthenticatorConfigParameters {
|
||||||
sub_command: ConfigSubCommand::EnableEnterpriseAttestation,
|
sub_command: ConfigSubCommand::EnableEnterpriseAttestation,
|
||||||
@@ -160,8 +164,12 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
&mut env,
|
||||||
|
key_agreement_key,
|
||||||
|
pin_uv_auth_token,
|
||||||
|
PinUvAuthProtocol::V1,
|
||||||
|
);
|
||||||
|
|
||||||
let config_params = AuthenticatorConfigParameters {
|
let config_params = AuthenticatorConfigParameters {
|
||||||
sub_command: ConfigSubCommand::ToggleAlwaysUv,
|
sub_command: ConfigSubCommand::ToggleAlwaysUv,
|
||||||
@@ -195,8 +203,12 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol);
|
&mut env,
|
||||||
|
key_agreement_key,
|
||||||
|
pin_uv_auth_token,
|
||||||
|
pin_uv_auth_protocol,
|
||||||
|
);
|
||||||
storage::set_pin(&mut env, &[0x88; 16], 4).unwrap();
|
storage::set_pin(&mut env, &[0x88; 16], 4).unwrap();
|
||||||
|
|
||||||
let mut config_data = vec![0xFF; 32];
|
let mut config_data = vec![0xFF; 32];
|
||||||
@@ -265,8 +277,12 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
&mut env,
|
||||||
|
key_agreement_key,
|
||||||
|
pin_uv_auth_token,
|
||||||
|
PinUvAuthProtocol::V1,
|
||||||
|
);
|
||||||
|
|
||||||
// First, increase minimum PIN length from 4 to 6 without PIN auth.
|
// First, increase minimum PIN length from 4 to 6 without PIN auth.
|
||||||
let min_pin_length = 6;
|
let min_pin_length = 6;
|
||||||
@@ -309,8 +325,12 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
&mut env,
|
||||||
|
key_agreement_key,
|
||||||
|
pin_uv_auth_token,
|
||||||
|
PinUvAuthProtocol::V1,
|
||||||
|
);
|
||||||
|
|
||||||
// First, set RP IDs without PIN auth.
|
// First, set RP IDs without PIN auth.
|
||||||
let min_pin_length = 6;
|
let min_pin_length = 6;
|
||||||
@@ -385,8 +405,12 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
&mut env,
|
||||||
|
key_agreement_key,
|
||||||
|
pin_uv_auth_token,
|
||||||
|
PinUvAuthProtocol::V1,
|
||||||
|
);
|
||||||
|
|
||||||
storage::set_pin(&mut env, &[0x88; 16], 4).unwrap();
|
storage::set_pin(&mut env, &[0x88; 16], 4).unwrap();
|
||||||
// Increase min PIN, force PIN change.
|
// Increase min PIN, force PIN change.
|
||||||
@@ -408,8 +432,12 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
&mut env,
|
||||||
|
key_agreement_key,
|
||||||
|
pin_uv_auth_token,
|
||||||
|
PinUvAuthProtocol::V1,
|
||||||
|
);
|
||||||
|
|
||||||
storage::set_pin(&mut env, &[0x88; 16], 4).unwrap();
|
storage::set_pin(&mut env, &[0x88; 16], 4).unwrap();
|
||||||
let pin_uv_auth_param = Some(vec![
|
let pin_uv_auth_param = Some(vec![
|
||||||
@@ -439,8 +467,12 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
&mut env,
|
||||||
|
key_agreement_key,
|
||||||
|
pin_uv_auth_token,
|
||||||
|
PinUvAuthProtocol::V1,
|
||||||
|
);
|
||||||
|
|
||||||
let config_params = AuthenticatorConfigParameters {
|
let config_params = AuthenticatorConfigParameters {
|
||||||
sub_command: ConfigSubCommand::VendorPrototype,
|
sub_command: ConfigSubCommand::VendorPrototype,
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use super::super::clock::CtapInstant;
|
|
||||||
use super::client_pin::{ClientPin, PinPermission};
|
use super::client_pin::{ClientPin, PinPermission};
|
||||||
use super::command::AuthenticatorCredentialManagementParameters;
|
use super::command::AuthenticatorCredentialManagementParameters;
|
||||||
use super::data_formats::{
|
use super::data_formats::{
|
||||||
@@ -108,9 +107,9 @@ fn enumerate_credentials_response(
|
|||||||
/// Check if the token permissions have the correct associated RP ID.
|
/// 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.
|
/// Either no RP ID is associated, or the RP ID matches the stored credential.
|
||||||
fn check_rp_id_permissions(
|
fn check_rp_id_permissions<E: Env>(
|
||||||
env: &mut impl Env,
|
env: &mut E,
|
||||||
client_pin: &mut ClientPin,
|
client_pin: &mut ClientPin<E>,
|
||||||
credential_id: &[u8],
|
credential_id: &[u8],
|
||||||
) -> Result<(), Ctap2StatusCode> {
|
) -> Result<(), Ctap2StatusCode> {
|
||||||
// Pre-check a sufficient condition before calling the store.
|
// Pre-check a sufficient condition before calling the store.
|
||||||
@@ -135,17 +134,16 @@ fn process_get_creds_metadata(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Processes the subcommand enumerateRPsBegin for CredentialManagement.
|
/// Processes the subcommand enumerateRPsBegin for CredentialManagement.
|
||||||
fn process_enumerate_rps_begin(
|
fn process_enumerate_rps_begin<E: Env>(
|
||||||
env: &mut impl Env,
|
env: &mut E,
|
||||||
stateful_command_permission: &mut StatefulPermission,
|
stateful_command_permission: &mut StatefulPermission<E>,
|
||||||
channel: Channel,
|
channel: Channel,
|
||||||
now: CtapInstant,
|
|
||||||
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
|
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
|
||||||
let rp_set = get_stored_rp_ids(env)?;
|
let rp_set = get_stored_rp_ids(env)?;
|
||||||
let total_rps = rp_set.len();
|
let total_rps = rp_set.len();
|
||||||
|
|
||||||
if total_rps > 1 {
|
if total_rps > 1 {
|
||||||
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()
|
// TODO https://github.com/rust-lang/rust/issues/62924 replace with pop_first()
|
||||||
let rp_id = rp_set
|
let rp_id = rp_set
|
||||||
@@ -156,9 +154,9 @@ fn process_enumerate_rps_begin(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Processes the subcommand enumerateRPsGetNextRP for CredentialManagement.
|
/// Processes the subcommand enumerateRPsGetNextRP for CredentialManagement.
|
||||||
fn process_enumerate_rps_get_next_rp(
|
fn process_enumerate_rps_get_next_rp<E: Env>(
|
||||||
env: &mut impl Env,
|
env: &mut E,
|
||||||
stateful_command_permission: &mut StatefulPermission,
|
stateful_command_permission: &mut StatefulPermission<E>,
|
||||||
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
|
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
|
||||||
let rp_id_index = stateful_command_permission.next_enumerate_rp()?;
|
let rp_id_index = stateful_command_permission.next_enumerate_rp()?;
|
||||||
let rp_set = get_stored_rp_ids(env)?;
|
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.
|
/// Processes the subcommand enumerateCredentialsBegin for CredentialManagement.
|
||||||
fn process_enumerate_credentials_begin(
|
fn process_enumerate_credentials_begin<E: Env>(
|
||||||
env: &mut impl Env,
|
env: &mut E,
|
||||||
stateful_command_permission: &mut StatefulPermission,
|
stateful_command_permission: &mut StatefulPermission<E>,
|
||||||
client_pin: &mut ClientPin,
|
client_pin: &mut ClientPin<E>,
|
||||||
sub_command_params: CredentialManagementSubCommandParameters,
|
sub_command_params: CredentialManagementSubCommandParameters,
|
||||||
channel: Channel,
|
channel: Channel,
|
||||||
now: CtapInstant,
|
|
||||||
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
|
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
|
||||||
let rp_id_hash = sub_command_params
|
let rp_id_hash = sub_command_params
|
||||||
.rp_id_hash
|
.rp_id_hash
|
||||||
@@ -203,7 +200,7 @@ fn process_enumerate_credentials_begin(
|
|||||||
let credential = storage::get_credential(env, current_key)?;
|
let credential = storage::get_credential(env, current_key)?;
|
||||||
if total_credentials > 1 {
|
if total_credentials > 1 {
|
||||||
stateful_command_permission.set_command(
|
stateful_command_permission.set_command(
|
||||||
now,
|
env,
|
||||||
StatefulCommand::EnumerateCredentials(rp_credentials),
|
StatefulCommand::EnumerateCredentials(rp_credentials),
|
||||||
channel,
|
channel,
|
||||||
);
|
);
|
||||||
@@ -212,9 +209,9 @@ fn process_enumerate_credentials_begin(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Processes the subcommand enumerateCredentialsGetNextCredential for CredentialManagement.
|
/// Processes the subcommand enumerateCredentialsGetNextCredential for CredentialManagement.
|
||||||
fn process_enumerate_credentials_get_next_credential(
|
fn process_enumerate_credentials_get_next_credential<E: Env>(
|
||||||
env: &mut impl Env,
|
env: &mut E,
|
||||||
stateful_command_permission: &mut StatefulPermission,
|
stateful_command_permission: &mut StatefulPermission<E>,
|
||||||
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
|
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
|
||||||
let credential_key = stateful_command_permission.next_enumerate_credential()?;
|
let credential_key = stateful_command_permission.next_enumerate_credential()?;
|
||||||
let credential = storage::get_credential(env, credential_key)?;
|
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.
|
/// Processes the subcommand deleteCredential for CredentialManagement.
|
||||||
fn process_delete_credential(
|
fn process_delete_credential<E: Env>(
|
||||||
env: &mut impl Env,
|
env: &mut E,
|
||||||
client_pin: &mut ClientPin,
|
client_pin: &mut ClientPin<E>,
|
||||||
sub_command_params: CredentialManagementSubCommandParameters,
|
sub_command_params: CredentialManagementSubCommandParameters,
|
||||||
) -> Result<(), Ctap2StatusCode> {
|
) -> Result<(), Ctap2StatusCode> {
|
||||||
let credential_id = sub_command_params
|
let credential_id = sub_command_params
|
||||||
@@ -236,9 +233,9 @@ fn process_delete_credential(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Processes the subcommand updateUserInformation for CredentialManagement.
|
/// Processes the subcommand updateUserInformation for CredentialManagement.
|
||||||
fn process_update_user_information(
|
fn process_update_user_information<E: Env>(
|
||||||
env: &mut impl Env,
|
env: &mut E,
|
||||||
client_pin: &mut ClientPin,
|
client_pin: &mut ClientPin<E>,
|
||||||
sub_command_params: CredentialManagementSubCommandParameters,
|
sub_command_params: CredentialManagementSubCommandParameters,
|
||||||
) -> Result<(), Ctap2StatusCode> {
|
) -> Result<(), Ctap2StatusCode> {
|
||||||
let credential_id = sub_command_params
|
let credential_id = sub_command_params
|
||||||
@@ -253,13 +250,12 @@ fn process_update_user_information(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Processes the CredentialManagement command and all its subcommands.
|
/// Processes the CredentialManagement command and all its subcommands.
|
||||||
pub fn process_credential_management(
|
pub fn process_credential_management<E: Env>(
|
||||||
env: &mut impl Env,
|
env: &mut E,
|
||||||
stateful_command_permission: &mut StatefulPermission,
|
stateful_command_permission: &mut StatefulPermission<E>,
|
||||||
client_pin: &mut ClientPin,
|
client_pin: &mut ClientPin<E>,
|
||||||
cred_management_params: AuthenticatorCredentialManagementParameters,
|
cred_management_params: AuthenticatorCredentialManagementParameters,
|
||||||
channel: Channel,
|
channel: Channel,
|
||||||
now: CtapInstant,
|
|
||||||
) -> Result<ResponseData, Ctap2StatusCode> {
|
) -> Result<ResponseData, Ctap2StatusCode> {
|
||||||
let AuthenticatorCredentialManagementParameters {
|
let AuthenticatorCredentialManagementParameters {
|
||||||
sub_command,
|
sub_command,
|
||||||
@@ -319,7 +315,6 @@ pub fn process_credential_management(
|
|||||||
env,
|
env,
|
||||||
stateful_command_permission,
|
stateful_command_permission,
|
||||||
channel,
|
channel,
|
||||||
now,
|
|
||||||
)?)
|
)?)
|
||||||
}
|
}
|
||||||
CredentialManagementSubCommand::EnumerateRpsGetNextRp => Some(
|
CredentialManagementSubCommand::EnumerateRpsGetNextRp => Some(
|
||||||
@@ -332,7 +327,6 @@ pub fn process_credential_management(
|
|||||||
client_pin,
|
client_pin,
|
||||||
sub_command_params.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
sub_command_params.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?,
|
||||||
channel,
|
channel,
|
||||||
now,
|
|
||||||
)?)
|
)?)
|
||||||
}
|
}
|
||||||
CredentialManagementSubCommand::EnumerateCredentialsGetNextCredential => Some(
|
CredentialManagementSubCommand::EnumerateCredentialsGetNextCredential => Some(
|
||||||
@@ -392,11 +386,15 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let client_pin =
|
let client_pin = ClientPin::<TestEnv>::new_test(
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol);
|
&mut env,
|
||||||
|
key_agreement_key,
|
||||||
|
pin_uv_auth_token,
|
||||||
|
pin_uv_auth_protocol,
|
||||||
|
);
|
||||||
let credential_source = create_credential_source(&mut env);
|
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;
|
ctap_state.client_pin = client_pin;
|
||||||
|
|
||||||
storage::set_pin(&mut env, &[0u8; 16], 4).unwrap();
|
storage::set_pin(&mut env, &[0u8; 16], 4).unwrap();
|
||||||
@@ -419,7 +417,6 @@ mod test {
|
|||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
DUMMY_CHANNEL,
|
DUMMY_CHANNEL,
|
||||||
CtapInstant::new(0),
|
|
||||||
);
|
);
|
||||||
let initial_capacity = match cred_management_response.unwrap() {
|
let initial_capacity = match cred_management_response.unwrap() {
|
||||||
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
|
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
|
||||||
@@ -445,7 +442,6 @@ mod test {
|
|||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
DUMMY_CHANNEL,
|
DUMMY_CHANNEL,
|
||||||
CtapInstant::new(0),
|
|
||||||
);
|
);
|
||||||
match cred_management_response.unwrap() {
|
match cred_management_response.unwrap() {
|
||||||
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
|
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
|
||||||
@@ -474,13 +470,17 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let client_pin =
|
let client_pin = ClientPin::<TestEnv>::new_test(
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
&mut env,
|
||||||
|
key_agreement_key,
|
||||||
|
pin_uv_auth_token,
|
||||||
|
PinUvAuthProtocol::V1,
|
||||||
|
);
|
||||||
let credential_source1 = create_credential_source(&mut env);
|
let credential_source1 = create_credential_source(&mut env);
|
||||||
let mut credential_source2 = create_credential_source(&mut env);
|
let mut credential_source2 = create_credential_source(&mut env);
|
||||||
credential_source2.rp_id = "another.example.com".to_string();
|
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;
|
ctap_state.client_pin = client_pin;
|
||||||
|
|
||||||
storage::store_credential(&mut env, credential_source1).unwrap();
|
storage::store_credential(&mut env, credential_source1).unwrap();
|
||||||
@@ -504,7 +504,6 @@ mod test {
|
|||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
DUMMY_CHANNEL,
|
DUMMY_CHANNEL,
|
||||||
CtapInstant::new(0),
|
|
||||||
);
|
);
|
||||||
let first_rp_id = match cred_management_response.unwrap() {
|
let first_rp_id = match cred_management_response.unwrap() {
|
||||||
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
|
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
|
||||||
@@ -529,7 +528,6 @@ mod test {
|
|||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
DUMMY_CHANNEL,
|
DUMMY_CHANNEL,
|
||||||
CtapInstant::new(0),
|
|
||||||
);
|
);
|
||||||
let second_rp_id = match cred_management_response.unwrap() {
|
let second_rp_id = match cred_management_response.unwrap() {
|
||||||
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
|
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
|
||||||
@@ -555,7 +553,6 @@ mod test {
|
|||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
DUMMY_CHANNEL,
|
DUMMY_CHANNEL,
|
||||||
CtapInstant::new(0),
|
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cred_management_response,
|
cred_management_response,
|
||||||
@@ -568,11 +565,15 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let client_pin =
|
let client_pin = ClientPin::<TestEnv>::new_test(
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
&mut env,
|
||||||
|
key_agreement_key,
|
||||||
|
pin_uv_auth_token,
|
||||||
|
PinUvAuthProtocol::V1,
|
||||||
|
);
|
||||||
let credential_source = create_credential_source(&mut env);
|
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;
|
ctap_state.client_pin = client_pin;
|
||||||
|
|
||||||
const NUM_CREDENTIALS: usize = 20;
|
const NUM_CREDENTIALS: usize = 20;
|
||||||
@@ -605,7 +606,6 @@ mod test {
|
|||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
DUMMY_CHANNEL,
|
DUMMY_CHANNEL,
|
||||||
CtapInstant::new(0),
|
|
||||||
);
|
);
|
||||||
match cred_management_response.unwrap() {
|
match cred_management_response.unwrap() {
|
||||||
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
|
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
|
||||||
@@ -636,7 +636,6 @@ mod test {
|
|||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
DUMMY_CHANNEL,
|
DUMMY_CHANNEL,
|
||||||
CtapInstant::new(0),
|
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cred_management_response,
|
cred_management_response,
|
||||||
@@ -649,8 +648,12 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let client_pin =
|
let client_pin = ClientPin::<TestEnv>::new_test(
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
&mut env,
|
||||||
|
key_agreement_key,
|
||||||
|
pin_uv_auth_token,
|
||||||
|
PinUvAuthProtocol::V1,
|
||||||
|
);
|
||||||
let credential_source1 = create_credential_source(&mut env);
|
let credential_source1 = create_credential_source(&mut env);
|
||||||
let mut credential_source2 = create_credential_source(&mut env);
|
let mut credential_source2 = create_credential_source(&mut env);
|
||||||
credential_source2.user_handle = vec![0x02];
|
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_display_name = Some("User Two".to_string());
|
||||||
credential_source2.user_icon = Some("icon2".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;
|
ctap_state.client_pin = client_pin;
|
||||||
|
|
||||||
storage::store_credential(&mut env, credential_source1).unwrap();
|
storage::store_credential(&mut env, credential_source1).unwrap();
|
||||||
@@ -689,7 +692,6 @@ mod test {
|
|||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
DUMMY_CHANNEL,
|
DUMMY_CHANNEL,
|
||||||
CtapInstant::new(0),
|
|
||||||
);
|
);
|
||||||
let first_credential_id = match cred_management_response.unwrap() {
|
let first_credential_id = match cred_management_response.unwrap() {
|
||||||
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
|
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
|
||||||
@@ -713,7 +715,6 @@ mod test {
|
|||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
DUMMY_CHANNEL,
|
DUMMY_CHANNEL,
|
||||||
CtapInstant::new(0),
|
|
||||||
);
|
);
|
||||||
let second_credential_id = match cred_management_response.unwrap() {
|
let second_credential_id = match cred_management_response.unwrap() {
|
||||||
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
|
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
|
||||||
@@ -738,7 +739,6 @@ mod test {
|
|||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
DUMMY_CHANNEL,
|
DUMMY_CHANNEL,
|
||||||
CtapInstant::new(0),
|
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cred_management_response,
|
cred_management_response,
|
||||||
@@ -751,12 +751,16 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let client_pin =
|
let client_pin = ClientPin::<TestEnv>::new_test(
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
&mut env,
|
||||||
|
key_agreement_key,
|
||||||
|
pin_uv_auth_token,
|
||||||
|
PinUvAuthProtocol::V1,
|
||||||
|
);
|
||||||
let mut credential_source = create_credential_source(&mut env);
|
let mut credential_source = create_credential_source(&mut env);
|
||||||
credential_source.credential_id = vec![0x1D; 32];
|
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;
|
ctap_state.client_pin = client_pin;
|
||||||
|
|
||||||
storage::store_credential(&mut env, credential_source).unwrap();
|
storage::store_credential(&mut env, credential_source).unwrap();
|
||||||
@@ -789,7 +793,6 @@ mod test {
|
|||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
DUMMY_CHANNEL,
|
DUMMY_CHANNEL,
|
||||||
CtapInstant::new(0),
|
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cred_management_response,
|
cred_management_response,
|
||||||
@@ -808,7 +811,6 @@ mod test {
|
|||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
DUMMY_CHANNEL,
|
DUMMY_CHANNEL,
|
||||||
CtapInstant::new(0),
|
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cred_management_response,
|
cred_management_response,
|
||||||
@@ -821,12 +823,16 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let client_pin =
|
let client_pin = ClientPin::<TestEnv>::new_test(
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
&mut env,
|
||||||
|
key_agreement_key,
|
||||||
|
pin_uv_auth_token,
|
||||||
|
PinUvAuthProtocol::V1,
|
||||||
|
);
|
||||||
let mut credential_source = create_credential_source(&mut env);
|
let mut credential_source = create_credential_source(&mut env);
|
||||||
credential_source.credential_id = vec![0x1D; 32];
|
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;
|
ctap_state.client_pin = client_pin;
|
||||||
|
|
||||||
storage::store_credential(&mut env, credential_source).unwrap();
|
storage::store_credential(&mut env, credential_source).unwrap();
|
||||||
@@ -865,7 +871,6 @@ mod test {
|
|||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
DUMMY_CHANNEL,
|
DUMMY_CHANNEL,
|
||||||
CtapInstant::new(0),
|
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cred_management_response,
|
cred_management_response,
|
||||||
@@ -887,7 +892,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_process_credential_management_invalid_pin_uv_auth_param() {
|
fn test_process_credential_management_invalid_pin_uv_auth_param() {
|
||||||
let mut env = TestEnv::new();
|
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();
|
storage::set_pin(&mut env, &[0u8; 16], 4).unwrap();
|
||||||
|
|
||||||
@@ -903,7 +908,6 @@ mod test {
|
|||||||
&mut ctap_state.client_pin,
|
&mut ctap_state.client_pin,
|
||||||
cred_management_params,
|
cred_management_params,
|
||||||
DUMMY_CHANNEL,
|
DUMMY_CHANNEL,
|
||||||
CtapInstant::new(0),
|
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cred_management_response,
|
cred_management_response,
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use super::super::clock::CtapInstant;
|
|
||||||
use super::apdu::{Apdu, ApduStatusCode};
|
use super::apdu::{Apdu, ApduStatusCode};
|
||||||
use super::credential_id::{decrypt_credential_id, encrypt_to_credential_id};
|
use super::credential_id::{decrypt_credential_id, encrypt_to_credential_id};
|
||||||
use super::crypto_wrapper::PrivateKey;
|
use super::crypto_wrapper::PrivateKey;
|
||||||
@@ -174,11 +173,10 @@ impl Ctap1Command {
|
|||||||
const VENDOR_SPECIFIC_FIRST: u8 = 0x40;
|
const VENDOR_SPECIFIC_FIRST: u8 = 0x40;
|
||||||
const VENDOR_SPECIFIC_LAST: u8 = 0xBF;
|
const VENDOR_SPECIFIC_LAST: u8 = 0xBF;
|
||||||
|
|
||||||
pub fn process_command(
|
pub fn process_command<E: Env>(
|
||||||
env: &mut impl Env,
|
env: &mut E,
|
||||||
message: &[u8],
|
message: &[u8],
|
||||||
ctap_state: &mut CtapState,
|
ctap_state: &mut CtapState<E>,
|
||||||
clock_value: CtapInstant,
|
|
||||||
) -> Result<Vec<u8>, Ctap1StatusCode> {
|
) -> Result<Vec<u8>, Ctap1StatusCode> {
|
||||||
if !ctap_state
|
if !ctap_state
|
||||||
.allows_ctap1(env)
|
.allows_ctap1(env)
|
||||||
@@ -192,7 +190,7 @@ impl Ctap1Command {
|
|||||||
challenge,
|
challenge,
|
||||||
application,
|
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);
|
return Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED);
|
||||||
}
|
}
|
||||||
Ctap1Command::process_register(env, challenge, application)
|
Ctap1Command::process_register(env, challenge, application)
|
||||||
@@ -205,8 +203,7 @@ impl Ctap1Command {
|
|||||||
flags,
|
flags,
|
||||||
} => {
|
} => {
|
||||||
// The order is important due to side effects of checking user presence.
|
// The order is important due to side effects of checking user presence.
|
||||||
if flags == Ctap1Flags::EnforceUpAndSign
|
if flags == Ctap1Flags::EnforceUpAndSign && !ctap_state.u2f_up_state.consume_up(env)
|
||||||
&& !ctap_state.u2f_up_state.consume_up(clock_value)
|
|
||||||
{
|
{
|
||||||
return Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED);
|
return Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED);
|
||||||
}
|
}
|
||||||
@@ -301,13 +298,13 @@ impl Ctap1Command {
|
|||||||
// +-------------------+---------+--------------+-----------------+
|
// +-------------------+---------+--------------+-----------------+
|
||||||
// + application (32B) | UP (1B) | Counter (4B) | challenge (32B) |
|
// + application (32B) | UP (1B) | Counter (4B) | challenge (32B) |
|
||||||
// +-------------------+---------+--------------+-----------------+
|
// +-------------------+---------+--------------+-----------------+
|
||||||
fn process_authenticate(
|
fn process_authenticate<E: Env>(
|
||||||
env: &mut impl Env,
|
env: &mut E,
|
||||||
challenge: [u8; 32],
|
challenge: [u8; 32],
|
||||||
application: [u8; 32],
|
application: [u8; 32],
|
||||||
key_handle: Vec<u8>,
|
key_handle: Vec<u8>,
|
||||||
flags: Ctap1Flags,
|
flags: Ctap1Flags,
|
||||||
ctap_state: &mut CtapState,
|
ctap_state: &mut CtapState<E>,
|
||||||
) -> Result<Vec<u8>, Ctap1StatusCode> {
|
) -> Result<Vec<u8>, Ctap1StatusCode> {
|
||||||
let credential_source = decrypt_credential_id(env, key_handle, &application)
|
let credential_source = decrypt_credential_id(env, key_handle, &application)
|
||||||
.map_err(|_| Ctap1StatusCode::SW_WRONG_DATA)?;
|
.map_err(|_| Ctap1StatusCode::SW_WRONG_DATA)?;
|
||||||
@@ -345,9 +342,9 @@ impl Ctap1Command {
|
|||||||
mod test {
|
mod test {
|
||||||
use super::super::credential_id::CBOR_CREDENTIAL_ID_SIZE;
|
use super::super::credential_id::CBOR_CREDENTIAL_ID_SIZE;
|
||||||
use super::super::data_formats::SignatureAlgorithm;
|
use super::super::data_formats::SignatureAlgorithm;
|
||||||
|
use super::super::TOUCH_TIMEOUT_MS;
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::api::customization::Customization;
|
use crate::api::customization::Customization;
|
||||||
use crate::clock::TEST_CLOCK_FREQUENCY_HZ;
|
|
||||||
use crate::ctap::storage;
|
use crate::ctap::storage;
|
||||||
use crate::env::test::TestEnv;
|
use crate::env::test::TestEnv;
|
||||||
use crypto::Hash256;
|
use crypto::Hash256;
|
||||||
@@ -394,15 +391,14 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
env.user_presence()
|
env.user_presence()
|
||||||
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
||||||
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
let mut ctap_state = CtapState::new(&mut env);
|
||||||
storage::toggle_always_uv(&mut env).unwrap();
|
storage::toggle_always_uv(&mut env).unwrap();
|
||||||
|
|
||||||
let application = [0x0A; 32];
|
let application = [0x0A; 32];
|
||||||
let message = create_register_message(&application);
|
let message = create_register_message(&application);
|
||||||
ctap_state.u2f_up_state.consume_up(CtapInstant::new(0));
|
ctap_state.u2f_up_state.consume_up(&mut env);
|
||||||
ctap_state.u2f_up_state.grant_up(CtapInstant::new(0));
|
ctap_state.u2f_up_state.grant_up(&mut env);
|
||||||
let response =
|
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||||
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
|
|
||||||
assert_eq!(response, Err(Ctap1StatusCode::SW_COMMAND_NOT_ALLOWED));
|
assert_eq!(response, Err(Ctap1StatusCode::SW_COMMAND_NOT_ALLOWED));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,14 +407,13 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
env.user_presence()
|
env.user_presence()
|
||||||
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
||||||
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
let mut ctap_state = CtapState::new(&mut env);
|
||||||
|
|
||||||
let application = [0x0A; 32];
|
let application = [0x0A; 32];
|
||||||
let message = create_register_message(&application);
|
let message = create_register_message(&application);
|
||||||
ctap_state.u2f_up_state.consume_up(CtapInstant::new(0));
|
ctap_state.u2f_up_state.consume_up(&mut env);
|
||||||
ctap_state.u2f_up_state.grant_up(CtapInstant::new(0));
|
ctap_state.u2f_up_state.grant_up(&mut env);
|
||||||
let response =
|
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||||
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
|
|
||||||
// Certificate and private key are missing
|
// Certificate and private key are missing
|
||||||
assert_eq!(response, Err(Ctap1StatusCode::SW_INTERNAL_EXCEPTION));
|
assert_eq!(response, Err(Ctap1StatusCode::SW_INTERNAL_EXCEPTION));
|
||||||
|
|
||||||
@@ -429,11 +424,9 @@ mod test {
|
|||||||
env.attestation_store()
|
env.attestation_store()
|
||||||
.set(&attestation_store::Id::Batch, Some(&attestation))
|
.set(&attestation_store::Id::Batch, Some(&attestation))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
ctap_state.u2f_up_state.consume_up(CtapInstant::new(0));
|
ctap_state.u2f_up_state.consume_up(&mut env);
|
||||||
ctap_state.u2f_up_state.grant_up(CtapInstant::new(0));
|
ctap_state.u2f_up_state.grant_up(&mut env);
|
||||||
let response =
|
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state).unwrap();
|
||||||
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0))
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(response[0], Ctap1Command::LEGACY_BYTE);
|
assert_eq!(response[0], Ctap1Command::LEGACY_BYTE);
|
||||||
assert_eq!(response[66], CBOR_CREDENTIAL_ID_SIZE as u8);
|
assert_eq!(response[66], CBOR_CREDENTIAL_ID_SIZE as u8);
|
||||||
assert!(decrypt_credential_id(
|
assert!(decrypt_credential_id(
|
||||||
@@ -455,16 +448,12 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
env.user_presence()
|
env.user_presence()
|
||||||
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
||||||
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
let mut ctap_state = CtapState::new(&mut env);
|
||||||
|
|
||||||
let application = [0x0A; 32];
|
let application = [0x0A; 32];
|
||||||
let message = create_register_message(&application);
|
let message = create_register_message(&application);
|
||||||
let response = Ctap1Command::process_command(
|
let response =
|
||||||
&mut env,
|
Ctap1Command::process_command(&mut env, &message[..message.len() - 1], &mut ctap_state);
|
||||||
&message[..message.len() - 1],
|
|
||||||
&mut ctap_state,
|
|
||||||
CtapInstant::new(0),
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_LENGTH));
|
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_LENGTH));
|
||||||
}
|
}
|
||||||
@@ -477,14 +466,12 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
env.user_presence()
|
env.user_presence()
|
||||||
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
||||||
let mut ctap_state = CtapState::new(&mut env, 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.consume_up(&mut env);
|
||||||
ctap_state.u2f_up_state.grant_up(CtapInstant::new(0));
|
ctap_state.u2f_up_state.grant_up(&mut env);
|
||||||
let timeout_clock_value: CtapInstant =
|
env.clock().advance(TOUCH_TIMEOUT_MS);
|
||||||
CtapInstant::new((30001 * TEST_CLOCK_FREQUENCY_HZ as u64) / 1000);
|
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||||
let response =
|
|
||||||
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, timeout_clock_value);
|
|
||||||
assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED));
|
assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -494,15 +481,14 @@ mod test {
|
|||||||
env.user_presence()
|
env.user_presence()
|
||||||
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
||||||
let sk = PrivateKey::new(&mut env, SignatureAlgorithm::Es256);
|
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 rp_id = "example.com";
|
||||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
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 key_handle = encrypt_to_credential_id(&mut env, &sk, &application, None, None).unwrap();
|
||||||
let message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
let message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
||||||
|
|
||||||
let response =
|
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||||
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
|
|
||||||
assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED));
|
assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -512,7 +498,7 @@ mod test {
|
|||||||
env.user_presence()
|
env.user_presence()
|
||||||
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
||||||
let sk = PrivateKey::new(&mut env, SignatureAlgorithm::Es256);
|
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 rp_id = "example.com";
|
||||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||||
@@ -520,8 +506,7 @@ mod test {
|
|||||||
let application = [0x55; 32];
|
let application = [0x55; 32];
|
||||||
let message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
let message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
||||||
|
|
||||||
let response =
|
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||||
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
|
|
||||||
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_DATA));
|
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_DATA));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -531,7 +516,7 @@ mod test {
|
|||||||
env.user_presence()
|
env.user_presence()
|
||||||
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
||||||
let sk = PrivateKey::new(&mut env, SignatureAlgorithm::Es256);
|
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 rp_id = "example.com";
|
||||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||||
@@ -543,23 +528,19 @@ mod test {
|
|||||||
);
|
);
|
||||||
|
|
||||||
message.push(0x00);
|
message.push(0x00);
|
||||||
let response =
|
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||||
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
|
|
||||||
assert!(response.is_ok());
|
assert!(response.is_ok());
|
||||||
|
|
||||||
message.push(0x00);
|
message.push(0x00);
|
||||||
let response =
|
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||||
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
|
|
||||||
assert!(response.is_ok());
|
assert!(response.is_ok());
|
||||||
|
|
||||||
message.push(0x00);
|
message.push(0x00);
|
||||||
let response =
|
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||||
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
|
|
||||||
assert!(response.is_ok());
|
assert!(response.is_ok());
|
||||||
|
|
||||||
message.push(0x00);
|
message.push(0x00);
|
||||||
let response =
|
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||||
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
|
|
||||||
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_LENGTH));
|
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_LENGTH));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -569,7 +550,7 @@ mod test {
|
|||||||
env.user_presence()
|
env.user_presence()
|
||||||
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
||||||
let sk = PrivateKey::new(&mut env, SignatureAlgorithm::Es256);
|
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 rp_id = "example.com";
|
||||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||||
@@ -578,8 +559,7 @@ mod test {
|
|||||||
create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
||||||
message[0] = 0xEE;
|
message[0] = 0xEE;
|
||||||
|
|
||||||
let response =
|
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||||
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
|
|
||||||
assert_eq!(response, Err(Ctap1StatusCode::SW_CLA_INVALID));
|
assert_eq!(response, Err(Ctap1StatusCode::SW_CLA_INVALID));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -589,7 +569,7 @@ mod test {
|
|||||||
env.user_presence()
|
env.user_presence()
|
||||||
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
||||||
let sk = PrivateKey::new(&mut env, SignatureAlgorithm::Es256);
|
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 rp_id = "example.com";
|
||||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||||
@@ -598,8 +578,7 @@ mod test {
|
|||||||
create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
||||||
message[1] = 0xEE;
|
message[1] = 0xEE;
|
||||||
|
|
||||||
let response =
|
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||||
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
|
|
||||||
assert_eq!(response, Err(Ctap1StatusCode::SW_INS_INVALID));
|
assert_eq!(response, Err(Ctap1StatusCode::SW_INS_INVALID));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -609,7 +588,7 @@ mod test {
|
|||||||
env.user_presence()
|
env.user_presence()
|
||||||
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
||||||
let sk = PrivateKey::new(&mut env, SignatureAlgorithm::Es256);
|
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 rp_id = "example.com";
|
||||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||||
@@ -618,8 +597,7 @@ mod test {
|
|||||||
create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
||||||
message[2] = 0xEE;
|
message[2] = 0xEE;
|
||||||
|
|
||||||
let response =
|
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||||
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
|
|
||||||
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_DATA));
|
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_DATA));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -637,7 +615,7 @@ mod test {
|
|||||||
env.user_presence()
|
env.user_presence()
|
||||||
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
||||||
let sk = PrivateKey::new(&mut env, SignatureAlgorithm::Es256);
|
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 rp_id = "example.com";
|
||||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||||
@@ -645,11 +623,9 @@ mod test {
|
|||||||
let message =
|
let message =
|
||||||
create_authenticate_message(&application, Ctap1Flags::EnforceUpAndSign, &key_handle);
|
create_authenticate_message(&application, Ctap1Flags::EnforceUpAndSign, &key_handle);
|
||||||
|
|
||||||
ctap_state.u2f_up_state.consume_up(CtapInstant::new(0));
|
ctap_state.u2f_up_state.consume_up(&mut env);
|
||||||
ctap_state.u2f_up_state.grant_up(CtapInstant::new(0));
|
ctap_state.u2f_up_state.grant_up(&mut env);
|
||||||
let response =
|
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state).unwrap();
|
||||||
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0))
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(response[0], 0x01);
|
assert_eq!(response[0], 0x01);
|
||||||
let global_signature_counter = storage::global_signature_counter(&mut env).unwrap();
|
let global_signature_counter = storage::global_signature_counter(&mut env).unwrap();
|
||||||
check_signature_counter(
|
check_signature_counter(
|
||||||
@@ -665,7 +641,7 @@ mod test {
|
|||||||
env.user_presence()
|
env.user_presence()
|
||||||
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
||||||
let sk = PrivateKey::new(&mut env, SignatureAlgorithm::Es256);
|
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 rp_id = "example.com";
|
||||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||||
@@ -676,13 +652,8 @@ mod test {
|
|||||||
&key_handle,
|
&key_handle,
|
||||||
);
|
);
|
||||||
|
|
||||||
let response = Ctap1Command::process_command(
|
env.clock().advance(TOUCH_TIMEOUT_MS);
|
||||||
&mut env,
|
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state).unwrap();
|
||||||
&message,
|
|
||||||
&mut ctap_state,
|
|
||||||
CtapInstant::new((30001 * TEST_CLOCK_FREQUENCY_HZ as u64) / 1000),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(response[0], 0x01);
|
assert_eq!(response[0], 0x01);
|
||||||
let global_signature_counter = storage::global_signature_counter(&mut env).unwrap();
|
let global_signature_counter = storage::global_signature_counter(&mut env).unwrap();
|
||||||
check_signature_counter(
|
check_signature_counter(
|
||||||
@@ -702,12 +673,11 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
env.user_presence()
|
env.user_presence()
|
||||||
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
||||||
let mut ctap_state = CtapState::new(&mut env, 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.consume_up(&mut env);
|
||||||
ctap_state.u2f_up_state.grant_up(CtapInstant::new(0));
|
ctap_state.u2f_up_state.grant_up(&mut env);
|
||||||
let response =
|
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||||
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0));
|
|
||||||
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_DATA));
|
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_DATA));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -721,16 +691,12 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
env.user_presence()
|
env.user_presence()
|
||||||
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
.set(|| panic!("Unexpected user presence check in CTAP1"));
|
||||||
let mut ctap_state = CtapState::new(&mut env, 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.consume_up(&mut env);
|
||||||
ctap_state.u2f_up_state.grant_up(CtapInstant::new(0));
|
ctap_state.u2f_up_state.grant_up(&mut env);
|
||||||
let response = Ctap1Command::process_command(
|
env.clock().advance(TOUCH_TIMEOUT_MS);
|
||||||
&mut env,
|
let response = Ctap1Command::process_command(&mut env, &message, &mut ctap_state);
|
||||||
&message,
|
|
||||||
&mut ctap_state,
|
|
||||||
CtapInstant::new((30001 * TEST_CLOCK_FREQUENCY_HZ as u64) / 1000),
|
|
||||||
);
|
|
||||||
assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED));
|
assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,16 +21,29 @@ pub use self::receive::MessageAssembler;
|
|||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
use self::receive::MessageAssembler;
|
use self::receive::MessageAssembler;
|
||||||
pub use self::send::HidPacketIterator;
|
pub use self::send::HidPacketIterator;
|
||||||
use super::super::clock::{ClockInt, CtapInstant};
|
|
||||||
use super::status_code::Ctap2StatusCode;
|
use super::status_code::Ctap2StatusCode;
|
||||||
|
#[cfg(test)]
|
||||||
|
use crate::env::test::TestEnv;
|
||||||
use crate::env::Env;
|
use crate::env::Env;
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use arrayref::{array_ref, array_refs};
|
use arrayref::{array_ref, array_refs};
|
||||||
use embedded_time::duration::Milliseconds;
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use enum_iterator::IntoEnumIterator;
|
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 HidPacket = [u8; 64];
|
||||||
pub type ChannelID = [u8; 4];
|
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.
|
/// 2. If you didn't receive any message or preprocessing discarded it, stop.
|
||||||
/// 3. Handles all CTAP protocol interactions.
|
/// 3. Handles all CTAP protocol interactions.
|
||||||
/// 4. `split_message` creates packets out of the response message.
|
/// 4. `split_message` creates packets out of the response message.
|
||||||
pub struct CtapHid {
|
pub struct CtapHid<E: Env> {
|
||||||
assembler: MessageAssembler,
|
assembler: MessageAssembler<E>,
|
||||||
// The specification only requires unique CIDs, the allocation algorithm is vendor specific.
|
// The specification only requires unique CIDs, the allocation algorithm is vendor specific.
|
||||||
// We allocate them incrementally, that is all `cid` such that 1 <= cid <= allocated_cids are
|
// We allocate them incrementally, that is all `cid` such that 1 <= cid <= allocated_cids are
|
||||||
// allocated.
|
// allocated.
|
||||||
@@ -176,34 +189,17 @@ pub struct CtapHid {
|
|||||||
capabilities: u8,
|
capabilities: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CtapHid {
|
impl<E: Env> CtapHid<E> {
|
||||||
// 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;
|
|
||||||
|
|
||||||
pub const CAPABILITY_WINK: u8 = 0x01;
|
pub const CAPABILITY_WINK: u8 = 0x01;
|
||||||
pub const CAPABILITY_CBOR: u8 = 0x04;
|
pub const CAPABILITY_CBOR: u8 = 0x04;
|
||||||
#[cfg(any(not(feature = "with_ctap1"), feature = "vendor_hid"))]
|
#[cfg(any(not(feature = "with_ctap1"), feature = "vendor_hid"))]
|
||||||
pub const CAPABILITY_NMSG: u8 = 0x08;
|
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.
|
/// Creates a new CTAP HID packet parser.
|
||||||
///
|
///
|
||||||
/// The capabilities passed in are reported to the client in Init.
|
/// The capabilities passed in are reported to the client in Init.
|
||||||
pub fn new(capabilities: u8) -> CtapHid {
|
pub fn new(capabilities: u8) -> CtapHid<E> {
|
||||||
CtapHid {
|
Self {
|
||||||
assembler: MessageAssembler::new(),
|
assembler: MessageAssembler::new(),
|
||||||
allocated_cids: 0,
|
allocated_cids: 0,
|
||||||
capabilities,
|
capabilities,
|
||||||
@@ -228,14 +224,9 @@ impl CtapHid {
|
|||||||
/// You may ignore PING, it's behaving correctly by default (input == output).
|
/// 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
|
/// Ignoring the others is incorrect behavior. You have to at least replace them with an error
|
||||||
/// message:
|
/// message:
|
||||||
/// `CtapHid::error_message(message.cid, CtapHid::ERR_INVALID_CMD)`
|
/// `Self::error_message(message.cid, CtapHidError::InvalidCmd)`
|
||||||
pub fn parse_packet(
|
pub fn parse_packet(&mut self, env: &mut E, packet: &HidPacket) -> Option<Message> {
|
||||||
&mut self,
|
match self.assembler.parse_packet(env, packet) {
|
||||||
env: &mut impl Env,
|
|
||||||
packet: &HidPacket,
|
|
||||||
clock_value: CtapInstant,
|
|
||||||
) -> Option<Message> {
|
|
||||||
match self.assembler.parse_packet(env, packet, clock_value) {
|
|
||||||
Ok(Some(message)) => {
|
Ok(Some(message)) => {
|
||||||
debug_ctap!(env, "Received message: {:02x?}", message);
|
debug_ctap!(env, "Received message: {:02x?}", message);
|
||||||
self.preprocess_message(message)
|
self.preprocess_message(message)
|
||||||
@@ -248,9 +239,9 @@ impl CtapHid {
|
|||||||
if matches!(error, CtapHidError::UnexpectedContinuation) {
|
if matches!(error, CtapHidError::UnexpectedContinuation) {
|
||||||
None
|
None
|
||||||
} else if !self.is_allocated_channel(cid) {
|
} else if !self.is_allocated_channel(cid) {
|
||||||
Some(CtapHid::error_message(cid, CtapHidError::InvalidChannel))
|
Some(Self::error_message(cid, CtapHidError::InvalidChannel))
|
||||||
} else {
|
} 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> {
|
fn preprocess_message(&mut self, message: Message) -> Option<Message> {
|
||||||
let cid = message.cid;
|
let cid = message.cid;
|
||||||
if !self.has_valid_channel(&message) {
|
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 {
|
match message.cmd {
|
||||||
@@ -276,10 +267,10 @@ impl CtapHid {
|
|||||||
// CTAP 2.1 from 2021-06-15, section 11.2.9.1.3.
|
// CTAP 2.1 from 2021-06-15, section 11.2.9.1.3.
|
||||||
CtapHidCommand::Init => {
|
CtapHidCommand::Init => {
|
||||||
if message.payload.len() != 8 {
|
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.
|
// TODO: Prevent allocating 2^32 channels.
|
||||||
self.allocated_cids += 1;
|
self.allocated_cids += 1;
|
||||||
(self.allocated_cids as u32).to_be_bytes()
|
(self.allocated_cids as u32).to_be_bytes()
|
||||||
@@ -291,10 +282,10 @@ impl CtapHid {
|
|||||||
let mut payload = vec![0; 17];
|
let mut payload = vec![0; 17];
|
||||||
payload[..8].copy_from_slice(&message.payload);
|
payload[..8].copy_from_slice(&message.payload);
|
||||||
payload[8..12].copy_from_slice(&new_cid);
|
payload[8..12].copy_from_slice(&new_cid);
|
||||||
payload[12] = CtapHid::PROTOCOL_VERSION;
|
payload[12] = PROTOCOL_VERSION;
|
||||||
payload[13] = CtapHid::DEVICE_VERSION_MAJOR;
|
payload[13] = DEVICE_VERSION_MAJOR;
|
||||||
payload[14] = CtapHid::DEVICE_VERSION_MINOR;
|
payload[14] = DEVICE_VERSION_MINOR;
|
||||||
payload[15] = CtapHid::DEVICE_VERSION_BUILD;
|
payload[15] = DEVICE_VERSION_BUILD;
|
||||||
payload[16] = self.capabilities;
|
payload[16] = self.capabilities;
|
||||||
|
|
||||||
Some(Message {
|
Some(Message {
|
||||||
@@ -317,7 +308,7 @@ impl CtapHid {
|
|||||||
CtapHidCommand::Wink => Some(message),
|
CtapHidCommand::Wink => Some(message),
|
||||||
_ => {
|
_ => {
|
||||||
// Unknown or unsupported command.
|
// 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 {
|
fn has_valid_channel(&self, message: &Message) -> bool {
|
||||||
match message.cid {
|
match message.cid {
|
||||||
// Only INIT commands use the broadcast channel.
|
// 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.
|
// Check that the channel is allocated.
|
||||||
_ => self.is_allocated_channel(message.cid),
|
_ => self.is_allocated_channel(message.cid),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_allocated_channel(&self, cid: ChannelID) -> bool {
|
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 {
|
pub fn error_message(cid: ChannelID, error_code: CtapHidError) -> Message {
|
||||||
@@ -346,8 +337,8 @@ impl CtapHid {
|
|||||||
/// Helper function to parse a raw packet.
|
/// Helper function to parse a raw packet.
|
||||||
pub fn process_single_packet(packet: &HidPacket) -> (ChannelID, ProcessedPacket) {
|
pub fn process_single_packet(packet: &HidPacket) -> (ChannelID, ProcessedPacket) {
|
||||||
let (&cid, rest) = array_refs![packet, 4, 60];
|
let (&cid, rest) = array_refs![packet, 4, 60];
|
||||||
if rest[0] & CtapHid::PACKET_TYPE_MASK != 0 {
|
if rest[0] & PACKET_TYPE_MASK != 0 {
|
||||||
let cmd = rest[0] & !CtapHid::PACKET_TYPE_MASK;
|
let cmd = rest[0] & !PACKET_TYPE_MASK;
|
||||||
let len = (rest[1] as usize) << 8 | (rest[2] as usize);
|
let len = (rest[1] as usize) << 8 | (rest[2] as usize);
|
||||||
(
|
(
|
||||||
cid,
|
cid,
|
||||||
@@ -394,7 +385,7 @@ impl CtapHid {
|
|||||||
|
|
||||||
/// Generates the HID response packets for a keepalive status.
|
/// Generates the HID response packets for a keepalive status.
|
||||||
pub fn keepalive(cid: ChannelID, status: KeepaliveStatus) -> HidPacketIterator {
|
pub fn keepalive(cid: ChannelID, status: KeepaliveStatus) -> HidPacketIterator {
|
||||||
CtapHid::split_message(Message {
|
Self::split_message(Message {
|
||||||
cid,
|
cid,
|
||||||
cmd: CtapHidCommand::Keepalive,
|
cmd: CtapHidCommand::Keepalive,
|
||||||
payload: vec![status as u8],
|
payload: vec![status as u8],
|
||||||
@@ -402,9 +393,9 @@ impl CtapHid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn new_initialized() -> (CtapHid, ChannelID) {
|
pub fn new_initialized() -> (Self, ChannelID) {
|
||||||
(
|
(
|
||||||
CtapHid {
|
Self {
|
||||||
assembler: MessageAssembler::new(),
|
assembler: MessageAssembler::new(),
|
||||||
allocated_cids: 1,
|
allocated_cids: 1,
|
||||||
capabilities: 0x0D,
|
capabilities: 0x0D,
|
||||||
@@ -417,7 +408,6 @@ impl CtapHid {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::env::test::TestEnv;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_split_assemble() {
|
fn test_split_assemble() {
|
||||||
@@ -430,9 +420,9 @@ mod test {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut messages = Vec::new();
|
let mut messages = Vec::new();
|
||||||
let mut assembler = MessageAssembler::new();
|
let mut assembler = MessageAssembler::<TestEnv>::new();
|
||||||
for packet in HidPacketIterator::new(message.clone()).unwrap() {
|
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(Some(msg)) => messages.push(msg),
|
||||||
Ok(None) => (),
|
Ok(None) => (),
|
||||||
Err(_) => panic!("Couldn't assemble packet: {:02x?}", &packet as &[u8]),
|
Err(_) => panic!("Couldn't assemble packet: {:02x?}", &packet as &[u8]),
|
||||||
@@ -446,21 +436,18 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_spurious_continuation_packet() {
|
fn test_spurious_continuation_packet() {
|
||||||
let mut env = TestEnv::new();
|
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];
|
let mut packet = [0x00; 64];
|
||||||
packet[0..7].copy_from_slice(&[0xC1, 0xC1, 0xC1, 0xC1, 0x00, 0x51, 0x51]);
|
packet[0..7].copy_from_slice(&[0xC1, 0xC1, 0xC1, 0xC1, 0x00, 0x51, 0x51]);
|
||||||
// Continuation packets are silently ignored.
|
// Continuation packets are silently ignored.
|
||||||
assert_eq!(
|
assert_eq!(ctap_hid.parse_packet(&mut env, &packet), None);
|
||||||
ctap_hid.parse_packet(&mut env, &packet, CtapInstant::new(0)),
|
|
||||||
None
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_command_init() {
|
fn test_command_init() {
|
||||||
let mut ctap_hid = CtapHid::new(0x0D);
|
let mut ctap_hid = CtapHid::<TestEnv>::new(0x0D);
|
||||||
let init_message = Message {
|
let init_message = Message {
|
||||||
cid: CtapHid::CHANNEL_BROADCAST,
|
cid: CHANNEL_BROADCAST,
|
||||||
cmd: CtapHidCommand::Init,
|
cmd: CtapHidCommand::Init,
|
||||||
payload: vec![0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0],
|
payload: vec![0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0],
|
||||||
};
|
};
|
||||||
@@ -468,7 +455,7 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
reply,
|
reply,
|
||||||
Some(Message {
|
Some(Message {
|
||||||
cid: CtapHid::CHANNEL_BROADCAST,
|
cid: CHANNEL_BROADCAST,
|
||||||
cmd: CtapHidCommand::Init,
|
cmd: CtapHidCommand::Init,
|
||||||
payload: vec![
|
payload: vec![
|
||||||
0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, // Nonce
|
0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, // Nonce
|
||||||
@@ -484,7 +471,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_command_init_for_sync() {
|
fn test_command_init_for_sync() {
|
||||||
let mut env = TestEnv::new();
|
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.
|
// Ping packet with a length longer than one packet.
|
||||||
let mut packet1 = [0x51; 64];
|
let mut packet1 = [0x51; 64];
|
||||||
@@ -496,12 +483,9 @@ mod test {
|
|||||||
packet2[4..15].copy_from_slice(&[
|
packet2[4..15].copy_from_slice(&[
|
||||||
0x86, 0x00, 0x08, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0,
|
0x86, 0x00, 0x08, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0,
|
||||||
]);
|
]);
|
||||||
|
assert_eq!(ctap_hid.parse_packet(&mut env, &packet1), None);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ctap_hid.parse_packet(&mut env, &packet1, CtapInstant::new(0)),
|
ctap_hid.parse_packet(&mut env, &packet2),
|
||||||
None
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
ctap_hid.parse_packet(&mut env, &packet2, CtapInstant::new(0)),
|
|
||||||
Some(Message {
|
Some(Message {
|
||||||
cid,
|
cid,
|
||||||
cmd: CtapHidCommand::Init,
|
cmd: CtapHidCommand::Init,
|
||||||
@@ -519,13 +503,13 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_command_ping() {
|
fn test_command_ping() {
|
||||||
let mut env = TestEnv::new();
|
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];
|
let mut ping_packet = [0x00; 64];
|
||||||
ping_packet[..4].copy_from_slice(&cid);
|
ping_packet[..4].copy_from_slice(&cid);
|
||||||
ping_packet[4..9].copy_from_slice(&[0x81, 0x00, 0x02, 0x99, 0x99]);
|
ping_packet[4..9].copy_from_slice(&[0x81, 0x00, 0x02, 0x99, 0x99]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ctap_hid.parse_packet(&mut env, &ping_packet, CtapInstant::new(0)),
|
ctap_hid.parse_packet(&mut env, &ping_packet),
|
||||||
Some(Message {
|
Some(Message {
|
||||||
cid,
|
cid,
|
||||||
cmd: CtapHidCommand::Ping,
|
cmd: CtapHidCommand::Ping,
|
||||||
@@ -537,13 +521,13 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_command_cancel() {
|
fn test_command_cancel() {
|
||||||
let mut env = TestEnv::new();
|
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];
|
let mut cancel_packet = [0x00; 64];
|
||||||
cancel_packet[..4].copy_from_slice(&cid);
|
cancel_packet[..4].copy_from_slice(&cid);
|
||||||
cancel_packet[4..7].copy_from_slice(&[0x91, 0x00, 0x00]);
|
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);
|
assert_eq!(response, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -554,7 +538,7 @@ mod test {
|
|||||||
cmd: CtapHidCommand::Ping,
|
cmd: CtapHidCommand::Ping,
|
||||||
payload: vec![0x99, 0x99],
|
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];
|
let mut expected_packet = [0x00; 64];
|
||||||
expected_packet[..9]
|
expected_packet[..9]
|
||||||
.copy_from_slice(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x02, 0x99, 0x99]);
|
.copy_from_slice(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x02, 0x99, 0x99]);
|
||||||
@@ -570,7 +554,7 @@ mod test {
|
|||||||
cmd: CtapHidCommand::Cbor,
|
cmd: CtapHidCommand::Cbor,
|
||||||
payload,
|
payload,
|
||||||
};
|
};
|
||||||
let mut response = CtapHid::split_message(message);
|
let mut response = CtapHid::<TestEnv>::split_message(message);
|
||||||
let mut expected_packet = [0x00; 64];
|
let mut expected_packet = [0x00; 64];
|
||||||
expected_packet[..8].copy_from_slice(&[0x12, 0x34, 0x56, 0x78, 0x90, 0x00, 0x01, 0xF2]);
|
expected_packet[..8].copy_from_slice(&[0x12, 0x34, 0x56, 0x78, 0x90, 0x00, 0x01, 0xF2]);
|
||||||
assert_eq!(response.next(), Some(expected_packet));
|
assert_eq!(response.next(), Some(expected_packet));
|
||||||
@@ -581,7 +565,7 @@ mod test {
|
|||||||
fn test_keepalive() {
|
fn test_keepalive() {
|
||||||
for &status in [KeepaliveStatus::Processing, KeepaliveStatus::UpNeeded].iter() {
|
for &status in [KeepaliveStatus::Processing, KeepaliveStatus::UpNeeded].iter() {
|
||||||
let cid = [0x12, 0x34, 0x56, 0x78];
|
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];
|
let mut expected_packet = [0x00; 64];
|
||||||
expected_packet[..8].copy_from_slice(&[
|
expected_packet[..8].copy_from_slice(&[
|
||||||
0x12,
|
0x12,
|
||||||
@@ -604,7 +588,7 @@ mod test {
|
|||||||
let mut packet = [0x00; 64];
|
let mut packet = [0x00; 64];
|
||||||
packet[..4].copy_from_slice(&cid);
|
packet[..4].copy_from_slice(&cid);
|
||||||
packet[4..9].copy_from_slice(&[0x81, 0x00, 0x02, 0x99, 0x99]);
|
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);
|
assert_eq!(processed_cid, cid);
|
||||||
let expected_packet = ProcessedPacket::InitPacket {
|
let expected_packet = ProcessedPacket::InitPacket {
|
||||||
cmd: CtapHidCommand::Ping as u8,
|
cmd: CtapHidCommand::Ping as u8,
|
||||||
@@ -627,7 +611,7 @@ mod test {
|
|||||||
fn test_error_message() {
|
fn test_error_message() {
|
||||||
let cid = [0x12, 0x34, 0x56, 0x78];
|
let cid = [0x12, 0x34, 0x56, 0x78];
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
CtapHid::error_message(cid, CtapHidError::InvalidCmd),
|
CtapHid::<TestEnv>::error_message(cid, CtapHidError::InvalidCmd),
|
||||||
Message {
|
Message {
|
||||||
cid,
|
cid,
|
||||||
cmd: CtapHidCommand::Error,
|
cmd: CtapHidCommand::Error,
|
||||||
|
|||||||
@@ -15,20 +15,23 @@
|
|||||||
use super::{
|
use super::{
|
||||||
ChannelID, CtapHid, CtapHidCommand, CtapHidError, HidPacket, Message, ProcessedPacket,
|
ChannelID, CtapHid, CtapHidCommand, CtapHidError, HidPacket, Message, ProcessedPacket,
|
||||||
};
|
};
|
||||||
|
use crate::api::clock::Clock;
|
||||||
use crate::api::customization::Customization;
|
use crate::api::customization::Customization;
|
||||||
use crate::clock::CtapInstant;
|
|
||||||
use crate::env::Env;
|
use crate::env::Env;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use core::mem::swap;
|
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.
|
/// 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.
|
// Whether this is waiting to receive an initialization packet.
|
||||||
idle: bool,
|
idle: bool,
|
||||||
// Current channel ID.
|
// Current channel ID.
|
||||||
cid: ChannelID,
|
cid: ChannelID,
|
||||||
// Timestamp of the last packet received on the current channel.
|
// Timestamp of the last packet received on the current channel.
|
||||||
last_timestamp: CtapInstant,
|
timer: <E::Clock as Clock>::Timer,
|
||||||
// Current command.
|
// Current command.
|
||||||
cmd: u8,
|
cmd: u8,
|
||||||
// Sequence number expected for the next packet.
|
// Sequence number expected for the next packet.
|
||||||
@@ -39,12 +42,12 @@ pub struct MessageAssembler {
|
|||||||
payload: Vec<u8>,
|
payload: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MessageAssembler {
|
impl<E: Env> MessageAssembler<E> {
|
||||||
pub fn new() -> MessageAssembler {
|
pub fn new() -> MessageAssembler<E> {
|
||||||
MessageAssembler {
|
MessageAssembler {
|
||||||
idle: true,
|
idle: true,
|
||||||
cid: [0, 0, 0, 0],
|
cid: [0, 0, 0, 0],
|
||||||
last_timestamp: CtapInstant::new(0),
|
timer: <E::Clock as Clock>::Timer::default(),
|
||||||
cmd: 0,
|
cmd: 0,
|
||||||
seq: 0,
|
seq: 0,
|
||||||
remaining_payload_len: 0,
|
remaining_payload_len: 0,
|
||||||
@@ -57,7 +60,7 @@ impl MessageAssembler {
|
|||||||
fn reset(&mut self) {
|
fn reset(&mut self) {
|
||||||
self.idle = true;
|
self.idle = true;
|
||||||
self.cid = [0, 0, 0, 0];
|
self.cid = [0, 0, 0, 0];
|
||||||
self.last_timestamp = CtapInstant::new(0);
|
self.timer = <E::Clock as Clock>::Timer::default();
|
||||||
self.cmd = 0;
|
self.cmd = 0;
|
||||||
self.seq = 0;
|
self.seq = 0;
|
||||||
self.remaining_payload_len = 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
|
// full message was assembled after this packet, or None if more packets are needed to fill the
|
||||||
// message.
|
// message.
|
||||||
// - An Err() result if there was a parsing error.
|
// - 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(
|
pub fn parse_packet(
|
||||||
&mut self,
|
&mut self,
|
||||||
env: &mut impl Env,
|
env: &mut E,
|
||||||
packet: &HidPacket,
|
packet: &HidPacket,
|
||||||
timestamp: CtapInstant,
|
|
||||||
) -> Result<Option<Message>, (ChannelID, CtapHidError)> {
|
) -> Result<Option<Message>, (ChannelID, CtapHidError)> {
|
||||||
// TODO: Support non-full-speed devices (i.e. packet len != 64)? This isn't recommended by
|
// TODO: Support non-full-speed devices (i.e. packet len != 64)? This isn't recommended by
|
||||||
// section 8.8.1
|
// 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.
|
// The current channel timed out.
|
||||||
// Save the channel ID and reset the state.
|
// Save the channel ID and reset the state.
|
||||||
let current_cid = self.cid;
|
let current_cid = self.cid;
|
||||||
@@ -98,7 +98,7 @@ impl MessageAssembler {
|
|||||||
// Expecting an initialization packet.
|
// Expecting an initialization packet.
|
||||||
match processed_packet {
|
match processed_packet {
|
||||||
ProcessedPacket::InitPacket { cmd, len, data } => {
|
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 { .. } => {
|
ProcessedPacket::ContinuationPacket { .. } => {
|
||||||
// CTAP specification (version 20190130) section 8.1.5.4
|
// CTAP specification (version 20190130) section 8.1.5.4
|
||||||
@@ -120,7 +120,7 @@ impl MessageAssembler {
|
|||||||
ProcessedPacket::InitPacket { cmd, len, data } => {
|
ProcessedPacket::InitPacket { cmd, len, data } => {
|
||||||
self.reset();
|
self.reset();
|
||||||
if cmd == CtapHidCommand::Init as u8 {
|
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 {
|
} else {
|
||||||
Err((cid, CtapHidError::InvalidSeq))
|
Err((cid, CtapHidError::InvalidSeq))
|
||||||
}
|
}
|
||||||
@@ -132,7 +132,7 @@ impl MessageAssembler {
|
|||||||
Err((cid, CtapHidError::InvalidSeq))
|
Err((cid, CtapHidError::InvalidSeq))
|
||||||
} else {
|
} else {
|
||||||
// Update the last timestamp.
|
// 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.
|
// Increment the sequence number for the next packet.
|
||||||
self.seq += 1;
|
self.seq += 1;
|
||||||
Ok(self.append_payload(data))
|
Ok(self.append_payload(data))
|
||||||
@@ -144,12 +144,11 @@ impl MessageAssembler {
|
|||||||
|
|
||||||
fn parse_init_packet(
|
fn parse_init_packet(
|
||||||
&mut self,
|
&mut self,
|
||||||
env: &mut impl Env,
|
env: &mut E,
|
||||||
cid: ChannelID,
|
cid: ChannelID,
|
||||||
cmd: u8,
|
cmd: u8,
|
||||||
len: usize,
|
len: usize,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
timestamp: CtapInstant,
|
|
||||||
) -> Result<Option<Message>, (ChannelID, CtapHidError)> {
|
) -> Result<Option<Message>, (ChannelID, CtapHidError)> {
|
||||||
// Reject invalid lengths early to reduce the risk of running out of memory.
|
// Reject invalid lengths early to reduce the risk of running out of memory.
|
||||||
// TODO: also reject invalid commands early?
|
// TODO: also reject invalid commands early?
|
||||||
@@ -157,7 +156,7 @@ impl MessageAssembler {
|
|||||||
return Err((cid, CtapHidError::InvalidLen));
|
return Err((cid, CtapHidError::InvalidLen));
|
||||||
}
|
}
|
||||||
self.cid = cid;
|
self.cid = cid;
|
||||||
self.last_timestamp = timestamp;
|
self.timer = env.clock().make_timer(TIMEOUT_DURATION_MS);
|
||||||
self.cmd = cmd;
|
self.cmd = cmd;
|
||||||
self.seq = 0;
|
self.seq = 0;
|
||||||
self.remaining_payload_len = len;
|
self.remaining_payload_len = len;
|
||||||
@@ -187,9 +186,7 @@ impl MessageAssembler {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::ctap::hid::CtapHid;
|
|
||||||
use crate::env::test::TestEnv;
|
use crate::env::test::TestEnv;
|
||||||
use embedded_time::duration::Milliseconds;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@@ -212,14 +209,8 @@ mod test {
|
|||||||
fn test_empty_payload() {
|
fn test_empty_payload() {
|
||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let mut assembler = MessageAssembler::new();
|
let mut assembler = MessageAssembler::new();
|
||||||
// Except for tests that exercise timeouts, all packets are synchronized at the same dummy
|
|
||||||
// timestamp.
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
assembler.parse_packet(
|
assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x90])),
|
||||||
&mut env,
|
|
||||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x90]),
|
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
|
||||||
Ok(Some(Message {
|
Ok(Some(Message {
|
||||||
cid: [0x12, 0x34, 0x56, 0x78],
|
cid: [0x12, 0x34, 0x56, 0x78],
|
||||||
cmd: CtapHidCommand::Cbor,
|
cmd: CtapHidCommand::Cbor,
|
||||||
@@ -236,7 +227,6 @@ mod test {
|
|||||||
assembler.parse_packet(
|
assembler.parse_packet(
|
||||||
&mut env,
|
&mut env,
|
||||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x90, 0x00, 0x10]),
|
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x90, 0x00, 0x10]),
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
),
|
||||||
Ok(Some(Message {
|
Ok(Some(Message {
|
||||||
cid: [0x12, 0x34, 0x56, 0x78],
|
cid: [0x12, 0x34, 0x56, 0x78],
|
||||||
@@ -257,7 +247,6 @@ mod test {
|
|||||||
assembler.parse_packet(
|
assembler.parse_packet(
|
||||||
&mut env,
|
&mut env,
|
||||||
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x90, 0x00, 0x10], 0xFF),
|
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x90, 0x00, 0x10], 0xFF),
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
),
|
||||||
Ok(Some(Message {
|
Ok(Some(Message {
|
||||||
cid: [0x12, 0x34, 0x56, 0x78],
|
cid: [0x12, 0x34, 0x56, 0x78],
|
||||||
@@ -275,16 +264,11 @@ mod test {
|
|||||||
assembler.parse_packet(
|
assembler.parse_packet(
|
||||||
&mut env,
|
&mut env,
|
||||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]),
|
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]),
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
),
|
||||||
Ok(None)
|
Ok(None)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
assembler.parse_packet(
|
assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00])),
|
||||||
&mut env,
|
|
||||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]),
|
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
|
||||||
Ok(Some(Message {
|
Ok(Some(Message {
|
||||||
cid: [0x12, 0x34, 0x56, 0x78],
|
cid: [0x12, 0x34, 0x56, 0x78],
|
||||||
cmd: CtapHidCommand::Ping,
|
cmd: CtapHidCommand::Ping,
|
||||||
@@ -301,24 +285,15 @@ mod test {
|
|||||||
assembler.parse_packet(
|
assembler.parse_packet(
|
||||||
&mut env,
|
&mut env,
|
||||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x80]),
|
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x80]),
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
),
|
||||||
Ok(None)
|
Ok(None)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
assembler.parse_packet(
|
assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00])),
|
||||||
&mut env,
|
|
||||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]),
|
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
assembler.parse_packet(
|
assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x01])),
|
||||||
&mut env,
|
|
||||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x01]),
|
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
|
||||||
Ok(Some(Message {
|
Ok(Some(Message {
|
||||||
cid: [0x12, 0x34, 0x56, 0x78],
|
cid: [0x12, 0x34, 0x56, 0x78],
|
||||||
cmd: CtapHidCommand::Ping,
|
cmd: CtapHidCommand::Ping,
|
||||||
@@ -335,26 +310,17 @@ mod test {
|
|||||||
assembler.parse_packet(
|
assembler.parse_packet(
|
||||||
&mut env,
|
&mut env,
|
||||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x1D, 0xB9]),
|
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x1D, 0xB9]),
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
),
|
||||||
Ok(None)
|
Ok(None)
|
||||||
);
|
);
|
||||||
for seq in 0..0x7F {
|
for seq in 0..0x7F {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
assembler.parse_packet(
|
assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, seq])),
|
||||||
&mut env,
|
|
||||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, seq]),
|
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
assembler.parse_packet(
|
assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x7F])),
|
||||||
&mut env,
|
|
||||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x7F]),
|
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
|
||||||
Ok(Some(Message {
|
Ok(Some(Message {
|
||||||
cid: [0x12, 0x34, 0x56, 0x78],
|
cid: [0x12, 0x34, 0x56, 0x78],
|
||||||
cmd: CtapHidCommand::Ping,
|
cmd: CtapHidCommand::Ping,
|
||||||
@@ -380,7 +346,6 @@ mod test {
|
|||||||
&[0x12, 0x34, 0x56, 0x78, 0x80 | cmd as u8, 0x00, 0x80],
|
&[0x12, 0x34, 0x56, 0x78, 0x80 | cmd as u8, 0x00, 0x80],
|
||||||
byte
|
byte
|
||||||
),
|
),
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
),
|
||||||
Ok(None)
|
Ok(None)
|
||||||
);
|
);
|
||||||
@@ -388,7 +353,6 @@ mod test {
|
|||||||
assembler.parse_packet(
|
assembler.parse_packet(
|
||||||
&mut env,
|
&mut env,
|
||||||
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x00], byte),
|
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x00], byte),
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
),
|
||||||
Ok(None)
|
Ok(None)
|
||||||
);
|
);
|
||||||
@@ -396,7 +360,6 @@ mod test {
|
|||||||
assembler.parse_packet(
|
assembler.parse_packet(
|
||||||
&mut env,
|
&mut env,
|
||||||
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x01], byte),
|
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x01], byte),
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
),
|
||||||
Ok(Some(Message {
|
Ok(Some(Message {
|
||||||
cid: [0x12, 0x34, 0x56, 0x78],
|
cid: [0x12, 0x34, 0x56, 0x78],
|
||||||
@@ -422,24 +385,17 @@ mod test {
|
|||||||
assembler.parse_packet(
|
assembler.parse_packet(
|
||||||
&mut env,
|
&mut env,
|
||||||
&byte_extend(&[0x12, 0x34, 0x56, cid, 0x80 | cmd as u8, 0x00, 0x80], byte),
|
&byte_extend(&[0x12, 0x34, 0x56, cid, 0x80 | cmd as u8, 0x00, 0x80], byte),
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
),
|
||||||
Ok(None)
|
Ok(None)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
assembler.parse_packet(
|
assembler
|
||||||
&mut env,
|
.parse_packet(&mut env, &byte_extend(&[0x12, 0x34, 0x56, cid, 0x00], byte)),
|
||||||
&byte_extend(&[0x12, 0x34, 0x56, cid, 0x00], byte),
|
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
assembler.parse_packet(
|
assembler
|
||||||
&mut env,
|
.parse_packet(&mut env, &byte_extend(&[0x12, 0x34, 0x56, cid, 0x01], byte)),
|
||||||
&byte_extend(&[0x12, 0x34, 0x56, cid, 0x01], byte),
|
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
|
||||||
Ok(Some(Message {
|
Ok(Some(Message {
|
||||||
cid: [0x12, 0x34, 0x56, cid],
|
cid: [0x12, 0x34, 0x56, cid],
|
||||||
cmd,
|
cmd,
|
||||||
@@ -457,7 +413,6 @@ mod test {
|
|||||||
assembler.parse_packet(
|
assembler.parse_packet(
|
||||||
&mut env,
|
&mut env,
|
||||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]),
|
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]),
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
),
|
||||||
Ok(None)
|
Ok(None)
|
||||||
);
|
);
|
||||||
@@ -470,7 +425,6 @@ mod test {
|
|||||||
assembler.parse_packet(
|
assembler.parse_packet(
|
||||||
&mut env,
|
&mut env,
|
||||||
&byte_extend(&[0x12, 0x34, 0x56, 0x9A, cmd as u8, 0x00], byte),
|
&byte_extend(&[0x12, 0x34, 0x56, 0x9A, cmd as u8, 0x00], byte),
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
),
|
||||||
Err(([0x12, 0x34, 0x56, 0x9A], CtapHidError::ChannelBusy))
|
Err(([0x12, 0x34, 0x56, 0x9A], CtapHidError::ChannelBusy))
|
||||||
);
|
);
|
||||||
@@ -478,11 +432,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
assembler.parse_packet(
|
assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00])),
|
||||||
&mut env,
|
|
||||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]),
|
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
|
||||||
Ok(Some(Message {
|
Ok(Some(Message {
|
||||||
cid: [0x12, 0x34, 0x56, 0x78],
|
cid: [0x12, 0x34, 0x56, 0x78],
|
||||||
cmd: CtapHidCommand::Ping,
|
cmd: CtapHidCommand::Ping,
|
||||||
@@ -505,7 +455,6 @@ mod test {
|
|||||||
assembler.parse_packet(
|
assembler.parse_packet(
|
||||||
&mut env,
|
&mut env,
|
||||||
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x10], byte),
|
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x10], byte),
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
),
|
||||||
Ok(Some(Message {
|
Ok(Some(Message {
|
||||||
cid: [0x12, 0x34, 0x56, 0x78],
|
cid: [0x12, 0x34, 0x56, 0x78],
|
||||||
@@ -517,11 +466,7 @@ mod test {
|
|||||||
// Spurious continuation packet.
|
// Spurious continuation packet.
|
||||||
let seq = i;
|
let seq = i;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
assembler.parse_packet(
|
assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, seq])),
|
||||||
&mut env,
|
|
||||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, seq]),
|
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
|
||||||
Err((
|
Err((
|
||||||
[0x12, 0x34, 0x56, 0x78],
|
[0x12, 0x34, 0x56, 0x78],
|
||||||
CtapHidError::UnexpectedContinuation
|
CtapHidError::UnexpectedContinuation
|
||||||
@@ -538,16 +483,11 @@ mod test {
|
|||||||
assembler.parse_packet(
|
assembler.parse_packet(
|
||||||
&mut env,
|
&mut env,
|
||||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]),
|
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]),
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
),
|
||||||
Ok(None)
|
Ok(None)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
assembler.parse_packet(
|
assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x80])),
|
||||||
&mut env,
|
|
||||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x80]),
|
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
|
||||||
Err(([0x12, 0x34, 0x56, 0x78], CtapHidError::InvalidSeq))
|
Err(([0x12, 0x34, 0x56, 0x78], CtapHidError::InvalidSeq))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -560,16 +500,11 @@ mod test {
|
|||||||
assembler.parse_packet(
|
assembler.parse_packet(
|
||||||
&mut env,
|
&mut env,
|
||||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]),
|
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]),
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
),
|
||||||
Ok(None)
|
Ok(None)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
assembler.parse_packet(
|
assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x01])),
|
||||||
&mut env,
|
|
||||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x01]),
|
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
|
||||||
Err(([0x12, 0x34, 0x56, 0x78], CtapHidError::InvalidSeq))
|
Err(([0x12, 0x34, 0x56, 0x78], CtapHidError::InvalidSeq))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -582,16 +517,12 @@ mod test {
|
|||||||
assembler.parse_packet(
|
assembler.parse_packet(
|
||||||
&mut env,
|
&mut env,
|
||||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]),
|
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]),
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
),
|
||||||
Ok(None)
|
Ok(None)
|
||||||
);
|
);
|
||||||
|
env.clock().advance(TIMEOUT_DURATION_MS);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
assembler.parse_packet(
|
assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00])),
|
||||||
&mut env,
|
|
||||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]),
|
|
||||||
CtapInstant::new(0) + CtapHid::TIMEOUT_DURATION
|
|
||||||
),
|
|
||||||
Err(([0x12, 0x34, 0x56, 0x78], CtapHidError::MsgTimeout))
|
Err(([0x12, 0x34, 0x56, 0x78], CtapHidError::MsgTimeout))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -599,37 +530,27 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_just_in_time_packets() {
|
fn test_just_in_time_packets() {
|
||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let mut timestamp: CtapInstant = CtapInstant::new(0);
|
|
||||||
// Delay between each packet is just below the threshold.
|
// 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();
|
let mut assembler = MessageAssembler::new();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
assembler.parse_packet(
|
assembler.parse_packet(
|
||||||
&mut env,
|
&mut env,
|
||||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x1D, 0xB9]),
|
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x1D, 0xB9]),
|
||||||
timestamp
|
|
||||||
),
|
),
|
||||||
Ok(None)
|
Ok(None)
|
||||||
);
|
);
|
||||||
for seq in 0..0x7F {
|
for seq in 0..0x7F {
|
||||||
timestamp = timestamp + delay;
|
env.clock().advance(delay);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
assembler.parse_packet(
|
assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, seq])),
|
||||||
&mut env,
|
|
||||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, seq]),
|
|
||||||
timestamp
|
|
||||||
),
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
timestamp = timestamp + delay;
|
env.clock().advance(delay);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
assembler.parse_packet(
|
assembler.parse_packet(&mut env, &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x7F])),
|
||||||
&mut env,
|
|
||||||
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x7F]),
|
|
||||||
timestamp
|
|
||||||
),
|
|
||||||
Ok(Some(Message {
|
Ok(Some(Message {
|
||||||
cid: [0x12, 0x34, 0x56, 0x78],
|
cid: [0x12, 0x34, 0x56, 0x78],
|
||||||
cmd: CtapHidCommand::Ping,
|
cmd: CtapHidCommand::Ping,
|
||||||
@@ -647,7 +568,6 @@ mod test {
|
|||||||
assembler.parse_packet(
|
assembler.parse_packet(
|
||||||
&mut env,
|
&mut env,
|
||||||
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x02, 0x00], 0x51),
|
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x02, 0x00], 0x51),
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
),
|
||||||
Ok(None)
|
Ok(None)
|
||||||
);
|
);
|
||||||
@@ -659,7 +579,6 @@ mod test {
|
|||||||
0x12, 0x34, 0x56, 0x78, 0x86, 0x00, 0x08, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC,
|
0x12, 0x34, 0x56, 0x78, 0x86, 0x00, 0x08, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC,
|
||||||
0xDE, 0xF0
|
0xDE, 0xF0
|
||||||
]),
|
]),
|
||||||
CtapInstant::new(0)
|
|
||||||
),
|
),
|
||||||
Ok(Some(Message {
|
Ok(Some(Message {
|
||||||
cid: [0x12, 0x34, 0x56, 0x78],
|
cid: [0x12, 0x34, 0x56, 0x78],
|
||||||
|
|||||||
@@ -12,7 +12,9 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use super::{CtapHid, HidPacket, Message};
|
use super::{HidPacket, Message};
|
||||||
|
|
||||||
|
const TYPE_INIT_BIT: u8 = 0x80;
|
||||||
|
|
||||||
/// Iterator for HID packets.
|
/// Iterator for HID packets.
|
||||||
///
|
///
|
||||||
@@ -121,7 +123,7 @@ impl Iterator for MessageSplitter {
|
|||||||
match self.seq {
|
match self.seq {
|
||||||
None => {
|
None => {
|
||||||
// First, send an initialization packet.
|
// 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[5] = (payload_len >> 8) as u8;
|
||||||
self.packet[6] = payload_len as u8;
|
self.packet[6] = payload_len as u8;
|
||||||
|
|
||||||
|
|||||||
@@ -45,10 +45,10 @@ impl LargeBlobs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Process the large blob command.
|
/// Process the large blob command.
|
||||||
pub fn process_command(
|
pub fn process_command<E: Env>(
|
||||||
&mut self,
|
&mut self,
|
||||||
env: &mut impl Env,
|
env: &mut E,
|
||||||
client_pin: &mut ClientPin,
|
client_pin: &mut ClientPin<E>,
|
||||||
large_blobs_params: AuthenticatorLargeBlobsParameters,
|
large_blobs_params: AuthenticatorLargeBlobsParameters,
|
||||||
) -> Result<ResponseData, Ctap2StatusCode> {
|
) -> Result<ResponseData, Ctap2StatusCode> {
|
||||||
let AuthenticatorLargeBlobsParameters {
|
let AuthenticatorLargeBlobsParameters {
|
||||||
@@ -147,8 +147,12 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
&mut env,
|
||||||
|
key_agreement_key,
|
||||||
|
pin_uv_auth_token,
|
||||||
|
PinUvAuthProtocol::V1,
|
||||||
|
);
|
||||||
let mut large_blobs = LargeBlobs::new();
|
let mut large_blobs = LargeBlobs::new();
|
||||||
|
|
||||||
let large_blob = vec![
|
let large_blob = vec![
|
||||||
@@ -178,8 +182,12 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
&mut env,
|
||||||
|
key_agreement_key,
|
||||||
|
pin_uv_auth_token,
|
||||||
|
PinUvAuthProtocol::V1,
|
||||||
|
);
|
||||||
let mut large_blobs = LargeBlobs::new();
|
let mut large_blobs = LargeBlobs::new();
|
||||||
|
|
||||||
const BLOB_LEN: usize = 200;
|
const BLOB_LEN: usize = 200;
|
||||||
@@ -240,8 +248,12 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
&mut env,
|
||||||
|
key_agreement_key,
|
||||||
|
pin_uv_auth_token,
|
||||||
|
PinUvAuthProtocol::V1,
|
||||||
|
);
|
||||||
let mut large_blobs = LargeBlobs::new();
|
let mut large_blobs = LargeBlobs::new();
|
||||||
|
|
||||||
const BLOB_LEN: usize = 200;
|
const BLOB_LEN: usize = 200;
|
||||||
@@ -286,8 +298,12 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
&mut env,
|
||||||
|
key_agreement_key,
|
||||||
|
pin_uv_auth_token,
|
||||||
|
PinUvAuthProtocol::V1,
|
||||||
|
);
|
||||||
let mut large_blobs = LargeBlobs::new();
|
let mut large_blobs = LargeBlobs::new();
|
||||||
|
|
||||||
const BLOB_LEN: usize = 200;
|
const BLOB_LEN: usize = 200;
|
||||||
@@ -332,8 +348,12 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
&mut env,
|
||||||
|
key_agreement_key,
|
||||||
|
pin_uv_auth_token,
|
||||||
|
PinUvAuthProtocol::V1,
|
||||||
|
);
|
||||||
let mut large_blobs = LargeBlobs::new();
|
let mut large_blobs = LargeBlobs::new();
|
||||||
|
|
||||||
let large_blobs_params = AuthenticatorLargeBlobsParameters {
|
let large_blobs_params = AuthenticatorLargeBlobsParameters {
|
||||||
@@ -355,8 +375,12 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
&mut env,
|
||||||
|
key_agreement_key,
|
||||||
|
pin_uv_auth_token,
|
||||||
|
PinUvAuthProtocol::V1,
|
||||||
|
);
|
||||||
let mut large_blobs = LargeBlobs::new();
|
let mut large_blobs = LargeBlobs::new();
|
||||||
|
|
||||||
const BLOB_LEN: usize = 20;
|
const BLOB_LEN: usize = 20;
|
||||||
@@ -383,8 +407,12 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin =
|
let mut client_pin = ClientPin::<TestEnv>::new_test(
|
||||||
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol);
|
&mut env,
|
||||||
|
key_agreement_key,
|
||||||
|
pin_uv_auth_token,
|
||||||
|
pin_uv_auth_protocol,
|
||||||
|
);
|
||||||
let mut large_blobs = LargeBlobs::new();
|
let mut large_blobs = LargeBlobs::new();
|
||||||
|
|
||||||
const BLOB_LEN: usize = 20;
|
const BLOB_LEN: usize = 20;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::clock::{ClockInt, CtapInstant};
|
use crate::api::clock::Clock;
|
||||||
#[cfg(feature = "with_ctap1")]
|
#[cfg(feature = "with_ctap1")]
|
||||||
use crate::ctap::ctap1;
|
use crate::ctap::ctap1;
|
||||||
#[cfg(feature = "with_ctap1")]
|
#[cfg(feature = "with_ctap1")]
|
||||||
@@ -20,29 +20,29 @@ use crate::ctap::hid::ChannelID;
|
|||||||
use crate::ctap::hid::{
|
use crate::ctap::hid::{
|
||||||
CtapHid, CtapHidCommand, CtapHidError, HidPacket, HidPacketIterator, Message,
|
CtapHid, CtapHidCommand, CtapHidError, HidPacket, HidPacketIterator, Message,
|
||||||
};
|
};
|
||||||
use crate::ctap::{Channel, CtapState, TimedPermission};
|
use crate::ctap::{Channel, CtapState};
|
||||||
use crate::env::Env;
|
use crate::env::Env;
|
||||||
use embedded_time::duration::Milliseconds;
|
|
||||||
|
const WINK_TIMEOUT_DURATION_MS: usize = 5000;
|
||||||
|
|
||||||
/// Implements the standard CTAP command processing for HID.
|
/// Implements the standard CTAP command processing for HID.
|
||||||
pub struct MainHid {
|
pub struct MainHid<E: Env> {
|
||||||
hid: CtapHid,
|
hid: CtapHid<E>,
|
||||||
wink_permission: TimedPermission,
|
wink_permission: <E::Clock as Clock>::Timer,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MainHid {
|
impl<E: Env> MainHid<E> {
|
||||||
const WINK_TIMEOUT_DURATION: Milliseconds<ClockInt> = Milliseconds(5000 as ClockInt);
|
|
||||||
|
|
||||||
/// Instantiates a HID handler for CTAP1, CTAP2 and Wink.
|
/// Instantiates a HID handler for CTAP1, CTAP2 and Wink.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
#[cfg(feature = "with_ctap1")]
|
#[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"))]
|
#[cfg(not(feature = "with_ctap1"))]
|
||||||
let capabilities =
|
let capabilities = CtapHid::<E>::CAPABILITY_WINK
|
||||||
CtapHid::CAPABILITY_WINK | CtapHid::CAPABILITY_CBOR | CtapHid::CAPABILITY_NMSG;
|
| CtapHid::<E>::CAPABILITY_CBOR
|
||||||
|
| CtapHid::<E>::CAPABILITY_NMSG;
|
||||||
|
|
||||||
let hid = CtapHid::new(capabilities);
|
let hid = CtapHid::new(capabilities);
|
||||||
let wink_permission = TimedPermission::waiting();
|
let wink_permission = <E::Clock as Clock>::Timer::default();
|
||||||
MainHid {
|
MainHid {
|
||||||
hid,
|
hid,
|
||||||
wink_permission,
|
wink_permission,
|
||||||
@@ -52,15 +52,14 @@ impl MainHid {
|
|||||||
/// Processes an incoming USB HID packet, and returns an iterator for all outgoing packets.
|
/// Processes an incoming USB HID packet, and returns an iterator for all outgoing packets.
|
||||||
pub fn process_hid_packet(
|
pub fn process_hid_packet(
|
||||||
&mut self,
|
&mut self,
|
||||||
env: &mut impl Env,
|
env: &mut E,
|
||||||
packet: &HidPacket,
|
packet: &HidPacket,
|
||||||
now: CtapInstant,
|
ctap_state: &mut CtapState<E>,
|
||||||
ctap_state: &mut CtapState,
|
|
||||||
) -> HidPacketIterator {
|
) -> HidPacketIterator {
|
||||||
if let Some(message) = self.hid.parse_packet(env, packet, now) {
|
if let Some(message) = self.hid.parse_packet(env, packet) {
|
||||||
let processed_message = self.process_message(env, message, now, ctap_state);
|
let processed_message = self.process_message(env, message, ctap_state);
|
||||||
debug_ctap!(env, "Sending message: {:02x?}", processed_message);
|
debug_ctap!(env, "Sending message: {:02x?}", processed_message);
|
||||||
CtapHid::split_message(processed_message)
|
CtapHid::<E>::split_message(processed_message)
|
||||||
} else {
|
} else {
|
||||||
HidPacketIterator::none()
|
HidPacketIterator::none()
|
||||||
}
|
}
|
||||||
@@ -69,13 +68,12 @@ impl MainHid {
|
|||||||
/// Processes a message's commands that affect the protocol outside HID.
|
/// Processes a message's commands that affect the protocol outside HID.
|
||||||
pub fn process_message(
|
pub fn process_message(
|
||||||
&mut self,
|
&mut self,
|
||||||
env: &mut impl Env,
|
env: &mut E,
|
||||||
message: Message,
|
message: Message,
|
||||||
now: CtapInstant,
|
ctap_state: &mut CtapState<E>,
|
||||||
ctap_state: &mut CtapState,
|
|
||||||
) -> Message {
|
) -> Message {
|
||||||
// If another command arrives, stop winking to prevent accidential button touches.
|
// 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;
|
let cid = message.cid;
|
||||||
match message.cmd {
|
match message.cmd {
|
||||||
@@ -83,12 +81,12 @@ impl MainHid {
|
|||||||
CtapHidCommand::Msg => {
|
CtapHidCommand::Msg => {
|
||||||
// If we don't have CTAP1 backward compatibilty, this command is invalid.
|
// If we don't have CTAP1 backward compatibilty, this command is invalid.
|
||||||
#[cfg(not(feature = "with_ctap1"))]
|
#[cfg(not(feature = "with_ctap1"))]
|
||||||
return CtapHid::error_message(cid, CtapHidError::InvalidCmd);
|
return CtapHid::<E>::error_message(cid, CtapHidError::InvalidCmd);
|
||||||
|
|
||||||
#[cfg(feature = "with_ctap1")]
|
#[cfg(feature = "with_ctap1")]
|
||||||
match ctap1::Ctap1Command::process_command(env, &message.payload, ctap_state, now) {
|
match ctap1::Ctap1Command::process_command(env, &message.payload, ctap_state) {
|
||||||
Ok(payload) => MainHid::ctap1_success_message(cid, &payload),
|
Ok(payload) => Self::ctap1_success_message(cid, &payload),
|
||||||
Err(ctap1_status_code) => MainHid::ctap1_error_message(cid, ctap1_status_code),
|
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.
|
// 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.
|
// don't handle any other packet in the meantime.
|
||||||
// TODO: Send "Processing" type keep-alive packets in the meantime.
|
// TODO: Send "Processing" type keep-alive packets in the meantime.
|
||||||
let response =
|
let response =
|
||||||
ctap_state.process_command(env, &message.payload, Channel::MainHid(cid), now);
|
ctap_state.process_command(env, &message.payload, Channel::MainHid(cid));
|
||||||
Message {
|
Message {
|
||||||
cid,
|
cid,
|
||||||
cmd: CtapHidCommand::Cbor,
|
cmd: CtapHidCommand::Cbor,
|
||||||
@@ -107,12 +105,11 @@ impl MainHid {
|
|||||||
// CTAP 2.1 from 2021-06-15, section 11.2.9.2.1.
|
// CTAP 2.1 from 2021-06-15, section 11.2.9.2.1.
|
||||||
CtapHidCommand::Wink => {
|
CtapHidCommand::Wink => {
|
||||||
if message.payload.is_empty() {
|
if message.payload.is_empty() {
|
||||||
self.wink_permission =
|
self.wink_permission = env.clock().make_timer(WINK_TIMEOUT_DURATION_MS);
|
||||||
TimedPermission::granted(now, Self::WINK_TIMEOUT_DURATION);
|
|
||||||
// The response is empty like the request.
|
// The response is empty like the request.
|
||||||
message
|
message
|
||||||
} else {
|
} 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.
|
// 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.
|
/// Returns whether a wink permission is currently granted.
|
||||||
pub fn should_wink(&self, now: CtapInstant) -> bool {
|
pub fn should_wink(&self, env: &mut E) -> bool {
|
||||||
self.wink_permission.is_granted(now)
|
!env.clock().is_elapsed(&self.wink_permission)
|
||||||
}
|
|
||||||
|
|
||||||
/// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "with_ctap1")]
|
#[cfg(feature = "with_ctap1")]
|
||||||
@@ -159,11 +151,11 @@ mod test {
|
|||||||
use crate::ctap::hid::ChannelID;
|
use crate::ctap::hid::ChannelID;
|
||||||
use crate::env::test::TestEnv;
|
use crate::env::test::TestEnv;
|
||||||
|
|
||||||
fn new_initialized() -> (MainHid, ChannelID) {
|
fn new_initialized() -> (MainHid<TestEnv>, ChannelID) {
|
||||||
let (hid, cid) = CtapHid::new_initialized();
|
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,
|
hid,
|
||||||
wink_permission,
|
wink_permission,
|
||||||
},
|
},
|
||||||
@@ -174,19 +166,14 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_process_hid_packet() {
|
fn test_process_hid_packet() {
|
||||||
let mut env = TestEnv::new();
|
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 main_hid, cid) = new_initialized();
|
||||||
|
|
||||||
let mut ping_packet = [0x00; 64];
|
let mut ping_packet = [0x00; 64];
|
||||||
ping_packet[..4].copy_from_slice(&cid);
|
ping_packet[..4].copy_from_slice(&cid);
|
||||||
ping_packet[4..9].copy_from_slice(&[0x81, 0x00, 0x02, 0x99, 0x99]);
|
ping_packet[4..9].copy_from_slice(&[0x81, 0x00, 0x02, 0x99, 0x99]);
|
||||||
|
|
||||||
let mut response = main_hid.process_hid_packet(
|
let mut response = main_hid.process_hid_packet(&mut env, &ping_packet, &mut ctap_state);
|
||||||
&mut env,
|
|
||||||
&ping_packet,
|
|
||||||
CtapInstant::new(0),
|
|
||||||
&mut ctap_state,
|
|
||||||
);
|
|
||||||
assert_eq!(response.next(), Some(ping_packet));
|
assert_eq!(response.next(), Some(ping_packet));
|
||||||
assert_eq!(response.next(), None);
|
assert_eq!(response.next(), None);
|
||||||
}
|
}
|
||||||
@@ -194,42 +181,33 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_process_hid_packet_empty() {
|
fn test_process_hid_packet_empty() {
|
||||||
let mut env = TestEnv::new();
|
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 main_hid, cid) = new_initialized();
|
||||||
|
|
||||||
let mut cancel_packet = [0x00; 64];
|
let mut cancel_packet = [0x00; 64];
|
||||||
cancel_packet[..4].copy_from_slice(&cid);
|
cancel_packet[..4].copy_from_slice(&cid);
|
||||||
cancel_packet[4..7].copy_from_slice(&[0x91, 0x00, 0x00]);
|
cancel_packet[4..7].copy_from_slice(&[0x91, 0x00, 0x00]);
|
||||||
|
|
||||||
let mut response = main_hid.process_hid_packet(
|
let mut response = main_hid.process_hid_packet(&mut env, &cancel_packet, &mut ctap_state);
|
||||||
&mut env,
|
|
||||||
&cancel_packet,
|
|
||||||
CtapInstant::new(0),
|
|
||||||
&mut ctap_state,
|
|
||||||
);
|
|
||||||
assert_eq!(response.next(), None);
|
assert_eq!(response.next(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_wink() {
|
fn test_wink() {
|
||||||
let mut env = TestEnv::new();
|
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 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];
|
let mut wink_packet = [0x00; 64];
|
||||||
wink_packet[..4].copy_from_slice(&cid);
|
wink_packet[..4].copy_from_slice(&cid);
|
||||||
wink_packet[4..7].copy_from_slice(&[0x88, 0x00, 0x00]);
|
wink_packet[4..7].copy_from_slice(&[0x88, 0x00, 0x00]);
|
||||||
|
|
||||||
let mut response = main_hid.process_hid_packet(
|
let mut response = main_hid.process_hid_packet(&mut env, &wink_packet, &mut ctap_state);
|
||||||
&mut env,
|
|
||||||
&wink_packet,
|
|
||||||
CtapInstant::new(0),
|
|
||||||
&mut ctap_state,
|
|
||||||
);
|
|
||||||
assert_eq!(response.next(), Some(wink_packet));
|
assert_eq!(response.next(), Some(wink_packet));
|
||||||
assert_eq!(response.next(), None);
|
assert_eq!(response.next(), None);
|
||||||
assert!(main_hid.should_wink(CtapInstant::new(0)));
|
assert!(main_hid.should_wink(&mut env));
|
||||||
assert!(!main_hid.should_wink(CtapInstant::new(1) + MainHid::WINK_TIMEOUT_DURATION));
|
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
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
use crate::api::clock::Clock;
|
||||||
use crate::ctap::client_pin::PinPermission;
|
use crate::ctap::client_pin::PinPermission;
|
||||||
use crate::ctap::status_code::Ctap2StatusCode;
|
use crate::ctap::status_code::Ctap2StatusCode;
|
||||||
use crate::ctap::timed_permission::TimedPermission;
|
use crate::env::Env;
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
use crypto::sha256::Sha256;
|
use crypto::sha256::Sha256;
|
||||||
use crypto::Hash256;
|
use crypto::Hash256;
|
||||||
use embedded_time::duration::Milliseconds;
|
|
||||||
|
|
||||||
use crate::clock::{ClockInt, CtapInstant};
|
|
||||||
|
|
||||||
/// Timeout for auth tokens.
|
/// Timeout for auth tokens.
|
||||||
///
|
///
|
||||||
/// This usage time limit is correct for USB, BLE, and internal.
|
/// This usage time limit is correct for USB, BLE, and internal.
|
||||||
/// NFC only allows 19.8 seconds.
|
/// NFC only allows 19.8 seconds.
|
||||||
/// TODO(#15) multiplex over transports, add NFC
|
/// 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.
|
/// 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.
|
/// built-in user verification. Therefore, we never cache user presence.
|
||||||
///
|
///
|
||||||
/// This implementation does not use a rolling timer.
|
/// 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.
|
// Relies on the fact that all permissions are represented by powers of two.
|
||||||
permissions_set: u8,
|
permissions_set: u8,
|
||||||
permissions_rp_id: Option<String>,
|
permissions_rp_id: Option<String>,
|
||||||
usage_timer: TimedPermission,
|
usage_timer: <E::Clock as Clock>::Timer,
|
||||||
user_verified: bool,
|
user_verified: bool,
|
||||||
in_use: bool,
|
in_use: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PinUvAuthTokenState {
|
impl<E: Env> PinUvAuthTokenState<E> {
|
||||||
/// Creates a pinUvAuthToken state without permissions.
|
/// Creates a pinUvAuthToken state without permissions.
|
||||||
pub fn new() -> PinUvAuthTokenState {
|
pub fn new() -> Self {
|
||||||
PinUvAuthTokenState {
|
PinUvAuthTokenState {
|
||||||
permissions_set: 0,
|
permissions_set: 0,
|
||||||
permissions_rp_id: None,
|
permissions_rp_id: None,
|
||||||
usage_timer: TimedPermission::waiting(),
|
usage_timer: <E::Clock as Clock>::Timer::default(),
|
||||||
user_verified: false,
|
user_verified: false,
|
||||||
in_use: false,
|
in_use: false,
|
||||||
}
|
}
|
||||||
@@ -113,19 +111,18 @@ impl PinUvAuthTokenState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Starts the timer for pinUvAuthToken usage.
|
/// 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.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;
|
self.in_use = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the usage timer, and disables the pinUvAuthToken on timeout.
|
/// 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 {
|
if !self.in_use {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.usage_timer = self.usage_timer.check_expiration(now);
|
if env.clock().is_elapsed(&self.usage_timer) {
|
||||||
if !self.usage_timer.is_granted(now) {
|
|
||||||
self.stop_using_pin_uv_auth_token();
|
self.stop_using_pin_uv_auth_token();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -149,7 +146,7 @@ impl PinUvAuthTokenState {
|
|||||||
pub fn stop_using_pin_uv_auth_token(&mut self) {
|
pub fn stop_using_pin_uv_auth_token(&mut self) {
|
||||||
self.permissions_rp_id = None;
|
self.permissions_rp_id = None;
|
||||||
self.permissions_set = 0;
|
self.permissions_set = 0;
|
||||||
self.usage_timer = TimedPermission::waiting();
|
self.usage_timer = <E::Clock as Clock>::Timer::default();
|
||||||
self.user_verified = false;
|
self.user_verified = false;
|
||||||
self.in_use = false;
|
self.in_use = false;
|
||||||
}
|
}
|
||||||
@@ -158,27 +155,28 @@ impl PinUvAuthTokenState {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::env::test::TestEnv;
|
||||||
use enum_iterator::IntoEnumIterator;
|
use enum_iterator::IntoEnumIterator;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_observer() {
|
fn test_observer() {
|
||||||
let mut token_state = PinUvAuthTokenState::new();
|
let mut env = TestEnv::new();
|
||||||
let mut now: CtapInstant = CtapInstant::new(0);
|
let mut token_state = PinUvAuthTokenState::<TestEnv>::new();
|
||||||
token_state.begin_using_pin_uv_auth_token(now);
|
token_state.begin_using_pin_uv_auth_token(&mut env);
|
||||||
assert!(token_state.is_in_use());
|
assert!(token_state.is_in_use());
|
||||||
now = now + Milliseconds(100_u32);
|
env.clock().advance(100);
|
||||||
token_state.pin_uv_auth_token_usage_timer_observer(now);
|
token_state.pin_uv_auth_token_usage_timer_observer(&mut env);
|
||||||
assert!(token_state.is_in_use());
|
assert!(token_state.is_in_use());
|
||||||
now = now + INITIAL_USAGE_TIME_LIMIT;
|
env.clock().advance(INITIAL_USAGE_TIME_LIMIT_MS);
|
||||||
token_state.pin_uv_auth_token_usage_timer_observer(now);
|
token_state.pin_uv_auth_token_usage_timer_observer(&mut env);
|
||||||
assert!(!token_state.is_in_use());
|
assert!(!token_state.is_in_use());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_stop() {
|
fn test_stop() {
|
||||||
let mut token_state = PinUvAuthTokenState::new();
|
let mut env = TestEnv::new();
|
||||||
let now: CtapInstant = CtapInstant::new(0);
|
let mut token_state = PinUvAuthTokenState::<TestEnv>::new();
|
||||||
token_state.begin_using_pin_uv_auth_token(now);
|
token_state.begin_using_pin_uv_auth_token(&mut env);
|
||||||
assert!(token_state.is_in_use());
|
assert!(token_state.is_in_use());
|
||||||
token_state.stop_using_pin_uv_auth_token();
|
token_state.stop_using_pin_uv_auth_token();
|
||||||
assert!(!token_state.is_in_use());
|
assert!(!token_state.is_in_use());
|
||||||
@@ -186,7 +184,7 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_permissions() {
|
fn test_permissions() {
|
||||||
let mut token_state = PinUvAuthTokenState::new();
|
let mut token_state = PinUvAuthTokenState::<TestEnv>::new();
|
||||||
token_state.set_permissions(0xFF);
|
token_state.set_permissions(0xFF);
|
||||||
for permission in PinPermission::into_enum_iter() {
|
for permission in PinPermission::into_enum_iter() {
|
||||||
assert_eq!(token_state.has_permission(permission), Ok(()));
|
assert_eq!(token_state.has_permission(permission), Ok(()));
|
||||||
@@ -211,7 +209,7 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_permissions_rp_id_none() {
|
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");
|
let example_hash = Sha256::hash(b"example.com");
|
||||||
token_state.set_permissions_rp_id(None);
|
token_state.set_permissions_rp_id(None);
|
||||||
assert_eq!(token_state.has_no_permissions_rp_id(), Ok(()));
|
assert_eq!(token_state.has_no_permissions_rp_id(), Ok(()));
|
||||||
@@ -227,7 +225,7 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_permissions_rp_id_some() {
|
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");
|
let example_hash = Sha256::hash(b"example.com");
|
||||||
token_state.set_permissions_rp_id(Some(String::from("example.com")));
|
token_state.set_permissions_rp_id(Some(String::from("example.com")));
|
||||||
|
|
||||||
@@ -262,14 +260,14 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_user_verified_flag() {
|
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());
|
assert!(!token_state.get_user_verified_flag_value());
|
||||||
let now: CtapInstant = CtapInstant::new(0);
|
token_state.begin_using_pin_uv_auth_token(&mut env);
|
||||||
token_state.begin_using_pin_uv_auth_token(now);
|
|
||||||
assert!(token_state.get_user_verified_flag_value());
|
assert!(token_state.get_user_verified_flag_value());
|
||||||
token_state.clear_user_verified_flag();
|
token_state.clear_user_verified_flag();
|
||||||
assert!(!token_state.get_user_verified_flag_value());
|
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());
|
assert!(token_state.get_user_verified_flag_value());
|
||||||
token_state.stop_using_pin_uv_auth_token();
|
token_state.stop_using_pin_uv_auth_token();
|
||||||
assert!(!token_state.get_user_verified_flag_value());
|
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
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::clock::CtapInstant;
|
|
||||||
use crate::ctap::hid::{
|
use crate::ctap::hid::{
|
||||||
CtapHid, CtapHidCommand, CtapHidError, HidPacket, HidPacketIterator, Message,
|
CtapHid, CtapHidCommand, CtapHidError, HidPacket, HidPacketIterator, Message,
|
||||||
};
|
};
|
||||||
@@ -22,33 +21,32 @@ use crate::env::Env;
|
|||||||
/// Implements the non-standard command processing for HID.
|
/// Implements the non-standard command processing for HID.
|
||||||
///
|
///
|
||||||
/// Outside of the pure HID commands like INIT, only PING and CBOR commands are allowed.
|
/// Outside of the pure HID commands like INIT, only PING and CBOR commands are allowed.
|
||||||
pub struct VendorHid {
|
pub struct VendorHid<E: Env> {
|
||||||
hid: CtapHid,
|
hid: CtapHid<E>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VendorHid {
|
impl<E: Env> VendorHid<E> {
|
||||||
/// Instantiates a HID handler for CTAP1, CTAP2 and Wink.
|
/// Instantiates a HID handler for CTAP1, CTAP2 and Wink.
|
||||||
pub fn new() -> Self {
|
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 }
|
VendorHid { hid }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes an incoming USB HID packet, and returns an iterator for all outgoing packets.
|
/// Processes an incoming USB HID packet, and returns an iterator for all outgoing packets.
|
||||||
pub fn process_hid_packet(
|
pub fn process_hid_packet(
|
||||||
&mut self,
|
&mut self,
|
||||||
env: &mut impl Env,
|
env: &mut E,
|
||||||
packet: &HidPacket,
|
packet: &HidPacket,
|
||||||
now: CtapInstant,
|
ctap_state: &mut CtapState<E>,
|
||||||
ctap_state: &mut CtapState,
|
|
||||||
) -> HidPacketIterator {
|
) -> HidPacketIterator {
|
||||||
if let Some(message) = self.hid.parse_packet(env, packet, now) {
|
if let Some(message) = self.hid.parse_packet(env, packet) {
|
||||||
let processed_message = self.process_message(env, message, now, ctap_state);
|
let processed_message = self.process_message(env, message, ctap_state);
|
||||||
debug_ctap!(
|
debug_ctap!(
|
||||||
env,
|
env,
|
||||||
"Sending message through the second usage page: {:02x?}",
|
"Sending message through the second usage page: {:02x?}",
|
||||||
processed_message
|
processed_message
|
||||||
);
|
);
|
||||||
CtapHid::split_message(processed_message)
|
CtapHid::<E>::split_message(processed_message)
|
||||||
} else {
|
} else {
|
||||||
HidPacketIterator::none()
|
HidPacketIterator::none()
|
||||||
}
|
}
|
||||||
@@ -57,19 +55,18 @@ impl VendorHid {
|
|||||||
/// Processes a message's commands that affect the protocol outside HID.
|
/// Processes a message's commands that affect the protocol outside HID.
|
||||||
pub fn process_message(
|
pub fn process_message(
|
||||||
&mut self,
|
&mut self,
|
||||||
env: &mut impl Env,
|
env: &mut E,
|
||||||
message: Message,
|
message: Message,
|
||||||
now: CtapInstant,
|
ctap_state: &mut CtapState<E>,
|
||||||
ctap_state: &mut CtapState,
|
|
||||||
) -> Message {
|
) -> Message {
|
||||||
let cid = message.cid;
|
let cid = message.cid;
|
||||||
match message.cmd {
|
match message.cmd {
|
||||||
// There are no custom CTAP1 commands.
|
// 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.
|
// The CTAP2 processing function multiplexes internally.
|
||||||
CtapHidCommand::Cbor => {
|
CtapHidCommand::Cbor => {
|
||||||
let response =
|
let response =
|
||||||
ctap_state.process_command(env, &message.payload, Channel::VendorHid(cid), now);
|
ctap_state.process_command(env, &message.payload, Channel::VendorHid(cid));
|
||||||
Message {
|
Message {
|
||||||
cid,
|
cid,
|
||||||
cmd: CtapHidCommand::Cbor,
|
cmd: CtapHidCommand::Cbor,
|
||||||
@@ -77,7 +74,7 @@ impl VendorHid {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Call Wink over the main HID.
|
// 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.
|
// All other commands have already been processed, keep them as is.
|
||||||
_ => message,
|
_ => message,
|
||||||
}
|
}
|
||||||
@@ -90,27 +87,22 @@ mod test {
|
|||||||
use crate::ctap::hid::ChannelID;
|
use crate::ctap::hid::ChannelID;
|
||||||
use crate::env::test::TestEnv;
|
use crate::env::test::TestEnv;
|
||||||
|
|
||||||
fn new_initialized() -> (VendorHid, ChannelID) {
|
fn new_initialized() -> (VendorHid<TestEnv>, ChannelID) {
|
||||||
let (hid, cid) = CtapHid::new_initialized();
|
let (hid, cid) = CtapHid::new_initialized();
|
||||||
(VendorHid { hid }, cid)
|
(VendorHid::<TestEnv> { hid }, cid)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_process_hid_packet() {
|
fn test_process_hid_packet() {
|
||||||
let mut env = TestEnv::new();
|
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 vendor_hid, cid) = new_initialized();
|
||||||
|
|
||||||
let mut ping_packet = [0x00; 64];
|
let mut ping_packet = [0x00; 64];
|
||||||
ping_packet[..4].copy_from_slice(&cid);
|
ping_packet[..4].copy_from_slice(&cid);
|
||||||
ping_packet[4..9].copy_from_slice(&[0x81, 0x00, 0x02, 0x99, 0x99]);
|
ping_packet[4..9].copy_from_slice(&[0x81, 0x00, 0x02, 0x99, 0x99]);
|
||||||
|
|
||||||
let mut response = vendor_hid.process_hid_packet(
|
let mut response = vendor_hid.process_hid_packet(&mut env, &ping_packet, &mut ctap_state);
|
||||||
&mut env,
|
|
||||||
&ping_packet,
|
|
||||||
CtapInstant::new(0),
|
|
||||||
&mut ctap_state,
|
|
||||||
);
|
|
||||||
assert_eq!(response.next(), Some(ping_packet));
|
assert_eq!(response.next(), Some(ping_packet));
|
||||||
assert_eq!(response.next(), None);
|
assert_eq!(response.next(), None);
|
||||||
}
|
}
|
||||||
@@ -118,26 +110,21 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_process_hid_packet_empty() {
|
fn test_process_hid_packet_empty() {
|
||||||
let mut env = TestEnv::new();
|
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 vendor_hid, cid) = new_initialized();
|
||||||
|
|
||||||
let mut cancel_packet = [0x00; 64];
|
let mut cancel_packet = [0x00; 64];
|
||||||
cancel_packet[..4].copy_from_slice(&cid);
|
cancel_packet[..4].copy_from_slice(&cid);
|
||||||
cancel_packet[4..7].copy_from_slice(&[0x91, 0x00, 0x00]);
|
cancel_packet[4..7].copy_from_slice(&[0x91, 0x00, 0x00]);
|
||||||
|
|
||||||
let mut response = vendor_hid.process_hid_packet(
|
let mut response = vendor_hid.process_hid_packet(&mut env, &cancel_packet, &mut ctap_state);
|
||||||
&mut env,
|
|
||||||
&cancel_packet,
|
|
||||||
CtapInstant::new(0),
|
|
||||||
&mut ctap_state,
|
|
||||||
);
|
|
||||||
assert_eq!(response.next(), None);
|
assert_eq!(response.next(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_blocked_commands() {
|
fn test_blocked_commands() {
|
||||||
let mut env = TestEnv::new();
|
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 vendor_hid, cid) = new_initialized();
|
||||||
|
|
||||||
// Usually longer, but we don't parse them anyway.
|
// 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].copy_from_slice(&cid);
|
||||||
error_packet[4..8].copy_from_slice(&[0xBF, 0x00, 0x01, 0x01]);
|
error_packet[4..8].copy_from_slice(&[0xBF, 0x00, 0x01, 0x01]);
|
||||||
|
|
||||||
let mut response = vendor_hid.process_hid_packet(
|
let mut response = vendor_hid.process_hid_packet(&mut env, &msg_packet, &mut ctap_state);
|
||||||
&mut env,
|
|
||||||
&msg_packet,
|
|
||||||
CtapInstant::new(0),
|
|
||||||
&mut ctap_state,
|
|
||||||
);
|
|
||||||
assert_eq!(response.next(), Some(error_packet));
|
assert_eq!(response.next(), Some(error_packet));
|
||||||
assert_eq!(response.next(), None);
|
assert_eq!(response.next(), None);
|
||||||
|
|
||||||
let mut response = vendor_hid.process_hid_packet(
|
let mut response = vendor_hid.process_hid_packet(&mut env, &wink_packet, &mut ctap_state);
|
||||||
&mut env,
|
|
||||||
&wink_packet,
|
|
||||||
CtapInstant::new(0),
|
|
||||||
&mut ctap_state,
|
|
||||||
);
|
|
||||||
assert_eq!(response.next(), Some(error_packet));
|
assert_eq!(response.next(), Some(error_packet));
|
||||||
assert_eq!(response.next(), None);
|
assert_eq!(response.next(), None);
|
||||||
}
|
}
|
||||||
|
|||||||
5
src/env/mod.rs
vendored
5
src/env/mod.rs
vendored
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2022 Google LLC
|
// Copyright 2022-2023 Google LLC
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::api::attestation_store::AttestationStore;
|
use crate::api::attestation_store::AttestationStore;
|
||||||
|
use crate::api::clock::Clock;
|
||||||
use crate::api::connection::HidConnection;
|
use crate::api::connection::HidConnection;
|
||||||
use crate::api::customization::Customization;
|
use crate::api::customization::Customization;
|
||||||
use crate::api::firmware_protection::FirmwareProtection;
|
use crate::api::firmware_protection::FirmwareProtection;
|
||||||
@@ -38,12 +39,14 @@ pub trait Env {
|
|||||||
type Customization: Customization;
|
type Customization: Customization;
|
||||||
type HidConnection: HidConnection;
|
type HidConnection: HidConnection;
|
||||||
type AttestationStore: AttestationStore;
|
type AttestationStore: AttestationStore;
|
||||||
|
type Clock: Clock;
|
||||||
|
|
||||||
fn rng(&mut self) -> &mut Self::Rng;
|
fn rng(&mut self) -> &mut Self::Rng;
|
||||||
fn user_presence(&mut self) -> &mut Self::UserPresence;
|
fn user_presence(&mut self) -> &mut Self::UserPresence;
|
||||||
fn store(&mut self) -> &mut Store<Self::Storage>;
|
fn store(&mut self) -> &mut Store<Self::Storage>;
|
||||||
fn key_store(&mut self) -> &mut Self::KeyStore;
|
fn key_store(&mut self) -> &mut Self::KeyStore;
|
||||||
fn attestation_store(&mut self) -> &mut Self::AttestationStore;
|
fn attestation_store(&mut self) -> &mut Self::AttestationStore;
|
||||||
|
fn clock(&mut self) -> &mut Self::Clock;
|
||||||
|
|
||||||
/// Returns the upgrade storage instance.
|
/// Returns the upgrade storage instance.
|
||||||
///
|
///
|
||||||
|
|||||||
73
src/env/test/mod.rs
vendored
73
src/env/test/mod.rs
vendored
@@ -14,15 +14,14 @@
|
|||||||
|
|
||||||
use self::upgrade_storage::BufferUpgradeStorage;
|
use self::upgrade_storage::BufferUpgradeStorage;
|
||||||
use crate::api::attestation_store::AttestationStore;
|
use crate::api::attestation_store::AttestationStore;
|
||||||
|
use crate::api::clock::Clock;
|
||||||
use crate::api::connection::{HidConnection, SendOrRecvResult, SendOrRecvStatus};
|
use crate::api::connection::{HidConnection, SendOrRecvResult, SendOrRecvStatus};
|
||||||
use crate::api::customization::DEFAULT_CUSTOMIZATION;
|
use crate::api::customization::DEFAULT_CUSTOMIZATION;
|
||||||
use crate::api::firmware_protection::FirmwareProtection;
|
use crate::api::firmware_protection::FirmwareProtection;
|
||||||
use crate::api::user_presence::{UserPresence, UserPresenceResult};
|
use crate::api::user_presence::{UserPresence, UserPresenceResult};
|
||||||
use crate::api::{attestation_store, key_store};
|
use crate::api::{attestation_store, key_store};
|
||||||
use crate::clock::ClockInt;
|
|
||||||
use crate::env::Env;
|
use crate::env::Env;
|
||||||
use customization::TestCustomization;
|
use customization::TestCustomization;
|
||||||
use embedded_time::duration::Milliseconds;
|
|
||||||
use persistent_store::{BufferOptions, BufferStorage, Store};
|
use persistent_store::{BufferOptions, BufferStorage, Store};
|
||||||
use rand::rngs::StdRng;
|
use rand::rngs::StdRng;
|
||||||
use rand::{Rng, SeedableRng};
|
use rand::{Rng, SeedableRng};
|
||||||
@@ -37,6 +36,7 @@ pub struct TestEnv {
|
|||||||
store: Store<BufferStorage>,
|
store: Store<BufferStorage>,
|
||||||
upgrade_storage: Option<BufferUpgradeStorage>,
|
upgrade_storage: Option<BufferUpgradeStorage>,
|
||||||
customization: TestCustomization,
|
customization: TestCustomization,
|
||||||
|
clock: TestClock,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TestRng256 {
|
pub struct TestRng256 {
|
||||||
@@ -57,6 +57,43 @@ impl Rng256 for TestRng256 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq)]
|
||||||
|
pub struct TestTimer {
|
||||||
|
end_ms: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct TestClock {
|
||||||
|
/// The current time, as advanced, in milliseconds.
|
||||||
|
now_ms: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestClock {
|
||||||
|
pub fn advance(&mut self, milliseconds: usize) {
|
||||||
|
self.now_ms += milliseconds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clock for TestClock {
|
||||||
|
type Timer = TestTimer;
|
||||||
|
|
||||||
|
fn make_timer(&mut self, milliseconds: usize) -> Self::Timer {
|
||||||
|
TestTimer {
|
||||||
|
end_ms: self.now_ms + milliseconds,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_elapsed(&mut self, timer: &Self::Timer) -> bool {
|
||||||
|
self.now_ms >= timer.end_ms
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "debug_ctap")]
|
||||||
|
fn timestamp_us(&mut self) -> usize {
|
||||||
|
// Unused, but let's implement something because it's easy.
|
||||||
|
self.now_ms * 1000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct TestUserPresence {
|
pub struct TestUserPresence {
|
||||||
check: Box<dyn Fn() -> UserPresenceResult>,
|
check: Box<dyn Fn() -> UserPresenceResult>,
|
||||||
}
|
}
|
||||||
@@ -85,11 +122,7 @@ fn new_storage() -> BufferStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl HidConnection for TestEnv {
|
impl HidConnection for TestEnv {
|
||||||
fn send_and_maybe_recv(
|
fn send_and_maybe_recv(&mut self, _buf: &mut [u8; 64], _timeout_ms: usize) -> SendOrRecvResult {
|
||||||
&mut self,
|
|
||||||
_buf: &mut [u8; 64],
|
|
||||||
_timeout: Milliseconds<ClockInt>,
|
|
||||||
) -> SendOrRecvResult {
|
|
||||||
// TODO: Implement I/O from canned requests/responses for integration testing.
|
// TODO: Implement I/O from canned requests/responses for integration testing.
|
||||||
Ok(SendOrRecvStatus::Sent)
|
Ok(SendOrRecvStatus::Sent)
|
||||||
}
|
}
|
||||||
@@ -107,12 +140,14 @@ impl TestEnv {
|
|||||||
let store = Store::new(storage).ok().unwrap();
|
let store = Store::new(storage).ok().unwrap();
|
||||||
let upgrade_storage = Some(BufferUpgradeStorage::new().unwrap());
|
let upgrade_storage = Some(BufferUpgradeStorage::new().unwrap());
|
||||||
let customization = DEFAULT_CUSTOMIZATION.into();
|
let customization = DEFAULT_CUSTOMIZATION.into();
|
||||||
|
let clock = TestClock::default();
|
||||||
TestEnv {
|
TestEnv {
|
||||||
rng,
|
rng,
|
||||||
user_presence,
|
user_presence,
|
||||||
store,
|
store,
|
||||||
upgrade_storage,
|
upgrade_storage,
|
||||||
customization,
|
customization,
|
||||||
|
clock,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +172,7 @@ impl TestUserPresence {
|
|||||||
|
|
||||||
impl UserPresence for TestUserPresence {
|
impl UserPresence for TestUserPresence {
|
||||||
fn check_init(&mut self) {}
|
fn check_init(&mut self) {}
|
||||||
fn wait_with_timeout(&mut self, _timeout: Milliseconds<ClockInt>) -> UserPresenceResult {
|
fn wait_with_timeout(&mut self, _timeout_ms: usize) -> UserPresenceResult {
|
||||||
(self.check)()
|
(self.check)()
|
||||||
}
|
}
|
||||||
fn check_complete(&mut self) {}
|
fn check_complete(&mut self) {}
|
||||||
@@ -174,6 +209,7 @@ impl Env for TestEnv {
|
|||||||
type Storage = BufferStorage;
|
type Storage = BufferStorage;
|
||||||
type KeyStore = Self;
|
type KeyStore = Self;
|
||||||
type AttestationStore = Self;
|
type AttestationStore = Self;
|
||||||
|
type Clock = TestClock;
|
||||||
type UpgradeStorage = BufferUpgradeStorage;
|
type UpgradeStorage = BufferUpgradeStorage;
|
||||||
type FirmwareProtection = Self;
|
type FirmwareProtection = Self;
|
||||||
type Write = TestWrite;
|
type Write = TestWrite;
|
||||||
@@ -200,6 +236,10 @@ impl Env for TestEnv {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clock(&mut self) -> &mut Self::Clock {
|
||||||
|
&mut self.clock
|
||||||
|
}
|
||||||
|
|
||||||
fn upgrade_storage(&mut self) -> Option<&mut Self::UpgradeStorage> {
|
fn upgrade_storage(&mut self) -> Option<&mut Self::UpgradeStorage> {
|
||||||
self.upgrade_storage.as_mut()
|
self.upgrade_storage.as_mut()
|
||||||
}
|
}
|
||||||
@@ -225,3 +265,20 @@ impl Env for TestEnv {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[allow(clippy::module_inception)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_clock() {
|
||||||
|
let mut clock = TestClock::default();
|
||||||
|
let timer = clock.make_timer(3);
|
||||||
|
assert!(!clock.is_elapsed(&timer));
|
||||||
|
clock.advance(2);
|
||||||
|
assert!(!clock.is_elapsed(&timer));
|
||||||
|
clock.advance(1);
|
||||||
|
assert!(clock.is_elapsed(&timer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
118
src/env/tock/clock.rs
vendored
Normal file
118
src/env/tock/clock.rs
vendored
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
// Copyright 2022-2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
use crate::api::clock::Clock;
|
||||||
|
use libtock_drivers::timer::{get_clock_frequency, get_ticks};
|
||||||
|
|
||||||
|
/// 56-bits timestamp (valid for 70k+ years)
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialOrd, Ord, PartialEq, Eq)]
|
||||||
|
struct Timestamp {
|
||||||
|
epoch: usize, // 32-bits
|
||||||
|
tick: usize, // 24-bits (32kHz)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Timestamp {
|
||||||
|
/// Adds (potentially more than 24 bit of) ticks to this timestamp.
|
||||||
|
pub fn add_ticks(&mut self, ticks: usize) {
|
||||||
|
// Saturating should never happen, but it fails gracefully.
|
||||||
|
let sum = self.tick.saturating_add(ticks);
|
||||||
|
self.epoch += sum >> 24;
|
||||||
|
self.tick = sum & 0xff_ffff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct TockTimer {
|
||||||
|
deadline: Timestamp,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clock that produces timers through Tock syscalls.
|
||||||
|
///
|
||||||
|
/// To guarantee correctness, you have to call any of its functions at least once per full tick
|
||||||
|
/// counter wrap. In our case, 24 bit ticks with a 32 kHz frequency wrap after 512 seconds. If you
|
||||||
|
/// can't guarantee to regularly create or check timers, call tickle at least every 8 minutes.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct TockClock {
|
||||||
|
now: Timestamp,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TockClock {
|
||||||
|
/// Elapses timers before the clock wraps.
|
||||||
|
///
|
||||||
|
/// Call this regularly to timeout reliably despite wrapping clock ticks.
|
||||||
|
pub fn tickle(&mut self) {
|
||||||
|
let cur_tick = get_ticks().ok().unwrap();
|
||||||
|
if cur_tick < self.now.tick {
|
||||||
|
self.now.epoch += 1;
|
||||||
|
}
|
||||||
|
self.now.tick = cur_tick;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clock for TockClock {
|
||||||
|
type Timer = TockTimer;
|
||||||
|
|
||||||
|
fn make_timer(&mut self, milliseconds: usize) -> Self::Timer {
|
||||||
|
self.tickle();
|
||||||
|
let clock_frequency = get_clock_frequency().ok().unwrap();
|
||||||
|
let delta_tick = match milliseconds.checked_mul(clock_frequency) {
|
||||||
|
Some(x) => x / 1000,
|
||||||
|
// All CTAP timeouts are multiples of 100 so far. Worst case we timeout too early.
|
||||||
|
None => (milliseconds / 100).saturating_mul(clock_frequency / 10),
|
||||||
|
};
|
||||||
|
let mut deadline = self.now;
|
||||||
|
deadline.add_ticks(delta_tick);
|
||||||
|
Self::Timer { deadline }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_elapsed(&mut self, timer: &Self::Timer) -> bool {
|
||||||
|
self.tickle();
|
||||||
|
self.now >= timer.deadline
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "debug_ctap")]
|
||||||
|
fn timestamp_us(&mut self) -> usize {
|
||||||
|
let clock_frequency = get_clock_frequency().ok().unwrap();
|
||||||
|
let total_ticks = 0x100_0000u64 * self.now.epoch as u64 + self.now.tick as u64;
|
||||||
|
(total_ticks.wrapping_mul(1_000_000u64) / clock_frequency as u64) as usize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_timestamp_add_ticks() {
|
||||||
|
let mut timestamp = Timestamp::default();
|
||||||
|
timestamp.add_ticks(1);
|
||||||
|
let expected = Timestamp { epoch: 0, tick: 1 };
|
||||||
|
assert_eq!(timestamp, expected);
|
||||||
|
timestamp.add_ticks(0xff_ffff);
|
||||||
|
let expected = Timestamp { epoch: 1, tick: 0 };
|
||||||
|
assert_eq!(timestamp, expected);
|
||||||
|
timestamp.add_ticks(0x100_0000);
|
||||||
|
let expected = Timestamp { epoch: 2, tick: 0 };
|
||||||
|
assert_eq!(timestamp, expected);
|
||||||
|
timestamp.add_ticks(0x1ff_ffff);
|
||||||
|
let expected = Timestamp {
|
||||||
|
epoch: 3,
|
||||||
|
tick: 0xff_ffff,
|
||||||
|
};
|
||||||
|
assert_eq!(timestamp, expected);
|
||||||
|
timestamp.add_ticks(1);
|
||||||
|
let expected = Timestamp { epoch: 4, tick: 0 };
|
||||||
|
assert_eq!(timestamp, expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/env/tock/mod.rs
vendored
29
src/env/tock/mod.rs
vendored
@@ -19,12 +19,10 @@ use crate::api::customization::{CustomizationImpl, DEFAULT_CUSTOMIZATION};
|
|||||||
use crate::api::firmware_protection::FirmwareProtection;
|
use crate::api::firmware_protection::FirmwareProtection;
|
||||||
use crate::api::user_presence::{UserPresence, UserPresenceError, UserPresenceResult};
|
use crate::api::user_presence::{UserPresence, UserPresenceError, UserPresenceResult};
|
||||||
use crate::api::{attestation_store, key_store};
|
use crate::api::{attestation_store, key_store};
|
||||||
use crate::clock::{ClockInt, KEEPALIVE_DELAY_MS};
|
|
||||||
use crate::env::Env;
|
use crate::env::Env;
|
||||||
|
use clock::TockClock;
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
use core::sync::atomic::{AtomicBool, Ordering};
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
use embedded_time::duration::Milliseconds;
|
|
||||||
use embedded_time::fixed_point::FixedPoint;
|
|
||||||
use libtock_core::result::{CommandError, EALREADY};
|
use libtock_core::result::{CommandError, EALREADY};
|
||||||
use libtock_drivers::buttons::{self, ButtonState};
|
use libtock_drivers::buttons::{self, ButtonState};
|
||||||
use libtock_drivers::console::Console;
|
use libtock_drivers::console::Console;
|
||||||
@@ -35,6 +33,7 @@ use libtock_drivers::{crp, led, timer};
|
|||||||
use persistent_store::{StorageResult, Store};
|
use persistent_store::{StorageResult, Store};
|
||||||
use rng256::TockRng256;
|
use rng256::TockRng256;
|
||||||
|
|
||||||
|
mod clock;
|
||||||
mod storage;
|
mod storage;
|
||||||
|
|
||||||
pub struct TockHidConnection {
|
pub struct TockHidConnection {
|
||||||
@@ -42,14 +41,10 @@ pub struct TockHidConnection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl HidConnection for TockHidConnection {
|
impl HidConnection for TockHidConnection {
|
||||||
fn send_and_maybe_recv(
|
fn send_and_maybe_recv(&mut self, buf: &mut [u8; 64], timeout_ms: usize) -> SendOrRecvResult {
|
||||||
&mut self,
|
|
||||||
buf: &mut [u8; 64],
|
|
||||||
timeout: Milliseconds<ClockInt>,
|
|
||||||
) -> SendOrRecvResult {
|
|
||||||
match usb_ctap_hid::send_or_recv_with_timeout(
|
match usb_ctap_hid::send_or_recv_with_timeout(
|
||||||
buf,
|
buf,
|
||||||
timer::Duration::from_ms(timeout.integer() as isize),
|
Duration::from_ms(timeout_ms as isize),
|
||||||
self.endpoint,
|
self.endpoint,
|
||||||
) {
|
) {
|
||||||
Ok(usb_ctap_hid::SendOrRecvStatus::Timeout) => Ok(SendOrRecvStatus::Timeout),
|
Ok(usb_ctap_hid::SendOrRecvStatus::Timeout) => Ok(SendOrRecvStatus::Timeout),
|
||||||
@@ -70,6 +65,7 @@ pub struct TockEnv {
|
|||||||
#[cfg(feature = "vendor_hid")]
|
#[cfg(feature = "vendor_hid")]
|
||||||
vendor_connection: TockHidConnection,
|
vendor_connection: TockHidConnection,
|
||||||
blink_pattern: usize,
|
blink_pattern: usize,
|
||||||
|
clock: TockClock,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TockEnv {
|
impl TockEnv {
|
||||||
@@ -95,6 +91,7 @@ impl TockEnv {
|
|||||||
endpoint: UsbEndpoint::VendorHid,
|
endpoint: UsbEndpoint::VendorHid,
|
||||||
},
|
},
|
||||||
blink_pattern: 0,
|
blink_pattern: 0,
|
||||||
|
clock: TockClock::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,8 +112,9 @@ impl UserPresence for TockEnv {
|
|||||||
fn check_init(&mut self) {
|
fn check_init(&mut self) {
|
||||||
self.blink_pattern = 0;
|
self.blink_pattern = 0;
|
||||||
}
|
}
|
||||||
fn wait_with_timeout(&mut self, timeout: Milliseconds<ClockInt>) -> UserPresenceResult {
|
|
||||||
if timeout.integer() == 0 {
|
fn wait_with_timeout(&mut self, timeout_ms: usize) -> UserPresenceResult {
|
||||||
|
if timeout_ms == 0 {
|
||||||
return Err(UserPresenceError::Timeout);
|
return Err(UserPresenceError::Timeout);
|
||||||
}
|
}
|
||||||
blink_leds(self.blink_pattern);
|
blink_leds(self.blink_pattern);
|
||||||
@@ -141,7 +139,7 @@ impl UserPresence for TockEnv {
|
|||||||
});
|
});
|
||||||
let mut keepalive = keepalive_callback.init().flex_unwrap();
|
let mut keepalive = keepalive_callback.init().flex_unwrap();
|
||||||
let keepalive_alarm = keepalive
|
let keepalive_alarm = keepalive
|
||||||
.set_alarm(timer::Duration::from_ms(timeout.integer() as isize))
|
.set_alarm(Duration::from_ms(timeout_ms as isize))
|
||||||
.flex_unwrap();
|
.flex_unwrap();
|
||||||
|
|
||||||
// Wait for a button touch or an alarm.
|
// Wait for a button touch or an alarm.
|
||||||
@@ -224,6 +222,7 @@ impl Env for TockEnv {
|
|||||||
type Storage = TockStorage;
|
type Storage = TockStorage;
|
||||||
type KeyStore = Self;
|
type KeyStore = Self;
|
||||||
type AttestationStore = Self;
|
type AttestationStore = Self;
|
||||||
|
type Clock = TockClock;
|
||||||
type UpgradeStorage = TockUpgradeStorage;
|
type UpgradeStorage = TockUpgradeStorage;
|
||||||
type FirmwareProtection = Self;
|
type FirmwareProtection = Self;
|
||||||
type Write = Console;
|
type Write = Console;
|
||||||
@@ -250,6 +249,10 @@ impl Env for TockEnv {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clock(&mut self) -> &mut Self::Clock {
|
||||||
|
&mut self.clock
|
||||||
|
}
|
||||||
|
|
||||||
fn upgrade_storage(&mut self) -> Option<&mut Self::UpgradeStorage> {
|
fn upgrade_storage(&mut self) -> Option<&mut Self::UpgradeStorage> {
|
||||||
self.upgrade_storage.as_mut()
|
self.upgrade_storage.as_mut()
|
||||||
}
|
}
|
||||||
@@ -324,5 +327,3 @@ pub fn switch_off_leds() {
|
|||||||
led::get(l).flex_unwrap().off().flex_unwrap();
|
led::get(l).flex_unwrap().off().flex_unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const KEEPALIVE_DELAY_TOCK: Duration<isize> = Duration::from_ms(KEEPALIVE_DELAY_MS as isize);
|
|
||||||
|
|||||||
40
src/lib.rs
40
src/lib.rs
@@ -25,7 +25,6 @@ use crate::ctap::vendor_hid::VendorHid;
|
|||||||
use crate::ctap::CtapState;
|
use crate::ctap::CtapState;
|
||||||
pub use crate::ctap::Transport;
|
pub use crate::ctap::Transport;
|
||||||
use crate::env::Env;
|
use crate::env::Env;
|
||||||
use clock::CtapInstant;
|
|
||||||
|
|
||||||
// Those macros should eventually be split into trace, debug, info, warn, and error macros when
|
// Those macros should eventually be split into trace, debug, info, warn, and error macros when
|
||||||
// adding either the defmt or log feature and crate dependency.
|
// adding either the defmt or log feature and crate dependency.
|
||||||
@@ -45,7 +44,6 @@ macro_rules! debug_ctap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub mod api;
|
pub mod api;
|
||||||
pub mod clock;
|
|
||||||
// TODO(kaczmarczyck): Refactor this so that ctap module isn't public.
|
// TODO(kaczmarczyck): Refactor this so that ctap module isn't public.
|
||||||
pub mod ctap;
|
pub mod ctap;
|
||||||
pub mod env;
|
pub mod env;
|
||||||
@@ -55,18 +53,18 @@ pub mod test_helpers;
|
|||||||
/// CTAP implementation parameterized by its environment.
|
/// CTAP implementation parameterized by its environment.
|
||||||
pub struct Ctap<E: Env> {
|
pub struct Ctap<E: Env> {
|
||||||
env: E,
|
env: E,
|
||||||
state: CtapState,
|
state: CtapState<E>,
|
||||||
hid: MainHid,
|
hid: MainHid<E>,
|
||||||
#[cfg(feature = "vendor_hid")]
|
#[cfg(feature = "vendor_hid")]
|
||||||
vendor_hid: VendorHid,
|
vendor_hid: VendorHid<E>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Env> Ctap<E> {
|
impl<E: Env> Ctap<E> {
|
||||||
/// Instantiates a CTAP implementation given its environment.
|
/// Instantiates a CTAP implementation given its environment.
|
||||||
// This should only take the environment, but it temporarily takes the boot time until the
|
// This should only take the environment, but it temporarily takes the boot time until the
|
||||||
// clock is part of the environment.
|
// clock is part of the environment.
|
||||||
pub fn new(mut env: E, now: CtapInstant) -> Self {
|
pub fn new(mut env: E) -> Self {
|
||||||
let state = CtapState::new(&mut env, now);
|
let state = CtapState::<E>::new(&mut env);
|
||||||
let hid = MainHid::new();
|
let hid = MainHid::new();
|
||||||
#[cfg(feature = "vendor_hid")]
|
#[cfg(feature = "vendor_hid")]
|
||||||
let vendor_hid = VendorHid::new();
|
let vendor_hid = VendorHid::new();
|
||||||
@@ -79,11 +77,11 @@ impl<E: Env> Ctap<E> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn state(&mut self) -> &mut CtapState {
|
pub fn state(&mut self) -> &mut CtapState<E> {
|
||||||
&mut self.state
|
&mut self.state
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hid(&mut self) -> &mut MainHid {
|
pub fn hid(&mut self) -> &mut MainHid<E> {
|
||||||
&mut self.hid
|
&mut self.hid
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,23 +93,35 @@ impl<E: Env> Ctap<E> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
packet: &HidPacket,
|
packet: &HidPacket,
|
||||||
transport: Transport,
|
transport: Transport,
|
||||||
now: CtapInstant,
|
|
||||||
) -> HidPacketIterator {
|
) -> HidPacketIterator {
|
||||||
match transport {
|
match transport {
|
||||||
Transport::MainHid => {
|
Transport::MainHid => {
|
||||||
self.hid
|
self.hid
|
||||||
.process_hid_packet(&mut self.env, packet, now, &mut self.state)
|
.process_hid_packet(&mut self.env, packet, &mut self.state)
|
||||||
}
|
}
|
||||||
#[cfg(feature = "vendor_hid")]
|
#[cfg(feature = "vendor_hid")]
|
||||||
Transport::VendorHid => {
|
Transport::VendorHid => {
|
||||||
self.vendor_hid
|
self.vendor_hid
|
||||||
.process_hid_packet(&mut self.env, packet, now, &mut self.state)
|
.process_hid_packet(&mut self.env, packet, &mut self.state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_timeouts(&mut self, now: CtapInstant) {
|
pub fn update_timeouts(&mut self) {
|
||||||
self.state.update_timeouts(now);
|
self.state.update_timeouts(&mut self.env);
|
||||||
self.hid.update_wink_timeout(now);
|
}
|
||||||
|
|
||||||
|
pub fn should_wink(&mut self) -> bool {
|
||||||
|
self.hid.should_wink(&mut self.env)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "with_ctap1")]
|
||||||
|
pub fn u2f_grant_user_presence(&mut self) {
|
||||||
|
self.state.u2f_grant_user_presence(&mut self.env)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "with_ctap1")]
|
||||||
|
pub fn u2f_needs_user_presence(&mut self) -> bool {
|
||||||
|
self.state.u2f_needs_user_presence(&mut self.env)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
76
src/main.rs
76
src/main.rs
@@ -24,22 +24,16 @@ extern crate lang_items;
|
|||||||
#[cfg(feature = "with_ctap1")]
|
#[cfg(feature = "with_ctap1")]
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
#[cfg(feature = "debug_ctap")]
|
#[cfg(feature = "debug_ctap")]
|
||||||
use core::convert::TryFrom;
|
|
||||||
use core::convert::TryInto;
|
|
||||||
#[cfg(feature = "debug_ctap")]
|
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
|
use ctap2::api::clock::Clock;
|
||||||
use ctap2::api::connection::{HidConnection, SendOrRecvStatus};
|
use ctap2::api::connection::{HidConnection, SendOrRecvStatus};
|
||||||
#[cfg(feature = "debug_ctap")]
|
|
||||||
use ctap2::clock::CtapClock;
|
|
||||||
use ctap2::clock::{new_clock, Clock, ClockInt, KEEPALIVE_DELAY, KEEPALIVE_DELAY_MS};
|
|
||||||
use ctap2::ctap::hid::HidPacketIterator;
|
use ctap2::ctap::hid::HidPacketIterator;
|
||||||
|
use ctap2::ctap::KEEPALIVE_DELAY_MS;
|
||||||
#[cfg(feature = "with_ctap1")]
|
#[cfg(feature = "with_ctap1")]
|
||||||
use ctap2::env::tock::blink_leds;
|
use ctap2::env::tock::blink_leds;
|
||||||
use ctap2::env::tock::{switch_off_leds, wink_leds, TockEnv};
|
use ctap2::env::tock::{switch_off_leds, wink_leds, TockEnv};
|
||||||
|
use ctap2::env::Env;
|
||||||
use ctap2::Transport;
|
use ctap2::Transport;
|
||||||
#[cfg(feature = "debug_ctap")]
|
|
||||||
use embedded_time::duration::Microseconds;
|
|
||||||
use embedded_time::duration::Milliseconds;
|
|
||||||
#[cfg(feature = "with_ctap1")]
|
#[cfg(feature = "with_ctap1")]
|
||||||
use libtock_drivers::buttons::{self, ButtonState};
|
use libtock_drivers::buttons::{self, ButtonState};
|
||||||
#[cfg(feature = "debug_ctap")]
|
#[cfg(feature = "debug_ctap")]
|
||||||
@@ -51,8 +45,8 @@ use usb_ctap_hid::UsbEndpoint;
|
|||||||
|
|
||||||
libtock_core::stack_size! {0x4000}
|
libtock_core::stack_size! {0x4000}
|
||||||
|
|
||||||
const SEND_TIMEOUT: Milliseconds<ClockInt> = Milliseconds(1000);
|
const SEND_TIMEOUT_MS: usize = 1000;
|
||||||
const KEEPALIVE_DELAY_TOCK: Duration<isize> = Duration::from_ms(KEEPALIVE_DELAY_MS as isize);
|
const KEEPALIVE_DELAY_MS_TOCK: Duration<isize> = Duration::from_ms(KEEPALIVE_DELAY_MS as isize);
|
||||||
|
|
||||||
#[cfg(not(feature = "vendor_hid"))]
|
#[cfg(not(feature = "vendor_hid"))]
|
||||||
const NUM_ENDPOINTS: usize = 1;
|
const NUM_ENDPOINTS: usize = 1;
|
||||||
@@ -113,20 +107,18 @@ impl EndpointReplies {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn main() {
|
|
||||||
let clock = new_clock();
|
|
||||||
|
|
||||||
|
fn main() {
|
||||||
// Setup USB driver.
|
// Setup USB driver.
|
||||||
if !usb_ctap_hid::setup() {
|
if !usb_ctap_hid::setup() {
|
||||||
panic!("Cannot setup USB driver");
|
panic!("Cannot setup USB driver");
|
||||||
}
|
}
|
||||||
|
|
||||||
let boot_time = clock.try_now().unwrap();
|
|
||||||
let env = TockEnv::new();
|
let env = TockEnv::new();
|
||||||
let mut ctap = ctap2::Ctap::new(env, boot_time);
|
let mut ctap = ctap2::Ctap::new(env);
|
||||||
|
|
||||||
let mut led_counter = 0;
|
let mut led_counter = 0;
|
||||||
let mut last_led_increment = boot_time;
|
let mut led_blink_timer = <<TockEnv as Env>::Clock as Clock>::Timer::default();
|
||||||
|
|
||||||
let mut replies = EndpointReplies::new();
|
let mut replies = EndpointReplies::new();
|
||||||
|
|
||||||
@@ -159,21 +151,27 @@ fn main() {
|
|||||||
if let Some(mut packet) = replies.next_packet() {
|
if let Some(mut packet) = replies.next_packet() {
|
||||||
// send and receive.
|
// send and receive.
|
||||||
let hid_connection = packet.transport.hid_connection(ctap.env());
|
let hid_connection = packet.transport.hid_connection(ctap.env());
|
||||||
match hid_connection.send_and_maybe_recv(&mut packet.packet, SEND_TIMEOUT) {
|
match hid_connection.send_and_maybe_recv(&mut packet.packet, SEND_TIMEOUT_MS) {
|
||||||
Ok(SendOrRecvStatus::Timeout) => {
|
Ok(SendOrRecvStatus::Timeout) => {
|
||||||
#[cfg(feature = "debug_ctap")]
|
#[cfg(feature = "debug_ctap")]
|
||||||
print_packet_notice("Sending packet timed out", &clock);
|
print_packet_notice(
|
||||||
|
"Sending packet timed out",
|
||||||
|
ctap.env().clock().timestamp_us(),
|
||||||
|
);
|
||||||
// TODO: reset the ctap_hid state.
|
// TODO: reset the ctap_hid state.
|
||||||
// Since sending the packet timed out, we cancel this reply.
|
// Since sending the packet timed out, we cancel this reply.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Ok(SendOrRecvStatus::Sent) => {
|
Ok(SendOrRecvStatus::Sent) => {
|
||||||
#[cfg(feature = "debug_ctap")]
|
#[cfg(feature = "debug_ctap")]
|
||||||
print_packet_notice("Sent packet", &clock);
|
print_packet_notice("Sent packet", ctap.env().clock().timestamp_us());
|
||||||
}
|
}
|
||||||
Ok(SendOrRecvStatus::Received(ep)) => {
|
Ok(SendOrRecvStatus::Received(ep)) => {
|
||||||
#[cfg(feature = "debug_ctap")]
|
#[cfg(feature = "debug_ctap")]
|
||||||
print_packet_notice("Received another packet", &clock);
|
print_packet_notice(
|
||||||
|
"Received another packet",
|
||||||
|
ctap.env().clock().timestamp_us(),
|
||||||
|
);
|
||||||
usb_endpoint = Some(ep);
|
usb_endpoint = Some(ep);
|
||||||
|
|
||||||
// Copy to incoming packet to local buffer to be consistent
|
// Copy to incoming packet to local buffer to be consistent
|
||||||
@@ -185,12 +183,12 @@ fn main() {
|
|||||||
} else {
|
} else {
|
||||||
// receive
|
// receive
|
||||||
usb_endpoint =
|
usb_endpoint =
|
||||||
match usb_ctap_hid::recv_with_timeout(&mut pkt_request, KEEPALIVE_DELAY_TOCK)
|
match usb_ctap_hid::recv_with_timeout(&mut pkt_request, KEEPALIVE_DELAY_MS_TOCK)
|
||||||
.flex_unwrap()
|
.flex_unwrap()
|
||||||
{
|
{
|
||||||
usb_ctap_hid::SendOrRecvStatus::Received(endpoint) => {
|
usb_ctap_hid::SendOrRecvStatus::Received(endpoint) => {
|
||||||
#[cfg(feature = "debug_ctap")]
|
#[cfg(feature = "debug_ctap")]
|
||||||
print_packet_notice("Received packet", &clock);
|
print_packet_notice("Received packet", ctap.env().clock().timestamp_us());
|
||||||
Some(endpoint)
|
Some(endpoint)
|
||||||
}
|
}
|
||||||
usb_ctap_hid::SendOrRecvStatus::Sent => {
|
usb_ctap_hid::SendOrRecvStatus::Sent => {
|
||||||
@@ -200,11 +198,10 @@ fn main() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let now = clock.try_now().unwrap();
|
|
||||||
#[cfg(feature = "with_ctap1")]
|
#[cfg(feature = "with_ctap1")]
|
||||||
{
|
{
|
||||||
if button_touched.get() {
|
if button_touched.get() {
|
||||||
ctap.state().u2f_grant_user_presence(now);
|
ctap.u2f_grant_user_presence();
|
||||||
}
|
}
|
||||||
// Cleanup button callbacks. We miss button presses while processing though.
|
// Cleanup button callbacks. We miss button presses while processing though.
|
||||||
// Heavy computation mostly follows a registered touch luckily. Unregistering
|
// Heavy computation mostly follows a registered touch luckily. Unregistering
|
||||||
@@ -218,7 +215,8 @@ fn main() {
|
|||||||
|
|
||||||
// These calls are making sure that even for long inactivity, wrapping clock values
|
// These calls are making sure that even for long inactivity, wrapping clock values
|
||||||
// don't cause problems with timers.
|
// don't cause problems with timers.
|
||||||
ctap.update_timeouts(now);
|
ctap.update_timeouts();
|
||||||
|
ctap.env().clock().tickle();
|
||||||
|
|
||||||
if let Some(endpoint) = usb_endpoint {
|
if let Some(endpoint) = usb_endpoint {
|
||||||
let transport = match endpoint {
|
let transport = match endpoint {
|
||||||
@@ -226,7 +224,7 @@ fn main() {
|
|||||||
#[cfg(feature = "vendor_hid")]
|
#[cfg(feature = "vendor_hid")]
|
||||||
UsbEndpoint::VendorHid => Transport::VendorHid,
|
UsbEndpoint::VendorHid => Transport::VendorHid,
|
||||||
};
|
};
|
||||||
let reply = ctap.process_hid_packet(&pkt_request, transport, now);
|
let reply = ctap.process_hid_packet(&pkt_request, transport);
|
||||||
if reply.has_data() {
|
if reply.has_data() {
|
||||||
// Update endpoint with the reply.
|
// Update endpoint with the reply.
|
||||||
for ep in replies.replies.iter_mut() {
|
for ep in replies.replies.iter_mut() {
|
||||||
@@ -247,28 +245,20 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let now = clock.try_now().unwrap();
|
if ctap.env().clock().is_elapsed(&led_blink_timer) {
|
||||||
if let Some(wait_duration) = now.checked_duration_since(&last_led_increment) {
|
// Loops quickly when waiting for U2F user presence, so the next LED blink
|
||||||
let wait_duration: Milliseconds<ClockInt> = wait_duration.try_into().unwrap();
|
// state is only set if enough time has elapsed.
|
||||||
if wait_duration > KEEPALIVE_DELAY {
|
|
||||||
// Loops quickly when waiting for U2F user presence, so the next LED blink
|
|
||||||
// state is only set if enough time has elapsed.
|
|
||||||
led_counter += 1;
|
|
||||||
last_led_increment = now;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// This branch means the clock frequency changed. This should never happen.
|
|
||||||
led_counter += 1;
|
led_counter += 1;
|
||||||
last_led_increment = now;
|
led_blink_timer = ctap.env().clock().make_timer(KEEPALIVE_DELAY_MS)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctap.hid().should_wink(now) {
|
if ctap.should_wink() {
|
||||||
wink_leds(led_counter);
|
wink_leds(led_counter);
|
||||||
} else {
|
} else {
|
||||||
#[cfg(not(feature = "with_ctap1"))]
|
#[cfg(not(feature = "with_ctap1"))]
|
||||||
switch_off_leds();
|
switch_off_leds();
|
||||||
#[cfg(feature = "with_ctap1")]
|
#[cfg(feature = "with_ctap1")]
|
||||||
if ctap.state().u2f_needs_user_presence(now) {
|
if ctap.u2f_needs_user_presence() {
|
||||||
// Flash the LEDs with an almost regular pattern. The inaccuracy comes from
|
// Flash the LEDs with an almost regular pattern. The inaccuracy comes from
|
||||||
// delay caused by processing and sending of packets.
|
// delay caused by processing and sending of packets.
|
||||||
blink_leds(led_counter);
|
blink_leds(led_counter);
|
||||||
@@ -280,11 +270,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "debug_ctap")]
|
#[cfg(feature = "debug_ctap")]
|
||||||
fn print_packet_notice(notice_text: &str, clock: &CtapClock) {
|
fn print_packet_notice(notice_text: &str, now_us: usize) {
|
||||||
let now = clock.try_now().unwrap();
|
|
||||||
let now_us = Microseconds::<u64>::try_from(now.duration_since_epoch())
|
|
||||||
.unwrap()
|
|
||||||
.0;
|
|
||||||
writeln!(
|
writeln!(
|
||||||
Console::new(),
|
Console::new(),
|
||||||
"{} at {}.{:06} s",
|
"{} at {}.{:06} s",
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::clock::CtapInstant;
|
|
||||||
use crate::ctap::command::{
|
use crate::ctap::command::{
|
||||||
AuthenticatorAttestationMaterial, AuthenticatorConfigParameters,
|
AuthenticatorAttestationMaterial, AuthenticatorConfigParameters,
|
||||||
AuthenticatorVendorConfigureParameters, Command,
|
AuthenticatorVendorConfigureParameters, Command,
|
||||||
@@ -28,9 +27,9 @@ const DUMMY_CHANNEL: Channel = Channel::MainHid([0x12, 0x34, 0x56, 0x78]);
|
|||||||
#[cfg(feature = "vendor_hid")]
|
#[cfg(feature = "vendor_hid")]
|
||||||
const VENDOR_CHANNEL: Channel = Channel::VendorHid([0x12, 0x34, 0x56, 0x78]);
|
const VENDOR_CHANNEL: Channel = Channel::VendorHid([0x12, 0x34, 0x56, 0x78]);
|
||||||
|
|
||||||
pub fn enable_enterprise_attestation(
|
pub fn enable_enterprise_attestation<E: Env>(
|
||||||
state: &mut CtapState,
|
state: &mut CtapState<E>,
|
||||||
env: &mut impl Env,
|
env: &mut E,
|
||||||
) -> Result<AuthenticatorAttestationMaterial, Ctap2StatusCode> {
|
) -> Result<AuthenticatorAttestationMaterial, Ctap2StatusCode> {
|
||||||
let dummy_key = [0x41; key_material::ATTESTATION_PRIVATE_KEY_LENGTH];
|
let dummy_key = [0x41; key_material::ATTESTATION_PRIVATE_KEY_LENGTH];
|
||||||
let dummy_cert = vec![0xdd; 20];
|
let dummy_cert = vec![0xdd; 20];
|
||||||
@@ -47,7 +46,7 @@ pub fn enable_enterprise_attestation(
|
|||||||
#[cfg(not(feature = "vendor_hid"))]
|
#[cfg(not(feature = "vendor_hid"))]
|
||||||
let vendor_channel = DUMMY_CHANNEL;
|
let vendor_channel = DUMMY_CHANNEL;
|
||||||
let vendor_command = Command::AuthenticatorVendorConfigure(configure_params);
|
let vendor_command = Command::AuthenticatorVendorConfigure(configure_params);
|
||||||
state.process_parsed_command(env, vendor_command, vendor_channel, CtapInstant::new(0))?;
|
state.process_parsed_command(env, vendor_command, vendor_channel)?;
|
||||||
|
|
||||||
let config_params = AuthenticatorConfigParameters {
|
let config_params = AuthenticatorConfigParameters {
|
||||||
sub_command: ConfigSubCommand::EnableEnterpriseAttestation,
|
sub_command: ConfigSubCommand::EnableEnterpriseAttestation,
|
||||||
@@ -56,7 +55,7 @@ pub fn enable_enterprise_attestation(
|
|||||||
pin_uv_auth_protocol: None,
|
pin_uv_auth_protocol: None,
|
||||||
};
|
};
|
||||||
let config_command = Command::AuthenticatorConfig(config_params);
|
let config_command = Command::AuthenticatorConfig(config_params);
|
||||||
state.process_parsed_command(env, config_command, DUMMY_CHANNEL, CtapInstant::new(0))?;
|
state.process_parsed_command(env, config_command, DUMMY_CHANNEL)?;
|
||||||
|
|
||||||
Ok(attestation_material)
|
Ok(attestation_material)
|
||||||
}
|
}
|
||||||
|
|||||||
18
third_party/libtock-drivers/src/timer.rs
vendored
18
third_party/libtock-drivers/src/timer.rs
vendored
@@ -41,6 +41,24 @@ pub fn sleep(duration: Duration<isize>) -> TockResult<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_ticks() -> TockResult<usize> {
|
||||||
|
Ok(syscalls::command(
|
||||||
|
DRIVER_NUMBER,
|
||||||
|
command_nr::GET_CLOCK_VALUE,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_clock_frequency() -> TockResult<usize> {
|
||||||
|
Ok(syscalls::command(
|
||||||
|
DRIVER_NUMBER,
|
||||||
|
command_nr::GET_CLOCK_FREQUENCY,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)?)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn with_callback<CB>(callback: CB) -> WithCallback<'static, CB> {
|
pub fn with_callback<CB>(callback: CB) -> WithCallback<'static, CB> {
|
||||||
WithCallback {
|
WithCallback {
|
||||||
callback,
|
callback,
|
||||||
|
|||||||
Reference in New Issue
Block a user