command timeout for GetNextAssertion
This commit is contained in:
@@ -147,7 +147,7 @@ fn process_message<CheckUserPresence>(
|
|||||||
pub fn process_ctap_any_type(data: &[u8]) {
|
pub fn process_ctap_any_type(data: &[u8]) {
|
||||||
// Initialize ctap state and hid and get the allocated cid.
|
// Initialize ctap state and hid and get the allocated cid.
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
let mut ctap_hid = CtapHid::new();
|
let mut ctap_hid = CtapHid::new();
|
||||||
let cid = initialize(&mut ctap_state, &mut ctap_hid);
|
let cid = initialize(&mut ctap_state, &mut ctap_hid);
|
||||||
// Wrap input as message with the allocated cid.
|
// Wrap input as message with the allocated cid.
|
||||||
@@ -165,7 +165,7 @@ pub fn process_ctap_specific_type(data: &[u8], input_type: InputType) {
|
|||||||
}
|
}
|
||||||
// Initialize ctap state and hid and get the allocated cid.
|
// Initialize ctap state and hid and get the allocated cid.
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
let mut ctap_hid = CtapHid::new();
|
let mut ctap_hid = CtapHid::new();
|
||||||
let cid = initialize(&mut ctap_state, &mut ctap_hid);
|
let cid = initialize(&mut ctap_state, &mut ctap_hid);
|
||||||
// Wrap input as message with allocated cid and command type.
|
// Wrap input as message with allocated cid and command type.
|
||||||
|
|||||||
@@ -427,7 +427,7 @@ mod test {
|
|||||||
fn test_process_register() {
|
fn test_process_register() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1");
|
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1");
|
||||||
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence);
|
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE);
|
||||||
|
|
||||||
let application = [0x0A; 32];
|
let application = [0x0A; 32];
|
||||||
let message = create_register_message(&application);
|
let message = create_register_message(&application);
|
||||||
@@ -456,7 +456,7 @@ mod test {
|
|||||||
fn test_process_register_bad_message() {
|
fn test_process_register_bad_message() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1");
|
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1");
|
||||||
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence);
|
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE);
|
||||||
|
|
||||||
let application = [0x0A; 32];
|
let application = [0x0A; 32];
|
||||||
let message = create_register_message(&application);
|
let message = create_register_message(&application);
|
||||||
@@ -476,7 +476,7 @@ mod test {
|
|||||||
|
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1");
|
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1");
|
||||||
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence);
|
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE);
|
||||||
|
|
||||||
ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE);
|
ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE);
|
||||||
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
|
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
|
||||||
@@ -490,7 +490,7 @@ mod test {
|
|||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1");
|
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1");
|
||||||
let sk = crypto::ecdsa::SecKey::gensk(&mut rng);
|
let sk = crypto::ecdsa::SecKey::gensk(&mut rng);
|
||||||
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence);
|
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE);
|
||||||
|
|
||||||
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());
|
||||||
@@ -508,7 +508,7 @@ mod test {
|
|||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1");
|
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1");
|
||||||
let sk = crypto::ecdsa::SecKey::gensk(&mut rng);
|
let sk = crypto::ecdsa::SecKey::gensk(&mut rng);
|
||||||
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence);
|
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE);
|
||||||
|
|
||||||
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());
|
||||||
@@ -527,7 +527,7 @@ mod test {
|
|||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1");
|
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1");
|
||||||
let sk = crypto::ecdsa::SecKey::gensk(&mut rng);
|
let sk = crypto::ecdsa::SecKey::gensk(&mut rng);
|
||||||
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence);
|
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE);
|
||||||
|
|
||||||
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());
|
||||||
@@ -553,7 +553,7 @@ mod test {
|
|||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1");
|
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1");
|
||||||
let sk = crypto::ecdsa::SecKey::gensk(&mut rng);
|
let sk = crypto::ecdsa::SecKey::gensk(&mut rng);
|
||||||
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence);
|
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE);
|
||||||
|
|
||||||
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());
|
||||||
@@ -573,7 +573,7 @@ mod test {
|
|||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1");
|
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1");
|
||||||
let sk = crypto::ecdsa::SecKey::gensk(&mut rng);
|
let sk = crypto::ecdsa::SecKey::gensk(&mut rng);
|
||||||
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence);
|
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE);
|
||||||
|
|
||||||
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());
|
||||||
@@ -593,7 +593,7 @@ mod test {
|
|||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1");
|
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1");
|
||||||
let sk = crypto::ecdsa::SecKey::gensk(&mut rng);
|
let sk = crypto::ecdsa::SecKey::gensk(&mut rng);
|
||||||
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence);
|
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE);
|
||||||
|
|
||||||
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());
|
||||||
@@ -613,7 +613,7 @@ mod test {
|
|||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1");
|
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1");
|
||||||
let sk = crypto::ecdsa::SecKey::gensk(&mut rng);
|
let sk = crypto::ecdsa::SecKey::gensk(&mut rng);
|
||||||
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence);
|
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE);
|
||||||
|
|
||||||
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());
|
||||||
@@ -640,7 +640,7 @@ mod test {
|
|||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1");
|
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1");
|
||||||
let sk = crypto::ecdsa::SecKey::gensk(&mut rng);
|
let sk = crypto::ecdsa::SecKey::gensk(&mut rng);
|
||||||
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence);
|
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE);
|
||||||
|
|
||||||
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());
|
||||||
@@ -672,7 +672,7 @@ mod test {
|
|||||||
|
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1");
|
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1");
|
||||||
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence);
|
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE);
|
||||||
|
|
||||||
ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE);
|
ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE);
|
||||||
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
|
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
|
||||||
@@ -689,7 +689,7 @@ mod test {
|
|||||||
|
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1");
|
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1");
|
||||||
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence);
|
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE);
|
||||||
|
|
||||||
ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE);
|
ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE);
|
||||||
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
|
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
|
||||||
|
|||||||
@@ -200,7 +200,8 @@ impl CtapHid {
|
|||||||
// Each transaction is atomic, so we process the command directly here and
|
// Each transaction is atomic, so we process the command directly here and
|
||||||
// don't handle any other packet in the meantime.
|
// don't handle any other packet in the meantime.
|
||||||
// TODO: Send keep-alive packets in the meantime.
|
// TODO: Send keep-alive packets in the meantime.
|
||||||
let response = ctap_state.process_command(&message.payload, cid);
|
let response =
|
||||||
|
ctap_state.process_command(&message.payload, cid, clock_value);
|
||||||
if let Some(iterator) = CtapHid::split_message(Message {
|
if let Some(iterator) = CtapHid::split_message(Message {
|
||||||
cid,
|
cid,
|
||||||
cmd: CtapHid::COMMAND_CBOR,
|
cmd: CtapHid::COMMAND_CBOR,
|
||||||
@@ -520,7 +521,7 @@ mod test {
|
|||||||
fn test_spurious_continuation_packet() {
|
fn test_spurious_continuation_packet() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let user_immediately_present = |_| Ok(());
|
let user_immediately_present = |_| Ok(());
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
let mut ctap_hid = CtapHid::new();
|
let mut ctap_hid = CtapHid::new();
|
||||||
|
|
||||||
let mut packet = [0x00; 64];
|
let mut packet = [0x00; 64];
|
||||||
@@ -541,7 +542,7 @@ mod test {
|
|||||||
fn test_command_init() {
|
fn test_command_init() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let user_immediately_present = |_| Ok(());
|
let user_immediately_present = |_| Ok(());
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
let mut ctap_hid = CtapHid::new();
|
let mut ctap_hid = CtapHid::new();
|
||||||
|
|
||||||
let reply = process_messages(
|
let reply = process_messages(
|
||||||
@@ -586,7 +587,7 @@ mod test {
|
|||||||
fn test_command_init_for_sync() {
|
fn test_command_init_for_sync() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let user_immediately_present = |_| Ok(());
|
let user_immediately_present = |_| Ok(());
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
let mut ctap_hid = CtapHid::new();
|
let mut ctap_hid = CtapHid::new();
|
||||||
let cid = cid_from_init(&mut ctap_hid, &mut ctap_state);
|
let cid = cid_from_init(&mut ctap_hid, &mut ctap_state);
|
||||||
|
|
||||||
@@ -646,7 +647,7 @@ mod test {
|
|||||||
fn test_command_ping() {
|
fn test_command_ping() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let user_immediately_present = |_| Ok(());
|
let user_immediately_present = |_| Ok(());
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
let mut ctap_hid = CtapHid::new();
|
let mut ctap_hid = CtapHid::new();
|
||||||
let cid = cid_from_init(&mut ctap_hid, &mut ctap_state);
|
let cid = cid_from_init(&mut ctap_hid, &mut ctap_state);
|
||||||
|
|
||||||
|
|||||||
490
src/ctap/mod.rs
490
src/ctap/mod.rs
@@ -47,6 +47,7 @@ use self::response::{
|
|||||||
};
|
};
|
||||||
use self::status_code::Ctap2StatusCode;
|
use self::status_code::Ctap2StatusCode;
|
||||||
use self::storage::PersistentStore;
|
use self::storage::PersistentStore;
|
||||||
|
use self::timed_permission::TimedPermission;
|
||||||
#[cfg(feature = "with_ctap1")]
|
#[cfg(feature = "with_ctap1")]
|
||||||
use self::timed_permission::U2fUserPresenceState;
|
use self::timed_permission::U2fUserPresenceState;
|
||||||
use alloc::collections::BTreeMap;
|
use alloc::collections::BTreeMap;
|
||||||
@@ -65,7 +66,7 @@ use crypto::sha256::Sha256;
|
|||||||
use crypto::Hash256;
|
use crypto::Hash256;
|
||||||
#[cfg(feature = "debug_ctap")]
|
#[cfg(feature = "debug_ctap")]
|
||||||
use libtock_drivers::console::Console;
|
use libtock_drivers::console::Console;
|
||||||
use libtock_drivers::timer::{Duration, Timestamp};
|
use libtock_drivers::timer::{ClockValue, Duration};
|
||||||
|
|
||||||
// This flag enables or disables basic attestation for FIDO2. U2F is unaffected by
|
// This flag enables or disables basic attestation for FIDO2. U2F is unaffected by
|
||||||
// this setting. The basic attestation uses the signing key from key_material.rs
|
// this setting. The basic attestation uses the signing key from key_material.rs
|
||||||
@@ -99,7 +100,8 @@ const ED_FLAG: u8 = 0x80;
|
|||||||
pub const TOUCH_TIMEOUT_MS: isize = 30000;
|
pub const TOUCH_TIMEOUT_MS: isize = 30000;
|
||||||
#[cfg(feature = "with_ctap1")]
|
#[cfg(feature = "with_ctap1")]
|
||||||
const U2F_UP_PROMPT_TIMEOUT: Duration<isize> = Duration::from_ms(10000);
|
const U2F_UP_PROMPT_TIMEOUT: Duration<isize> = Duration::from_ms(10000);
|
||||||
const RESET_TIMEOUT_MS: isize = 10000;
|
const RESET_TIMEOUT_DURATION: Duration<isize> = Duration::from_ms(10000);
|
||||||
|
const STATEFUL_COMMAND_TIMEOUT_DURATION: Duration<isize> = Duration::from_ms(30000);
|
||||||
|
|
||||||
pub const FIDO2_VERSION_STRING: &str = "FIDO_2_0";
|
pub const FIDO2_VERSION_STRING: &str = "FIDO_2_0";
|
||||||
#[cfg(feature = "with_ctap1")]
|
#[cfg(feature = "with_ctap1")]
|
||||||
@@ -140,6 +142,17 @@ struct AssertionInput {
|
|||||||
hmac_secret_input: Option<GetAssertionHmacSecretInput>,
|
hmac_secret_input: Option<GetAssertionHmacSecretInput>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct AssertionState {
|
||||||
|
assertion_input: AssertionInput,
|
||||||
|
// Sorted by ascending order of creation, so the last element is the most recent one.
|
||||||
|
next_credentials: Vec<PublicKeyCredentialSource>,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum StatefulCommand {
|
||||||
|
Reset,
|
||||||
|
GetAssertion(AssertionState),
|
||||||
|
}
|
||||||
|
|
||||||
// This struct currently holds all state, not only the persistent memory. The persistent members are
|
// This struct currently holds all state, not only the persistent memory. The persistent members are
|
||||||
// in the persistent store field.
|
// in the persistent store field.
|
||||||
pub struct CtapState<'a, R: Rng256, CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>>
|
pub struct CtapState<'a, R: Rng256, CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>>
|
||||||
@@ -150,13 +163,11 @@ pub struct CtapState<'a, R: Rng256, CheckUserPresence: Fn(ChannelID) -> Result<(
|
|||||||
check_user_presence: CheckUserPresence,
|
check_user_presence: CheckUserPresence,
|
||||||
persistent_store: PersistentStore,
|
persistent_store: PersistentStore,
|
||||||
pin_protocol_v1: PinProtocolV1,
|
pin_protocol_v1: PinProtocolV1,
|
||||||
// This variable will be irreversibly set to false RESET_TIMEOUT_MS milliseconds after boot.
|
|
||||||
accepts_reset: bool,
|
|
||||||
#[cfg(feature = "with_ctap1")]
|
#[cfg(feature = "with_ctap1")]
|
||||||
pub u2f_up_state: U2fUserPresenceState,
|
pub u2f_up_state: U2fUserPresenceState,
|
||||||
// Sorted by ascending order of creation, so the last element is the most recent one.
|
// The state initializes to Reset and its timeout, and never goes back to Reset.
|
||||||
next_credentials: Vec<PublicKeyCredentialSource>,
|
stateful_command_permission: TimedPermission,
|
||||||
next_assertion_input: Option<AssertionInput>,
|
stateful_command_type: Option<StatefulCommand>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, R, CheckUserPresence> CtapState<'a, R, CheckUserPresence>
|
impl<'a, R, CheckUserPresence> CtapState<'a, R, CheckUserPresence>
|
||||||
@@ -169,6 +180,7 @@ where
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
rng: &'a mut R,
|
rng: &'a mut R,
|
||||||
check_user_presence: CheckUserPresence,
|
check_user_presence: CheckUserPresence,
|
||||||
|
now: ClockValue,
|
||||||
) -> CtapState<'a, R, CheckUserPresence> {
|
) -> CtapState<'a, R, CheckUserPresence> {
|
||||||
let persistent_store = PersistentStore::new(rng);
|
let persistent_store = PersistentStore::new(rng);
|
||||||
let pin_protocol_v1 = PinProtocolV1::new(rng);
|
let pin_protocol_v1 = PinProtocolV1::new(rng);
|
||||||
@@ -177,20 +189,26 @@ where
|
|||||||
check_user_presence,
|
check_user_presence,
|
||||||
persistent_store,
|
persistent_store,
|
||||||
pin_protocol_v1,
|
pin_protocol_v1,
|
||||||
accepts_reset: true,
|
|
||||||
#[cfg(feature = "with_ctap1")]
|
#[cfg(feature = "with_ctap1")]
|
||||||
u2f_up_state: U2fUserPresenceState::new(
|
u2f_up_state: U2fUserPresenceState::new(
|
||||||
U2F_UP_PROMPT_TIMEOUT,
|
U2F_UP_PROMPT_TIMEOUT,
|
||||||
Duration::from_ms(TOUCH_TIMEOUT_MS),
|
Duration::from_ms(TOUCH_TIMEOUT_MS),
|
||||||
),
|
),
|
||||||
next_credentials: vec![],
|
stateful_command_permission: TimedPermission::granted(now, RESET_TIMEOUT_DURATION),
|
||||||
next_assertion_input: None,
|
stateful_command_type: Some(StatefulCommand::Reset),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_disable_reset(&mut self, timestamp: Timestamp<isize>) {
|
pub fn update_command_permission(&mut self, now: ClockValue) {
|
||||||
if timestamp - Timestamp::<isize>::from_ms(0) > Duration::from_ms(RESET_TIMEOUT_MS) {
|
self.stateful_command_permission = self.stateful_command_permission.check_expiration(now);
|
||||||
self.accepts_reset = false;
|
}
|
||||||
|
|
||||||
|
fn check_command_permission(&mut self, now: ClockValue) -> Result<(), Ctap2StatusCode> {
|
||||||
|
self.stateful_command_permission = self.stateful_command_permission.check_expiration(now);
|
||||||
|
if self.stateful_command_permission.is_granted(now) {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,7 +324,12 @@ where
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_command(&mut self, command_cbor: &[u8], cid: ChannelID) -> Vec<u8> {
|
pub fn process_command(
|
||||||
|
&mut self,
|
||||||
|
command_cbor: &[u8],
|
||||||
|
cid: ChannelID,
|
||||||
|
now: ClockValue,
|
||||||
|
) -> Vec<u8> {
|
||||||
let cmd = Command::deserialize(command_cbor);
|
let cmd = Command::deserialize(command_cbor);
|
||||||
#[cfg(feature = "debug_ctap")]
|
#[cfg(feature = "debug_ctap")]
|
||||||
writeln!(&mut Console::new(), "Received command: {:#?}", cmd).unwrap();
|
writeln!(&mut Console::new(), "Received command: {:#?}", cmd).unwrap();
|
||||||
@@ -320,17 +343,32 @@ where
|
|||||||
Duration::from_ms(TOUCH_TIMEOUT_MS),
|
Duration::from_ms(TOUCH_TIMEOUT_MS),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
match (&command, &self.stateful_command_type) {
|
||||||
|
(
|
||||||
|
Command::AuthenticatorGetNextAssertion,
|
||||||
|
Some(StatefulCommand::GetAssertion(_)),
|
||||||
|
) => (),
|
||||||
|
(Command::AuthenticatorReset, Some(StatefulCommand::Reset)) => (),
|
||||||
|
// GetInfo does not reset stateful commands.
|
||||||
|
(Command::AuthenticatorGetInfo, _) => (),
|
||||||
|
// AuthenticatorSelection does not reset stateful commands.
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
(Command::AuthenticatorSelection, _) => (),
|
||||||
|
(_, _) => {
|
||||||
|
self.stateful_command_type = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
let response = match command {
|
let response = match command {
|
||||||
Command::AuthenticatorMakeCredential(params) => {
|
Command::AuthenticatorMakeCredential(params) => {
|
||||||
self.process_make_credential(params, cid)
|
self.process_make_credential(params, cid)
|
||||||
}
|
}
|
||||||
Command::AuthenticatorGetAssertion(params) => {
|
Command::AuthenticatorGetAssertion(params) => {
|
||||||
self.process_get_assertion(params, cid)
|
self.process_get_assertion(params, cid, now)
|
||||||
}
|
}
|
||||||
Command::AuthenticatorGetNextAssertion => self.process_get_next_assertion(),
|
Command::AuthenticatorGetNextAssertion => self.process_get_next_assertion(now),
|
||||||
Command::AuthenticatorGetInfo => self.process_get_info(),
|
Command::AuthenticatorGetInfo => self.process_get_info(),
|
||||||
Command::AuthenticatorClientPin(params) => self.process_client_pin(params),
|
Command::AuthenticatorClientPin(params) => self.process_client_pin(params),
|
||||||
Command::AuthenticatorReset => self.process_reset(cid),
|
Command::AuthenticatorReset => self.process_reset(cid, now),
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
Command::AuthenticatorSelection => self.process_selection(cid),
|
Command::AuthenticatorSelection => self.process_selection(cid),
|
||||||
// TODO(kaczmarczyck) implement FIDO 2.1 commands
|
// TODO(kaczmarczyck) implement FIDO 2.1 commands
|
||||||
@@ -659,6 +697,7 @@ where
|
|||||||
&mut self,
|
&mut self,
|
||||||
get_assertion_params: AuthenticatorGetAssertionParameters,
|
get_assertion_params: AuthenticatorGetAssertionParameters,
|
||||||
cid: ChannelID,
|
cid: ChannelID,
|
||||||
|
now: ClockValue,
|
||||||
) -> Result<ResponseData, Ctap2StatusCode> {
|
) -> Result<ResponseData, Ctap2StatusCode> {
|
||||||
let AuthenticatorGetAssertionParameters {
|
let AuthenticatorGetAssertionParameters {
|
||||||
rp_id,
|
rp_id,
|
||||||
@@ -745,7 +784,6 @@ where
|
|||||||
let (credential, remaining_credentials) = credentials
|
let (credential, remaining_credentials) = credentials
|
||||||
.split_last()
|
.split_last()
|
||||||
.ok_or(Ctap2StatusCode::CTAP2_ERR_NO_CREDENTIALS)?;
|
.ok_or(Ctap2StatusCode::CTAP2_ERR_NO_CREDENTIALS)?;
|
||||||
self.next_credentials = remaining_credentials.to_vec();
|
|
||||||
|
|
||||||
self.increment_global_signature_counter()?;
|
self.increment_global_signature_counter()?;
|
||||||
|
|
||||||
@@ -754,29 +792,37 @@ where
|
|||||||
auth_data: self.generate_auth_data(&rp_id_hash, flags)?,
|
auth_data: self.generate_auth_data(&rp_id_hash, flags)?,
|
||||||
hmac_secret_input,
|
hmac_secret_input,
|
||||||
};
|
};
|
||||||
let number_of_credentials = if self.next_credentials.is_empty() {
|
let number_of_credentials = if remaining_credentials.is_empty() {
|
||||||
self.next_assertion_input = None;
|
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
self.next_assertion_input = Some(assertion_input.clone());
|
self.stateful_command_permission =
|
||||||
Some(self.next_credentials.len() + 1)
|
TimedPermission::granted(now, STATEFUL_COMMAND_TIMEOUT_DURATION);
|
||||||
|
self.stateful_command_type = Some(StatefulCommand::GetAssertion(AssertionState {
|
||||||
|
assertion_input: assertion_input.clone(),
|
||||||
|
next_credentials: remaining_credentials.to_vec(),
|
||||||
|
}));
|
||||||
|
Some(remaining_credentials.len() + 1)
|
||||||
};
|
};
|
||||||
self.assertion_response(credential, assertion_input, number_of_credentials)
|
self.assertion_response(credential, assertion_input, number_of_credentials)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_get_next_assertion(&mut self) -> Result<ResponseData, Ctap2StatusCode> {
|
fn process_get_next_assertion(
|
||||||
// TODO(kaczmarczyck) introduce timer
|
&mut self,
|
||||||
let assertion_input = self
|
now: ClockValue,
|
||||||
.next_assertion_input
|
) -> Result<ResponseData, Ctap2StatusCode> {
|
||||||
.clone()
|
self.check_command_permission(now)?;
|
||||||
.ok_or(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED)?;
|
let (assertion_input, credential) =
|
||||||
let credential = self
|
if let Some(StatefulCommand::GetAssertion(assertion_state)) =
|
||||||
.next_credentials
|
&mut self.stateful_command_type
|
||||||
.pop()
|
{
|
||||||
.ok_or(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED)?;
|
let credential = assertion_state
|
||||||
if self.next_credentials.is_empty() {
|
.next_credentials
|
||||||
self.next_assertion_input = None;
|
.pop()
|
||||||
};
|
.ok_or(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED)?;
|
||||||
|
(assertion_state.assertion_input.clone(), credential)
|
||||||
|
} else {
|
||||||
|
return Err(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED);
|
||||||
|
};
|
||||||
self.assertion_response(&credential, assertion_input, None)
|
self.assertion_response(&credential, assertion_input, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -834,10 +880,17 @@ where
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_reset(&mut self, cid: ChannelID) -> Result<ResponseData, Ctap2StatusCode> {
|
fn process_reset(
|
||||||
|
&mut self,
|
||||||
|
cid: ChannelID,
|
||||||
|
now: ClockValue,
|
||||||
|
) -> Result<ResponseData, Ctap2StatusCode> {
|
||||||
// Resets are only possible in the first 10 seconds after booting.
|
// Resets are only possible in the first 10 seconds after booting.
|
||||||
if !self.accepts_reset {
|
// TODO(kaczmarczyck) 2.1 allows Reset after Reset and 15 seconds?
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED);
|
self.check_command_permission(now)?;
|
||||||
|
match &self.stateful_command_type {
|
||||||
|
Some(StatefulCommand::Reset) => (),
|
||||||
|
_ => return Err(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED),
|
||||||
}
|
}
|
||||||
(self.check_user_presence)(cid)?;
|
(self.check_user_presence)(cid)?;
|
||||||
|
|
||||||
@@ -886,8 +939,11 @@ mod test {
|
|||||||
MakeCredentialOptions, PublicKeyCredentialRpEntity, PublicKeyCredentialUserEntity,
|
MakeCredentialOptions, PublicKeyCredentialRpEntity, PublicKeyCredentialUserEntity,
|
||||||
};
|
};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use cbor::cbor_array;
|
||||||
use crypto::rng256::ThreadRng256;
|
use crypto::rng256::ThreadRng256;
|
||||||
|
|
||||||
|
const CLOCK_FREQUENCY_HZ: usize = 32768;
|
||||||
|
const DUMMY_CLOCK_VALUE: ClockValue = ClockValue::new(0, CLOCK_FREQUENCY_HZ);
|
||||||
// The keep-alive logic in the processing of some commands needs a channel ID to send
|
// The keep-alive logic in the processing of some commands needs a channel ID to send
|
||||||
// keep-alive packets to.
|
// keep-alive packets to.
|
||||||
// In tests where we define a dummy user-presence check that immediately returns, the channel
|
// In tests where we define a dummy user-presence check that immediately returns, the channel
|
||||||
@@ -898,8 +954,8 @@ mod test {
|
|||||||
fn test_get_info() {
|
fn test_get_info() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let user_immediately_present = |_| Ok(());
|
let user_immediately_present = |_| Ok(());
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
let info_reponse = ctap_state.process_command(&[0x04], DUMMY_CHANNEL_ID);
|
let info_reponse = ctap_state.process_command(&[0x04], DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
#[cfg(feature = "with_ctap2_1")]
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
let mut expected_response = vec![0x00, 0xAA, 0x01];
|
let mut expected_response = vec![0x00, 0xAA, 0x01];
|
||||||
@@ -955,7 +1011,7 @@ mod test {
|
|||||||
rp_icon: None,
|
rp_icon: None,
|
||||||
};
|
};
|
||||||
let user = PublicKeyCredentialUserEntity {
|
let user = PublicKeyCredentialUserEntity {
|
||||||
user_id: vec![0xFA, 0xB1, 0xA2],
|
user_id: vec![0x1D],
|
||||||
user_name: None,
|
user_name: None,
|
||||||
user_display_name: None,
|
user_display_name: None,
|
||||||
user_icon: None,
|
user_icon: None,
|
||||||
@@ -1008,7 +1064,7 @@ mod test {
|
|||||||
fn test_residential_process_make_credential() {
|
fn test_residential_process_make_credential() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let user_immediately_present = |_| Ok(());
|
let user_immediately_present = |_| Ok(());
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
let make_credential_params = create_minimal_make_credential_parameters();
|
let make_credential_params = create_minimal_make_credential_parameters();
|
||||||
let make_credential_response =
|
let make_credential_response =
|
||||||
@@ -1044,7 +1100,7 @@ mod test {
|
|||||||
fn test_non_residential_process_make_credential() {
|
fn test_non_residential_process_make_credential() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let user_immediately_present = |_| Ok(());
|
let user_immediately_present = |_| Ok(());
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
let mut make_credential_params = create_minimal_make_credential_parameters();
|
let mut make_credential_params = create_minimal_make_credential_parameters();
|
||||||
make_credential_params.options.rk = false;
|
make_credential_params.options.rk = false;
|
||||||
@@ -1081,7 +1137,7 @@ mod test {
|
|||||||
fn test_process_make_credential_unsupported_algorithm() {
|
fn test_process_make_credential_unsupported_algorithm() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let user_immediately_present = |_| Ok(());
|
let user_immediately_present = |_| Ok(());
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
let mut make_credential_params = create_minimal_make_credential_parameters();
|
let mut make_credential_params = create_minimal_make_credential_parameters();
|
||||||
make_credential_params.pub_key_cred_params = vec![];
|
make_credential_params.pub_key_cred_params = vec![];
|
||||||
@@ -1099,7 +1155,7 @@ mod test {
|
|||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let excluded_private_key = crypto::ecdsa::SecKey::gensk(&mut rng);
|
let excluded_private_key = crypto::ecdsa::SecKey::gensk(&mut rng);
|
||||||
let user_immediately_present = |_| Ok(());
|
let user_immediately_present = |_| Ok(());
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
let excluded_credential_id = vec![0x01, 0x23, 0x45, 0x67];
|
let excluded_credential_id = vec![0x01, 0x23, 0x45, 0x67];
|
||||||
let make_credential_params =
|
let make_credential_params =
|
||||||
@@ -1132,7 +1188,7 @@ mod test {
|
|||||||
fn test_process_make_credential_credential_with_cred_protect() {
|
fn test_process_make_credential_credential_with_cred_protect() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let user_immediately_present = |_| Ok(());
|
let user_immediately_present = |_| Ok(());
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
let test_policy = CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList;
|
let test_policy = CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList;
|
||||||
let make_credential_params =
|
let make_credential_params =
|
||||||
@@ -1186,7 +1242,7 @@ mod test {
|
|||||||
fn test_process_make_credential_hmac_secret() {
|
fn test_process_make_credential_hmac_secret() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let user_immediately_present = |_| Ok(());
|
let user_immediately_present = |_| Ok(());
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
let extensions = Some(MakeCredentialExtensions {
|
let extensions = Some(MakeCredentialExtensions {
|
||||||
hmac_secret: true,
|
hmac_secret: true,
|
||||||
@@ -1236,7 +1292,7 @@ mod test {
|
|||||||
fn test_process_make_credential_hmac_secret_resident_key() {
|
fn test_process_make_credential_hmac_secret_resident_key() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let user_immediately_present = |_| Ok(());
|
let user_immediately_present = |_| Ok(());
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
let extensions = Some(MakeCredentialExtensions {
|
let extensions = Some(MakeCredentialExtensions {
|
||||||
hmac_secret: true,
|
hmac_secret: true,
|
||||||
@@ -1285,7 +1341,8 @@ mod test {
|
|||||||
fn test_process_make_credential_cancelled() {
|
fn test_process_make_credential_cancelled() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let user_presence_always_cancel = |_| Err(Ctap2StatusCode::CTAP2_ERR_KEEPALIVE_CANCEL);
|
let user_presence_always_cancel = |_| Err(Ctap2StatusCode::CTAP2_ERR_KEEPALIVE_CANCEL);
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_presence_always_cancel);
|
let mut ctap_state =
|
||||||
|
CtapState::new(&mut rng, user_presence_always_cancel, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
let make_credential_params = create_minimal_make_credential_parameters();
|
let make_credential_params = create_minimal_make_credential_parameters();
|
||||||
let make_credential_response =
|
let make_credential_response =
|
||||||
@@ -1297,11 +1354,43 @@ mod test {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_assertion_response(
|
||||||
|
response: Result<ResponseData, Ctap2StatusCode>,
|
||||||
|
expected_user_id: Vec<u8>,
|
||||||
|
expected_number_of_credentials: Option<u64>,
|
||||||
|
) {
|
||||||
|
match response.unwrap() {
|
||||||
|
ResponseData::AuthenticatorGetAssertion(get_assertion_response) => {
|
||||||
|
let AuthenticatorGetAssertionResponse {
|
||||||
|
auth_data,
|
||||||
|
user,
|
||||||
|
number_of_credentials,
|
||||||
|
..
|
||||||
|
} = get_assertion_response;
|
||||||
|
let expected_auth_data = vec![
|
||||||
|
0xA3, 0x79, 0xA6, 0xF6, 0xEE, 0xAF, 0xB9, 0xA5, 0x5E, 0x37, 0x8C, 0x11, 0x80,
|
||||||
|
0x34, 0xE2, 0x75, 0x1E, 0x68, 0x2F, 0xAB, 0x9F, 0x2D, 0x30, 0xAB, 0x13, 0xD2,
|
||||||
|
0x12, 0x55, 0x86, 0xCE, 0x19, 0x47, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||||
|
];
|
||||||
|
assert_eq!(auth_data, expected_auth_data);
|
||||||
|
let expected_user = PublicKeyCredentialUserEntity {
|
||||||
|
user_id: expected_user_id,
|
||||||
|
user_name: None,
|
||||||
|
user_display_name: None,
|
||||||
|
user_icon: None,
|
||||||
|
};
|
||||||
|
assert_eq!(user, Some(expected_user));
|
||||||
|
assert_eq!(number_of_credentials, expected_number_of_credentials);
|
||||||
|
}
|
||||||
|
_ => panic!("Invalid response type"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_residential_process_get_assertion() {
|
fn test_residential_process_get_assertion() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let user_immediately_present = |_| Ok(());
|
let user_immediately_present = |_| Ok(());
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
let make_credential_params = create_minimal_make_credential_parameters();
|
let make_credential_params = create_minimal_make_credential_parameters();
|
||||||
assert!(ctap_state
|
assert!(ctap_state
|
||||||
@@ -1320,34 +1409,12 @@ mod test {
|
|||||||
pin_uv_auth_param: None,
|
pin_uv_auth_param: None,
|
||||||
pin_uv_auth_protocol: None,
|
pin_uv_auth_protocol: None,
|
||||||
};
|
};
|
||||||
let get_assertion_response =
|
let get_assertion_response = ctap_state.process_get_assertion(
|
||||||
ctap_state.process_get_assertion(get_assertion_params, DUMMY_CHANNEL_ID);
|
get_assertion_params,
|
||||||
|
DUMMY_CHANNEL_ID,
|
||||||
match get_assertion_response.unwrap() {
|
DUMMY_CLOCK_VALUE,
|
||||||
ResponseData::AuthenticatorGetAssertion(get_assertion_response) => {
|
);
|
||||||
let AuthenticatorGetAssertionResponse {
|
check_assertion_response(get_assertion_response, vec![0x1D], None);
|
||||||
auth_data,
|
|
||||||
user,
|
|
||||||
number_of_credentials,
|
|
||||||
..
|
|
||||||
} = get_assertion_response;
|
|
||||||
let expected_auth_data = vec![
|
|
||||||
0xA3, 0x79, 0xA6, 0xF6, 0xEE, 0xAF, 0xB9, 0xA5, 0x5E, 0x37, 0x8C, 0x11, 0x80,
|
|
||||||
0x34, 0xE2, 0x75, 0x1E, 0x68, 0x2F, 0xAB, 0x9F, 0x2D, 0x30, 0xAB, 0x13, 0xD2,
|
|
||||||
0x12, 0x55, 0x86, 0xCE, 0x19, 0x47, 0x00, 0x00, 0x00, 0x00, 0x01,
|
|
||||||
];
|
|
||||||
assert_eq!(auth_data, expected_auth_data);
|
|
||||||
let expected_user = PublicKeyCredentialUserEntity {
|
|
||||||
user_id: vec![0xFA, 0xB1, 0xA2],
|
|
||||||
user_name: None,
|
|
||||||
user_display_name: None,
|
|
||||||
user_icon: None,
|
|
||||||
};
|
|
||||||
assert_eq!(user, Some(expected_user));
|
|
||||||
assert!(number_of_credentials.is_none());
|
|
||||||
}
|
|
||||||
_ => panic!("Invalid response type"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1355,7 +1422,7 @@ mod test {
|
|||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let sk = crypto::ecdh::SecKey::gensk(&mut rng);
|
let sk = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||||
let user_immediately_present = |_| Ok(());
|
let user_immediately_present = |_| Ok(());
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
let make_extensions = Some(MakeCredentialExtensions {
|
let make_extensions = Some(MakeCredentialExtensions {
|
||||||
hmac_secret: true,
|
hmac_secret: true,
|
||||||
@@ -1405,8 +1472,11 @@ mod test {
|
|||||||
pin_uv_auth_param: None,
|
pin_uv_auth_param: None,
|
||||||
pin_uv_auth_protocol: None,
|
pin_uv_auth_protocol: None,
|
||||||
};
|
};
|
||||||
let get_assertion_response =
|
let get_assertion_response = ctap_state.process_get_assertion(
|
||||||
ctap_state.process_get_assertion(get_assertion_params, DUMMY_CHANNEL_ID);
|
get_assertion_params,
|
||||||
|
DUMMY_CHANNEL_ID,
|
||||||
|
DUMMY_CLOCK_VALUE,
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
get_assertion_response,
|
get_assertion_response,
|
||||||
@@ -1419,7 +1489,7 @@ mod test {
|
|||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let sk = crypto::ecdh::SecKey::gensk(&mut rng);
|
let sk = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||||
let user_immediately_present = |_| Ok(());
|
let user_immediately_present = |_| Ok(());
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
let make_extensions = Some(MakeCredentialExtensions {
|
let make_extensions = Some(MakeCredentialExtensions {
|
||||||
hmac_secret: true,
|
hmac_secret: true,
|
||||||
@@ -1453,8 +1523,11 @@ mod test {
|
|||||||
pin_uv_auth_param: None,
|
pin_uv_auth_param: None,
|
||||||
pin_uv_auth_protocol: None,
|
pin_uv_auth_protocol: None,
|
||||||
};
|
};
|
||||||
let get_assertion_response =
|
let get_assertion_response = ctap_state.process_get_assertion(
|
||||||
ctap_state.process_get_assertion(get_assertion_params, DUMMY_CHANNEL_ID);
|
get_assertion_params,
|
||||||
|
DUMMY_CHANNEL_ID,
|
||||||
|
DUMMY_CLOCK_VALUE,
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
get_assertion_response,
|
get_assertion_response,
|
||||||
@@ -1468,7 +1541,7 @@ mod test {
|
|||||||
let private_key = crypto::ecdsa::SecKey::gensk(&mut rng);
|
let private_key = crypto::ecdsa::SecKey::gensk(&mut rng);
|
||||||
let credential_id = rng.gen_uniform_u8x32().to_vec();
|
let credential_id = rng.gen_uniform_u8x32().to_vec();
|
||||||
let user_immediately_present = |_| Ok(());
|
let user_immediately_present = |_| Ok(());
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
let cred_desc = PublicKeyCredentialDescriptor {
|
let cred_desc = PublicKeyCredentialDescriptor {
|
||||||
key_type: PublicKeyCredentialType::PublicKey,
|
key_type: PublicKeyCredentialType::PublicKey,
|
||||||
@@ -1480,7 +1553,7 @@ mod test {
|
|||||||
credential_id: credential_id.clone(),
|
credential_id: credential_id.clone(),
|
||||||
private_key: private_key.clone(),
|
private_key: private_key.clone(),
|
||||||
rp_id: String::from("example.com"),
|
rp_id: String::from("example.com"),
|
||||||
user_handle: vec![0x00],
|
user_handle: vec![0x1D],
|
||||||
other_ui: None,
|
other_ui: None,
|
||||||
cred_random: None,
|
cred_random: None,
|
||||||
cred_protect_policy: Some(
|
cred_protect_policy: Some(
|
||||||
@@ -1505,8 +1578,11 @@ mod test {
|
|||||||
pin_uv_auth_param: None,
|
pin_uv_auth_param: None,
|
||||||
pin_uv_auth_protocol: None,
|
pin_uv_auth_protocol: None,
|
||||||
};
|
};
|
||||||
let get_assertion_response =
|
let get_assertion_response = ctap_state.process_get_assertion(
|
||||||
ctap_state.process_get_assertion(get_assertion_params, DUMMY_CHANNEL_ID);
|
get_assertion_params,
|
||||||
|
DUMMY_CHANNEL_ID,
|
||||||
|
DUMMY_CLOCK_VALUE,
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
get_assertion_response,
|
get_assertion_response,
|
||||||
Err(Ctap2StatusCode::CTAP2_ERR_NO_CREDENTIALS),
|
Err(Ctap2StatusCode::CTAP2_ERR_NO_CREDENTIALS),
|
||||||
@@ -1524,16 +1600,19 @@ mod test {
|
|||||||
pin_uv_auth_param: None,
|
pin_uv_auth_param: None,
|
||||||
pin_uv_auth_protocol: None,
|
pin_uv_auth_protocol: None,
|
||||||
};
|
};
|
||||||
let get_assertion_response =
|
let get_assertion_response = ctap_state.process_get_assertion(
|
||||||
ctap_state.process_get_assertion(get_assertion_params, DUMMY_CHANNEL_ID);
|
get_assertion_params,
|
||||||
assert!(get_assertion_response.is_ok());
|
DUMMY_CHANNEL_ID,
|
||||||
|
DUMMY_CLOCK_VALUE,
|
||||||
|
);
|
||||||
|
check_assertion_response(get_assertion_response, vec![0x1D], None);
|
||||||
|
|
||||||
let credential = PublicKeyCredentialSource {
|
let credential = PublicKeyCredentialSource {
|
||||||
key_type: PublicKeyCredentialType::PublicKey,
|
key_type: PublicKeyCredentialType::PublicKey,
|
||||||
credential_id,
|
credential_id,
|
||||||
private_key,
|
private_key,
|
||||||
rp_id: String::from("example.com"),
|
rp_id: String::from("example.com"),
|
||||||
user_handle: vec![0x00],
|
user_handle: vec![0x1D],
|
||||||
other_ui: None,
|
other_ui: None,
|
||||||
cred_random: None,
|
cred_random: None,
|
||||||
cred_protect_policy: Some(CredentialProtectionPolicy::UserVerificationRequired),
|
cred_protect_policy: Some(CredentialProtectionPolicy::UserVerificationRequired),
|
||||||
@@ -1556,8 +1635,11 @@ mod test {
|
|||||||
pin_uv_auth_param: None,
|
pin_uv_auth_param: None,
|
||||||
pin_uv_auth_protocol: None,
|
pin_uv_auth_protocol: None,
|
||||||
};
|
};
|
||||||
let get_assertion_response =
|
let get_assertion_response = ctap_state.process_get_assertion(
|
||||||
ctap_state.process_get_assertion(get_assertion_params, DUMMY_CHANNEL_ID);
|
get_assertion_params,
|
||||||
|
DUMMY_CHANNEL_ID,
|
||||||
|
DUMMY_CLOCK_VALUE,
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
get_assertion_response,
|
get_assertion_response,
|
||||||
Err(Ctap2StatusCode::CTAP2_ERR_NO_CREDENTIALS),
|
Err(Ctap2StatusCode::CTAP2_ERR_NO_CREDENTIALS),
|
||||||
@@ -1565,10 +1647,10 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_process_get_next_assertion() {
|
fn test_process_get_next_assertion_two_credentials() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let user_immediately_present = |_| Ok(());
|
let user_immediately_present = |_| Ok(());
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
let mut make_credential_params = create_minimal_make_credential_parameters();
|
let mut make_credential_params = create_minimal_make_credential_parameters();
|
||||||
make_credential_params.user.user_id = vec![0x01];
|
make_credential_params.user.user_id = vec![0x01];
|
||||||
@@ -1593,63 +1675,135 @@ mod test {
|
|||||||
pin_uv_auth_param: None,
|
pin_uv_auth_param: None,
|
||||||
pin_uv_auth_protocol: None,
|
pin_uv_auth_protocol: None,
|
||||||
};
|
};
|
||||||
let get_assertion_response =
|
let get_assertion_response = ctap_state.process_get_assertion(
|
||||||
ctap_state.process_get_assertion(get_assertion_params, DUMMY_CHANNEL_ID);
|
get_assertion_params,
|
||||||
|
DUMMY_CHANNEL_ID,
|
||||||
|
DUMMY_CLOCK_VALUE,
|
||||||
|
);
|
||||||
|
check_assertion_response(get_assertion_response, vec![0x02], Some(2));
|
||||||
|
|
||||||
match get_assertion_response.unwrap() {
|
let get_assertion_response = ctap_state.process_get_next_assertion(DUMMY_CLOCK_VALUE);
|
||||||
ResponseData::AuthenticatorGetAssertion(get_assertion_response) => {
|
check_assertion_response(get_assertion_response, vec![0x01], None);
|
||||||
let AuthenticatorGetAssertionResponse {
|
|
||||||
auth_data,
|
|
||||||
user,
|
|
||||||
number_of_credentials,
|
|
||||||
..
|
|
||||||
} = get_assertion_response;
|
|
||||||
let expected_auth_data = vec![
|
|
||||||
0xA3, 0x79, 0xA6, 0xF6, 0xEE, 0xAF, 0xB9, 0xA5, 0x5E, 0x37, 0x8C, 0x11, 0x80,
|
|
||||||
0x34, 0xE2, 0x75, 0x1E, 0x68, 0x2F, 0xAB, 0x9F, 0x2D, 0x30, 0xAB, 0x13, 0xD2,
|
|
||||||
0x12, 0x55, 0x86, 0xCE, 0x19, 0x47, 0x00, 0x00, 0x00, 0x00, 0x01,
|
|
||||||
];
|
|
||||||
assert_eq!(auth_data, expected_auth_data);
|
|
||||||
let expected_user = PublicKeyCredentialUserEntity {
|
|
||||||
user_id: vec![0x02],
|
|
||||||
user_name: None,
|
|
||||||
user_display_name: None,
|
|
||||||
user_icon: None,
|
|
||||||
};
|
|
||||||
assert_eq!(user, Some(expected_user));
|
|
||||||
assert_eq!(number_of_credentials, Some(2));
|
|
||||||
}
|
|
||||||
_ => panic!("Invalid response type"),
|
|
||||||
}
|
|
||||||
|
|
||||||
let get_assertion_response = ctap_state.process_get_next_assertion();
|
let get_assertion_response = ctap_state.process_get_next_assertion(DUMMY_CLOCK_VALUE);
|
||||||
match get_assertion_response.unwrap() {
|
assert_eq!(
|
||||||
ResponseData::AuthenticatorGetAssertion(get_assertion_response) => {
|
get_assertion_response,
|
||||||
let AuthenticatorGetAssertionResponse {
|
Err(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED)
|
||||||
auth_data,
|
);
|
||||||
user,
|
}
|
||||||
number_of_credentials,
|
|
||||||
..
|
|
||||||
} = get_assertion_response;
|
|
||||||
let expected_auth_data = vec![
|
|
||||||
0xA3, 0x79, 0xA6, 0xF6, 0xEE, 0xAF, 0xB9, 0xA5, 0x5E, 0x37, 0x8C, 0x11, 0x80,
|
|
||||||
0x34, 0xE2, 0x75, 0x1E, 0x68, 0x2F, 0xAB, 0x9F, 0x2D, 0x30, 0xAB, 0x13, 0xD2,
|
|
||||||
0x12, 0x55, 0x86, 0xCE, 0x19, 0x47, 0x00, 0x00, 0x00, 0x00, 0x01,
|
|
||||||
];
|
|
||||||
assert_eq!(auth_data, expected_auth_data);
|
|
||||||
let expected_user = PublicKeyCredentialUserEntity {
|
|
||||||
user_id: vec![0x01],
|
|
||||||
user_name: None,
|
|
||||||
user_display_name: None,
|
|
||||||
user_icon: None,
|
|
||||||
};
|
|
||||||
assert_eq!(user, Some(expected_user));
|
|
||||||
assert!(number_of_credentials.is_none());
|
|
||||||
}
|
|
||||||
_ => panic!("Invalid response type"),
|
|
||||||
}
|
|
||||||
|
|
||||||
let get_assertion_response = ctap_state.process_get_next_assertion();
|
#[test]
|
||||||
|
fn test_process_get_next_assertion_three_credentials() {
|
||||||
|
let mut rng = ThreadRng256 {};
|
||||||
|
let user_immediately_present = |_| Ok(());
|
||||||
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
|
let mut make_credential_params = create_minimal_make_credential_parameters();
|
||||||
|
make_credential_params.user.user_id = vec![0x01];
|
||||||
|
assert!(ctap_state
|
||||||
|
.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID)
|
||||||
|
.is_ok());
|
||||||
|
let mut make_credential_params = create_minimal_make_credential_parameters();
|
||||||
|
make_credential_params.user.user_id = vec![0x02];
|
||||||
|
assert!(ctap_state
|
||||||
|
.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID)
|
||||||
|
.is_ok());
|
||||||
|
let mut make_credential_params = create_minimal_make_credential_parameters();
|
||||||
|
make_credential_params.user.user_id = vec![0x03];
|
||||||
|
assert!(ctap_state
|
||||||
|
.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID)
|
||||||
|
.is_ok());
|
||||||
|
|
||||||
|
let get_assertion_params = AuthenticatorGetAssertionParameters {
|
||||||
|
rp_id: String::from("example.com"),
|
||||||
|
client_data_hash: vec![0xCD],
|
||||||
|
allow_list: None,
|
||||||
|
extensions: None,
|
||||||
|
options: GetAssertionOptions {
|
||||||
|
up: false,
|
||||||
|
uv: false,
|
||||||
|
},
|
||||||
|
pin_uv_auth_param: None,
|
||||||
|
pin_uv_auth_protocol: None,
|
||||||
|
};
|
||||||
|
let get_assertion_response = ctap_state.process_get_assertion(
|
||||||
|
get_assertion_params,
|
||||||
|
DUMMY_CHANNEL_ID,
|
||||||
|
DUMMY_CLOCK_VALUE,
|
||||||
|
);
|
||||||
|
check_assertion_response(get_assertion_response, vec![0x03], Some(3));
|
||||||
|
|
||||||
|
let get_assertion_response = ctap_state.process_get_next_assertion(DUMMY_CLOCK_VALUE);
|
||||||
|
check_assertion_response(get_assertion_response, vec![0x02], None);
|
||||||
|
|
||||||
|
let get_assertion_response = ctap_state.process_get_next_assertion(DUMMY_CLOCK_VALUE);
|
||||||
|
check_assertion_response(get_assertion_response, vec![0x01], None);
|
||||||
|
|
||||||
|
let get_assertion_response = ctap_state.process_get_next_assertion(DUMMY_CLOCK_VALUE);
|
||||||
|
assert_eq!(
|
||||||
|
get_assertion_response,
|
||||||
|
Err(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_process_get_next_assertion_not_allowed() {
|
||||||
|
let mut rng = ThreadRng256 {};
|
||||||
|
let user_immediately_present = |_| Ok(());
|
||||||
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
|
let get_assertion_response = ctap_state.process_get_next_assertion(DUMMY_CLOCK_VALUE);
|
||||||
|
assert_eq!(
|
||||||
|
get_assertion_response,
|
||||||
|
Err(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED)
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut make_credential_params = create_minimal_make_credential_parameters();
|
||||||
|
make_credential_params.user.user_id = vec![0x01];
|
||||||
|
assert!(ctap_state
|
||||||
|
.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID)
|
||||||
|
.is_ok());
|
||||||
|
let mut make_credential_params = create_minimal_make_credential_parameters();
|
||||||
|
make_credential_params.user.user_id = vec![0x02];
|
||||||
|
assert!(ctap_state
|
||||||
|
.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID)
|
||||||
|
.is_ok());
|
||||||
|
|
||||||
|
let get_assertion_params = AuthenticatorGetAssertionParameters {
|
||||||
|
rp_id: String::from("example.com"),
|
||||||
|
client_data_hash: vec![0xCD],
|
||||||
|
allow_list: None,
|
||||||
|
extensions: None,
|
||||||
|
options: GetAssertionOptions {
|
||||||
|
up: false,
|
||||||
|
uv: false,
|
||||||
|
},
|
||||||
|
pin_uv_auth_param: None,
|
||||||
|
pin_uv_auth_protocol: None,
|
||||||
|
};
|
||||||
|
let get_assertion_response = ctap_state.process_get_assertion(
|
||||||
|
get_assertion_params,
|
||||||
|
DUMMY_CHANNEL_ID,
|
||||||
|
DUMMY_CLOCK_VALUE,
|
||||||
|
);
|
||||||
|
assert!(get_assertion_response.is_ok());
|
||||||
|
|
||||||
|
// This is a MakeCredential command.
|
||||||
|
let mut command_cbor = vec![0x01];
|
||||||
|
let cbor_value = cbor_map! {
|
||||||
|
1 => vec![0xCD; 16],
|
||||||
|
2 => cbor_map! {
|
||||||
|
"id" => "example.com",
|
||||||
|
},
|
||||||
|
3 => cbor_map! {
|
||||||
|
"id" => vec![0x1D, 0x1D, 0x1D, 0x1D],
|
||||||
|
},
|
||||||
|
4 => cbor_array![ES256_CRED_PARAM],
|
||||||
|
};
|
||||||
|
assert!(cbor::write(cbor_value, &mut command_cbor));
|
||||||
|
ctap_state.process_command(&command_cbor, DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
|
let get_assertion_response = ctap_state.process_get_next_assertion(DUMMY_CLOCK_VALUE);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
get_assertion_response,
|
get_assertion_response,
|
||||||
Err(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED)
|
Err(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED)
|
||||||
@@ -1661,7 +1815,7 @@ mod test {
|
|||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let user_immediately_present = |_| Ok(());
|
let user_immediately_present = |_| Ok(());
|
||||||
let private_key = crypto::ecdsa::SecKey::gensk(&mut rng);
|
let private_key = crypto::ecdsa::SecKey::gensk(&mut rng);
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
let credential_id = vec![0x01, 0x23, 0x45, 0x67];
|
let credential_id = vec![0x01, 0x23, 0x45, 0x67];
|
||||||
let credential_source = PublicKeyCredentialSource {
|
let credential_source = PublicKeyCredentialSource {
|
||||||
@@ -1681,7 +1835,8 @@ mod test {
|
|||||||
.is_ok());
|
.is_ok());
|
||||||
assert!(ctap_state.persistent_store.count_credentials().unwrap() > 0);
|
assert!(ctap_state.persistent_store.count_credentials().unwrap() > 0);
|
||||||
|
|
||||||
let reset_reponse = ctap_state.process_command(&[0x07], DUMMY_CHANNEL_ID);
|
let reset_reponse =
|
||||||
|
ctap_state.process_command(&[0x07], DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE);
|
||||||
let expected_response = vec![0x00];
|
let expected_response = vec![0x00];
|
||||||
assert_eq!(reset_reponse, expected_response);
|
assert_eq!(reset_reponse, expected_response);
|
||||||
assert!(ctap_state.persistent_store.count_credentials().unwrap() == 0);
|
assert!(ctap_state.persistent_store.count_credentials().unwrap() == 0);
|
||||||
@@ -1691,9 +1846,10 @@ mod test {
|
|||||||
fn test_process_reset_cancelled() {
|
fn test_process_reset_cancelled() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let user_presence_always_cancel = |_| Err(Ctap2StatusCode::CTAP2_ERR_KEEPALIVE_CANCEL);
|
let user_presence_always_cancel = |_| Err(Ctap2StatusCode::CTAP2_ERR_KEEPALIVE_CANCEL);
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_presence_always_cancel);
|
let mut ctap_state =
|
||||||
|
CtapState::new(&mut rng, user_presence_always_cancel, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
let reset_reponse = ctap_state.process_reset(DUMMY_CHANNEL_ID);
|
let reset_reponse = ctap_state.process_reset(DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
reset_reponse,
|
reset_reponse,
|
||||||
@@ -1701,14 +1857,28 @@ mod test {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_process_reset_not_first() {
|
||||||
|
let mut rng = ThreadRng256 {};
|
||||||
|
let user_immediately_present = |_| Ok(());
|
||||||
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
|
// This is a GetNextAssertion command.
|
||||||
|
ctap_state.process_command(&[0x08], DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
|
let reset_reponse = ctap_state.process_reset(DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE);
|
||||||
|
assert_eq!(reset_reponse, Err(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_process_unknown_command() {
|
fn test_process_unknown_command() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let user_immediately_present = |_| Ok(());
|
let user_immediately_present = |_| Ok(());
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
// This command does not exist.
|
// This command does not exist.
|
||||||
let reset_reponse = ctap_state.process_command(&[0xDF], DUMMY_CHANNEL_ID);
|
let reset_reponse =
|
||||||
|
ctap_state.process_command(&[0xDF], DUMMY_CHANNEL_ID, DUMMY_CLOCK_VALUE);
|
||||||
let expected_response = vec![Ctap2StatusCode::CTAP1_ERR_INVALID_COMMAND as u8];
|
let expected_response = vec![Ctap2StatusCode::CTAP1_ERR_INVALID_COMMAND as u8];
|
||||||
assert_eq!(reset_reponse, expected_response);
|
assert_eq!(reset_reponse, expected_response);
|
||||||
}
|
}
|
||||||
@@ -1718,7 +1888,7 @@ mod test {
|
|||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let user_immediately_present = |_| Ok(());
|
let user_immediately_present = |_| Ok(());
|
||||||
let private_key = crypto::ecdsa::SecKey::gensk(&mut rng);
|
let private_key = crypto::ecdsa::SecKey::gensk(&mut rng);
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
// Usually, the relying party ID or its hash is provided by the client.
|
// Usually, the relying party ID or its hash is provided by the client.
|
||||||
// We are not testing the correctness of our SHA256 here, only if it is checked.
|
// We are not testing the correctness of our SHA256 here, only if it is checked.
|
||||||
@@ -1739,7 +1909,7 @@ mod test {
|
|||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let user_immediately_present = |_| Ok(());
|
let user_immediately_present = |_| Ok(());
|
||||||
let private_key = crypto::ecdsa::SecKey::gensk(&mut rng);
|
let private_key = crypto::ecdsa::SecKey::gensk(&mut rng);
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
// Usually, the relying party ID or its hash is provided by the client.
|
// Usually, the relying party ID or its hash is provided by the client.
|
||||||
// We are not testing the correctness of our SHA256 here, only if it is checked.
|
// We are not testing the correctness of our SHA256 here, only if it is checked.
|
||||||
@@ -1762,7 +1932,7 @@ mod test {
|
|||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let user_immediately_present = |_| Ok(());
|
let user_immediately_present = |_| Ok(());
|
||||||
let private_key = crypto::ecdsa::SecKey::gensk(&mut rng);
|
let private_key = crypto::ecdsa::SecKey::gensk(&mut rng);
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
// Same as above.
|
// Same as above.
|
||||||
let rp_id_hash = [0x55; 32];
|
let rp_id_hash = [0x55; 32];
|
||||||
@@ -1784,7 +1954,7 @@ mod test {
|
|||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let user_immediately_present = |_| Ok(());
|
let user_immediately_present = |_| Ok(());
|
||||||
let private_key = crypto::ecdsa::SecKey::gensk(&mut rng);
|
let private_key = crypto::ecdsa::SecKey::gensk(&mut rng);
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
// Same as above.
|
// Same as above.
|
||||||
let rp_id_hash = [0x55; 32];
|
let rp_id_hash = [0x55; 32];
|
||||||
|
|||||||
@@ -746,6 +746,21 @@ mod test {
|
|||||||
assert!(persistent_store.count_credentials().unwrap() > 0);
|
assert!(persistent_store.count_credentials().unwrap() > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_credential_order() {
|
||||||
|
let mut rng = ThreadRng256 {};
|
||||||
|
let mut persistent_store = PersistentStore::new(&mut rng);
|
||||||
|
let credential_source = create_credential_source(&mut rng, "example.com", vec![]);
|
||||||
|
let current_latest_creation = credential_source.creation_order;
|
||||||
|
assert!(persistent_store.store_credential(credential_source).is_ok());
|
||||||
|
let mut credential_source = create_credential_source(&mut rng, "example.com", vec![]);
|
||||||
|
credential_source.creation_order = persistent_store.new_creation_order().unwrap();
|
||||||
|
assert!(credential_source.creation_order > current_latest_creation);
|
||||||
|
let current_latest_creation = credential_source.creation_order;
|
||||||
|
assert!(persistent_store.store_credential(credential_source).is_ok());
|
||||||
|
assert!(persistent_store.new_creation_order().unwrap() > current_latest_creation);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[allow(clippy::assertions_on_constants)]
|
#[allow(clippy::assertions_on_constants)]
|
||||||
fn test_fill_store() {
|
fn test_fill_store() {
|
||||||
|
|||||||
11
src/main.rs
11
src/main.rs
@@ -37,9 +37,11 @@ use libtock_drivers::console::Console;
|
|||||||
use libtock_drivers::led;
|
use libtock_drivers::led;
|
||||||
use libtock_drivers::result::{FlexUnwrap, TockError};
|
use libtock_drivers::result::{FlexUnwrap, TockError};
|
||||||
use libtock_drivers::timer;
|
use libtock_drivers::timer;
|
||||||
|
use libtock_drivers::timer::Duration;
|
||||||
#[cfg(feature = "debug_ctap")]
|
#[cfg(feature = "debug_ctap")]
|
||||||
use libtock_drivers::timer::Timer;
|
use libtock_drivers::timer::Timer;
|
||||||
use libtock_drivers::timer::{Duration, Timestamp};
|
#[cfg(feature = "debug_ctap")]
|
||||||
|
use libtock_drivers::timer::Timestamp;
|
||||||
use libtock_drivers::usb_ctap_hid;
|
use libtock_drivers::usb_ctap_hid;
|
||||||
|
|
||||||
const KEEPALIVE_DELAY_MS: isize = 100;
|
const KEEPALIVE_DELAY_MS: isize = 100;
|
||||||
@@ -57,12 +59,13 @@ fn main() {
|
|||||||
panic!("Cannot setup USB driver");
|
panic!("Cannot setup USB driver");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let boot_time = timer.get_current_clock().flex_unwrap();
|
||||||
let mut rng = TockRng256 {};
|
let mut rng = TockRng256 {};
|
||||||
let mut ctap_state = CtapState::new(&mut rng, check_user_presence);
|
let mut ctap_state = CtapState::new(&mut rng, check_user_presence, boot_time);
|
||||||
let mut ctap_hid = CtapHid::new();
|
let mut ctap_hid = CtapHid::new();
|
||||||
|
|
||||||
let mut led_counter = 0;
|
let mut led_counter = 0;
|
||||||
let mut last_led_increment = timer.get_current_clock().flex_unwrap();
|
let mut last_led_increment = boot_time;
|
||||||
|
|
||||||
// Main loop. If CTAP1 is used, we register button presses for U2F while receiving and waiting.
|
// Main loop. If CTAP1 is used, we register button presses for U2F while receiving and waiting.
|
||||||
// The way TockOS and apps currently interact, callbacks need a yield syscall to execute,
|
// The way TockOS and apps currently interact, callbacks need a yield syscall to execute,
|
||||||
@@ -115,7 +118,7 @@ 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
|
||||||
// never randomly wink or grant user presence for U2F.
|
// never randomly wink or grant user presence for U2F.
|
||||||
ctap_state.check_disable_reset(Timestamp::<isize>::from_clock_value(now));
|
ctap_state.update_command_permission(now);
|
||||||
ctap_hid.wink_permission = ctap_hid.wink_permission.check_expiration(now);
|
ctap_hid.wink_permission = ctap_hid.wink_permission.check_expiration(now);
|
||||||
|
|
||||||
if has_packet {
|
if has_packet {
|
||||||
|
|||||||
Reference in New Issue
Block a user