Introduce a trait to abstract the CTAP environment

The end goal is to provide users with:
- the Env trait that they should implement
- the Ctap struct that they can use
This commit is contained in:
Julien Cretin
2022-03-02 13:50:08 +01:00
committed by Julien Cretin
parent 8a2e99960f
commit 18faf9f38f
10 changed files with 611 additions and 481 deletions

View File

@@ -18,16 +18,16 @@ extern crate lang_items;
use arrayref::array_ref; use arrayref::array_ref;
use core::convert::TryFrom; use core::convert::TryFrom;
use crypto::rng256::ThreadRng256; use ctap2::ctap::cbor_read;
use ctap2::ctap::command::{ use ctap2::ctap::command::{
AuthenticatorClientPinParameters, AuthenticatorGetAssertionParameters, AuthenticatorClientPinParameters, AuthenticatorGetAssertionParameters,
AuthenticatorMakeCredentialParameters, AuthenticatorMakeCredentialParameters,
}; };
use ctap2::ctap::hid::receive::MessageAssembler; use ctap2::ctap::hid::receive::MessageAssembler;
use ctap2::ctap::hid::send::HidPacketIterator; use ctap2::ctap::hid::send::HidPacketIterator;
use ctap2::ctap::hid::{ChannelID, CtapHid, HidPacket, Message}; use ctap2::ctap::hid::{ChannelID, HidPacket, Message};
use ctap2::ctap::status_code::Ctap2StatusCode; use ctap2::env::test::TestEnv;
use ctap2::ctap::{cbor_read, CtapState}; use ctap2::Ctap;
use libtock_drivers::timer::{ClockValue, Timestamp}; use libtock_drivers::timer::{ClockValue, Timestamp};
const COMMAND_INIT: u8 = 0x06; const COMMAND_INIT: u8 = 0x06;
@@ -46,10 +46,6 @@ pub enum InputType {
Ctap1, Ctap1,
} }
fn user_immediately_present(_: ChannelID) -> Result<(), Ctap2StatusCode> {
Ok(())
}
// Converts a byte slice into Message // Converts a byte slice into Message
fn raw_to_message(data: &[u8]) -> Message { fn raw_to_message(data: &[u8]) -> Message {
if data.len() <= 4 { if data.len() <= 4 {
@@ -71,13 +67,7 @@ fn raw_to_message(data: &[u8]) -> Message {
// Returns an initialized ctap state, hid and the allocated cid // Returns an initialized ctap state, hid and the allocated cid
// after processing the init command. // after processing the init command.
fn initialize<CheckUserPresence>( fn initialize(ctap: &mut Ctap<TestEnv>) -> ChannelID {
ctap_state: &mut CtapState<ThreadRng256, CheckUserPresence>,
ctap_hid: &mut CtapHid,
) -> ChannelID
where
CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>,
{
let nonce = vec![0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]; let nonce = vec![0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0];
let message = Message { let message = Message {
cid: CHANNEL_BROADCAST, cid: CHANNEL_BROADCAST,
@@ -87,7 +77,7 @@ where
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 ctap_hid.process_hid_packet(&pkt_request, DUMMY_CLOCK_VALUE, ctap_state) { for pkt_reply in ctap.process_hid_packet(&pkt_request, DUMMY_CLOCK_VALUE) {
if let Ok(Some(result)) = assembler_reply.parse_packet(&pkt_reply, DUMMY_TIMESTAMP) { if let Ok(Some(result)) = assembler_reply.parse_packet(&pkt_reply, DUMMY_TIMESTAMP) {
result_cid.copy_from_slice(&result.payload[8..12]); result_cid.copy_from_slice(&result.payload[8..12]);
} }
@@ -120,20 +110,12 @@ fn is_type(data: &[u8], input_type: InputType) -> bool {
// Interprets the raw data as a complete message (with channel id, command type and payload) and // Interprets the raw data as a complete message (with channel id, command type and payload) and
// invokes message splitting, packet processing at CTAP HID level and response assembling. // invokes message splitting, packet processing at CTAP HID level and response assembling.
fn process_message<CheckUserPresence>( fn process_message(data: &[u8], ctap: &mut Ctap<TestEnv>) {
data: &[u8],
ctap_state: &mut CtapState<ThreadRng256, CheckUserPresence>,
ctap_hid: &mut CtapHid,
) where
CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>,
{
let message = raw_to_message(data); let message = raw_to_message(data);
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, DUMMY_CLOCK_VALUE) {
ctap_hid.process_hid_packet(&pkt_request, DUMMY_CLOCK_VALUE, ctap_state)
{
// Only checks for assembling crashes, not for semantics. // Only checks for assembling crashes, not for semantics.
let _ = assembler_reply.parse_packet(&pkt_reply, DUMMY_TIMESTAMP); let _ = assembler_reply.parse_packet(&pkt_reply, DUMMY_TIMESTAMP);
} }
@@ -146,14 +128,12 @@ fn process_message<CheckUserPresence>(
// using an initialized and allocated channel. // using an initialized and allocated channel.
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 ctap = Ctap::new(TestEnv::new(), DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); let cid = initialize(&mut ctap);
let mut ctap_hid = CtapHid::new();
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.
let mut command = cid.to_vec(); let mut command = cid.to_vec();
command.extend(data); command.extend(data);
process_message(&command, &mut ctap_state, &mut ctap_hid); process_message(&command, &mut ctap);
} }
// Interprets the raw data as of the given input type and // Interprets the raw data as of the given input type and
@@ -164,10 +144,8 @@ pub fn process_ctap_specific_type(data: &[u8], input_type: InputType) {
return; return;
} }
// 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 ctap = Ctap::new(TestEnv::new(), DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE); let cid = initialize(&mut ctap);
let mut ctap_hid = CtapHid::new();
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.
let mut command = cid.to_vec(); let mut command = cid.to_vec();
match input_type { match input_type {
@@ -185,7 +163,7 @@ pub fn process_ctap_specific_type(data: &[u8], input_type: InputType) {
} }
} }
command.extend(data); command.extend(data);
process_message(&command, &mut ctap_state, &mut ctap_hid); process_message(&command, &mut ctap);
} }
// Splits the given data as HID packets and reassembles it, verifying that the original input message is reconstructed. // Splits the given data as HID packets and reassembles it, verifying that the original input message is reconstructed.

View File

@@ -359,7 +359,9 @@ mod test {
use super::super::pin_protocol::authenticate_pin_uv_auth_token; use super::super::pin_protocol::authenticate_pin_uv_auth_token;
use super::super::CtapState; use super::super::CtapState;
use super::*; use super::*;
use crypto::rng256::{Rng256, ThreadRng256}; use crate::env::test::TestEnv;
use crate::env::Env;
use crypto::rng256::Rng256;
const CLOCK_FREQUENCY_HZ: usize = 32768; const CLOCK_FREQUENCY_HZ: usize = 32768;
const DUMMY_CLOCK_VALUE: ClockValue = ClockValue::new(0, CLOCK_FREQUENCY_HZ); const DUMMY_CLOCK_VALUE: ClockValue = ClockValue::new(0, CLOCK_FREQUENCY_HZ);
@@ -383,15 +385,14 @@ mod test {
} }
fn test_helper_process_get_creds_metadata(pin_uv_auth_protocol: PinUvAuthProtocol) { fn test_helper_process_get_creds_metadata(pin_uv_auth_protocol: PinUvAuthProtocol) {
let mut rng = ThreadRng256 {}; let mut env = TestEnv::new();
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let pin_uv_auth_token = [0x55; 32]; let pin_uv_auth_token = [0x55; 32];
let client_pin = let client_pin =
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol); ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol);
let credential_source = create_credential_source(&mut rng); let credential_source = create_credential_source(env.rng());
let user_immediately_present = |_| Ok(()); let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
ctap_state.client_pin = client_pin; ctap_state.client_pin = client_pin;
ctap_state.persistent_store.set_pin(&[0u8; 16], 4).unwrap(); ctap_state.persistent_store.set_pin(&[0u8; 16], 4).unwrap();
@@ -467,17 +468,16 @@ mod test {
#[test] #[test]
fn test_process_enumerate_rps_with_uv() { fn test_process_enumerate_rps_with_uv() {
let mut rng = ThreadRng256 {}; let mut env = TestEnv::new();
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let pin_uv_auth_token = [0x55; 32]; let pin_uv_auth_token = [0x55; 32];
let client_pin = let client_pin =
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
let credential_source1 = create_credential_source(&mut rng); let credential_source1 = create_credential_source(env.rng());
let mut credential_source2 = create_credential_source(&mut rng); let mut credential_source2 = create_credential_source(env.rng());
credential_source2.rp_id = "another.example.com".to_string(); credential_source2.rp_id = "another.example.com".to_string();
let user_immediately_present = |_| Ok(()); let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
ctap_state.client_pin = client_pin; ctap_state.client_pin = client_pin;
ctap_state ctap_state
@@ -565,15 +565,14 @@ mod test {
#[test] #[test]
fn test_process_enumerate_rps_completeness() { fn test_process_enumerate_rps_completeness() {
let mut rng = ThreadRng256 {}; let mut env = TestEnv::new();
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let pin_uv_auth_token = [0x55; 32]; let pin_uv_auth_token = [0x55; 32];
let client_pin = let client_pin =
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
let credential_source = create_credential_source(&mut rng); let credential_source = create_credential_source(env.rng());
let user_immediately_present = |_| Ok(()); let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
ctap_state.client_pin = client_pin; ctap_state.client_pin = client_pin;
const NUM_CREDENTIALS: usize = 20; const NUM_CREDENTIALS: usize = 20;
@@ -648,20 +647,19 @@ mod test {
#[test] #[test]
fn test_process_enumerate_credentials_with_uv() { fn test_process_enumerate_credentials_with_uv() {
let mut rng = ThreadRng256 {}; let mut env = TestEnv::new();
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let pin_uv_auth_token = [0x55; 32]; let pin_uv_auth_token = [0x55; 32];
let client_pin = let client_pin =
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
let credential_source1 = create_credential_source(&mut rng); let credential_source1 = create_credential_source(env.rng());
let mut credential_source2 = create_credential_source(&mut rng); let mut credential_source2 = create_credential_source(env.rng());
credential_source2.user_handle = vec![0x02]; credential_source2.user_handle = vec![0x02];
credential_source2.user_name = Some("user2".to_string()); credential_source2.user_name = Some("user2".to_string());
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 user_immediately_present = |_| Ok(()); let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
ctap_state.client_pin = client_pin; ctap_state.client_pin = client_pin;
ctap_state ctap_state
@@ -754,16 +752,15 @@ mod test {
#[test] #[test]
fn test_process_delete_credential() { fn test_process_delete_credential() {
let mut rng = ThreadRng256 {}; let mut env = TestEnv::new();
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let pin_uv_auth_token = [0x55; 32]; let pin_uv_auth_token = [0x55; 32];
let client_pin = let client_pin =
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
let mut credential_source = create_credential_source(&mut rng); let mut credential_source = create_credential_source(env.rng());
credential_source.credential_id = vec![0x1D; 32]; credential_source.credential_id = vec![0x1D; 32];
let user_immediately_present = |_| Ok(()); let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
ctap_state.client_pin = client_pin; ctap_state.client_pin = client_pin;
ctap_state ctap_state
@@ -826,16 +823,15 @@ mod test {
#[test] #[test]
fn test_process_update_user_information() { fn test_process_update_user_information() {
let mut rng = ThreadRng256 {}; let mut env = TestEnv::new();
let key_agreement_key = crypto::ecdh::SecKey::gensk(&mut rng); let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
let pin_uv_auth_token = [0x55; 32]; let pin_uv_auth_token = [0x55; 32];
let client_pin = let client_pin =
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1); ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
let mut credential_source = create_credential_source(&mut rng); let mut credential_source = create_credential_source(env.rng());
credential_source.credential_id = vec![0x1D; 32]; credential_source.credential_id = vec![0x1D; 32];
let user_immediately_present = |_| Ok(()); let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
ctap_state.client_pin = client_pin; ctap_state.client_pin = client_pin;
ctap_state ctap_state
@@ -899,9 +895,8 @@ 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 rng = ThreadRng256 {}; let mut env = TestEnv::new();
let user_immediately_present = |_| Ok(()); let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
ctap_state.persistent_store.set_pin(&[0u8; 16], 4).unwrap(); ctap_state.persistent_store.set_pin(&[0u8; 16], 4).unwrap();

View File

@@ -13,14 +13,12 @@
// limitations under the License. // limitations under the License.
use super::apdu::{Apdu, ApduStatusCode}; use super::apdu::{Apdu, ApduStatusCode};
use super::hid::ChannelID;
use super::status_code::Ctap2StatusCode;
use super::CtapState; use super::CtapState;
use crate::env::Env;
use alloc::vec::Vec; use alloc::vec::Vec;
use arrayref::array_ref; use arrayref::array_ref;
use core::convert::Into; use core::convert::Into;
use core::convert::TryFrom; use core::convert::TryFrom;
use crypto::rng256::Rng256;
use libtock_drivers::timer::ClockValue; use libtock_drivers::timer::ClockValue;
// For now, they're the same thing with apdu.rs containing the authoritative definition // For now, they're the same thing with apdu.rs containing the authoritative definition
@@ -180,15 +178,12 @@ 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<R, CheckUserPresence>( pub fn process_command(
env: &mut impl Env,
message: &[u8], message: &[u8],
ctap_state: &mut CtapState<R, CheckUserPresence>, ctap_state: &mut CtapState,
clock_value: ClockValue, clock_value: ClockValue,
) -> Result<Vec<u8>, Ctap1StatusCode> ) -> Result<Vec<u8>, Ctap1StatusCode> {
where
R: Rng256,
CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>,
{
if !ctap_state if !ctap_state
.allows_ctap1() .allows_ctap1()
.map_err(|_| Ctap1StatusCode::SW_INTERNAL_EXCEPTION)? .map_err(|_| Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?
@@ -204,7 +199,7 @@ impl Ctap1Command {
if !ctap_state.u2f_up_state.consume_up(clock_value) { if !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);
} }
Ctap1Command::process_register(challenge, application, ctap_state) Ctap1Command::process_register(env, challenge, application, ctap_state)
} }
U2fCommand::Authenticate { U2fCommand::Authenticate {
@@ -220,6 +215,7 @@ impl Ctap1Command {
return Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED); return Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED);
} }
Ctap1Command::process_authenticate( Ctap1Command::process_authenticate(
env,
challenge, challenge,
application, application,
key_handle, key_handle,
@@ -247,19 +243,16 @@ impl Ctap1Command {
// +------+-------------------+-----------------+------------+--------------------+ // +------+-------------------+-----------------+------------+--------------------+
// + 0x00 | application (32B) | challenge (32B) | key handle | User pub key (65B) | // + 0x00 | application (32B) | challenge (32B) | key handle | User pub key (65B) |
// +------+-------------------+-----------------+------------+--------------------+ // +------+-------------------+-----------------+------------+--------------------+
fn process_register<R, CheckUserPresence>( fn process_register(
env: &mut impl Env,
challenge: [u8; 32], challenge: [u8; 32],
application: [u8; 32], application: [u8; 32],
ctap_state: &mut CtapState<R, CheckUserPresence>, ctap_state: &mut CtapState,
) -> Result<Vec<u8>, Ctap1StatusCode> ) -> Result<Vec<u8>, Ctap1StatusCode> {
where let sk = crypto::ecdsa::SecKey::gensk(env.rng());
R: Rng256,
CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>,
{
let sk = crypto::ecdsa::SecKey::gensk(ctap_state.rng);
let pk = sk.genpk(); let pk = sk.genpk();
let key_handle = ctap_state let key_handle = ctap_state
.encrypt_key_handle(sk, &application) .encrypt_key_handle(env, sk, &application)
.map_err(|_| Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?; .map_err(|_| Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?;
if key_handle.len() > 0xFF { if key_handle.len() > 0xFF {
// This is just being defensive with unreachable code. // This is just being defensive with unreachable code.
@@ -314,17 +307,14 @@ impl Ctap1Command {
// +-------------------+---------+--------------+-----------------+ // +-------------------+---------+--------------+-----------------+
// + application (32B) | UP (1B) | Counter (4B) | challenge (32B) | // + application (32B) | UP (1B) | Counter (4B) | challenge (32B) |
// +-------------------+---------+--------------+-----------------+ // +-------------------+---------+--------------+-----------------+
fn process_authenticate<R, CheckUserPresence>( fn process_authenticate(
env: &mut impl Env,
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<R, CheckUserPresence>, ctap_state: &mut CtapState,
) -> Result<Vec<u8>, Ctap1StatusCode> ) -> Result<Vec<u8>, Ctap1StatusCode> {
where
R: Rng256,
CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>,
{
let credential_source = ctap_state let credential_source = ctap_state
.decrypt_credential_source(key_handle, &application) .decrypt_credential_source(key_handle, &application)
.map_err(|_| Ctap1StatusCode::SW_WRONG_DATA)?; .map_err(|_| Ctap1StatusCode::SW_WRONG_DATA)?;
@@ -333,7 +323,7 @@ impl Ctap1Command {
return Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED); return Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED);
} }
ctap_state ctap_state
.increment_global_signature_counter() .increment_global_signature_counter(env)
.map_err(|_| Ctap1StatusCode::SW_WRONG_DATA)?; .map_err(|_| Ctap1StatusCode::SW_WRONG_DATA)?;
let mut signature_data = ctap_state let mut signature_data = ctap_state
.generate_auth_data(&application, Ctap1Command::USER_PRESENCE_INDICATOR_BYTE) .generate_auth_data(&application, Ctap1Command::USER_PRESENCE_INDICATOR_BYTE)
@@ -356,7 +346,7 @@ impl Ctap1Command {
mod test { mod test {
use super::super::{key_material, CREDENTIAL_ID_SIZE, USE_SIGNATURE_COUNTER}; use super::super::{key_material, CREDENTIAL_ID_SIZE, USE_SIGNATURE_COUNTER};
use super::*; use super::*;
use crypto::rng256::ThreadRng256; use crate::env::test::TestEnv;
use crypto::Hash256; use crypto::Hash256;
const CLOCK_FREQUENCY_HZ: usize = 32768; const CLOCK_FREQUENCY_HZ: usize = 32768;
@@ -406,30 +396,34 @@ mod test {
#[test] #[test]
fn test_process_allowed() { fn test_process_allowed() {
let mut rng = ThreadRng256 {}; let mut env = TestEnv::new();
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); env.user_presence()
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); .set(|_| panic!("Unexpected user presence check in CTAP1"));
let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE);
ctap_state.persistent_store.toggle_always_uv().unwrap(); ctap_state.persistent_store.toggle_always_uv().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(START_CLOCK_VALUE); ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE);
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE); ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE);
assert_eq!(response, Err(Ctap1StatusCode::SW_COMMAND_NOT_ALLOWED)); assert_eq!(response, Err(Ctap1StatusCode::SW_COMMAND_NOT_ALLOWED));
} }
#[test] #[test]
fn test_process_register() { fn test_process_register() {
let mut rng = ThreadRng256 {}; let mut env = TestEnv::new();
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); env.user_presence()
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); .set(|_| panic!("Unexpected user presence check in CTAP1"));
let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE);
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(START_CLOCK_VALUE); ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE);
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE); ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE);
// 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));
@@ -440,7 +434,8 @@ mod test {
.is_ok()); .is_ok());
ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE); ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE);
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE); ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE);
// Certificate is still missing // Certificate is still missing
assert_eq!(response, Err(Ctap1StatusCode::SW_INTERNAL_EXCEPTION)); assert_eq!(response, Err(Ctap1StatusCode::SW_INTERNAL_EXCEPTION));
@@ -452,7 +447,8 @@ mod test {
ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE); ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE);
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE); ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
let response = let response =
Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE).unwrap(); Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE)
.unwrap();
assert_eq!(response[0], Ctap1Command::LEGACY_BYTE); assert_eq!(response[0], Ctap1Command::LEGACY_BYTE);
assert_eq!(response[66], CREDENTIAL_ID_SIZE as u8); assert_eq!(response[66], CREDENTIAL_ID_SIZE as u8);
assert!(ctap_state assert!(ctap_state
@@ -468,13 +464,15 @@ mod test {
#[test] #[test]
fn test_process_register_bad_message() { fn test_process_register_bad_message() {
let mut rng = ThreadRng256 {}; let mut env = TestEnv::new();
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); env.user_presence()
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); .set(|_| panic!("Unexpected user presence check in CTAP1"));
let mut ctap_state = CtapState::new(&mut env, START_CLOCK_VALUE);
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 = Ctap1Command::process_command(
&mut env,
&message[..message.len() - 1], &message[..message.len() - 1],
&mut ctap_state, &mut ctap_state,
START_CLOCK_VALUE, START_CLOCK_VALUE,
@@ -488,60 +486,72 @@ mod test {
let application = [0x0A; 32]; let application = [0x0A; 32];
let message = create_register_message(&application); let message = create_register_message(&application);
let mut rng = ThreadRng256 {}; let mut env = TestEnv::new();
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); env.user_presence()
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); .set(|_| panic!("Unexpected user presence check in CTAP1"));
let mut ctap_state = CtapState::new(&mut env, 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);
let response = let response =
Ctap1Command::process_command(&message, &mut ctap_state, TIMEOUT_CLOCK_VALUE); Ctap1Command::process_command(&mut env, &message, &mut ctap_state, TIMEOUT_CLOCK_VALUE);
assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED)); assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED));
} }
#[test] #[test]
fn test_process_authenticate_check_only() { fn test_process_authenticate_check_only() {
let mut rng = ThreadRng256 {}; let mut env = TestEnv::new();
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); env.user_presence()
let sk = crypto::ecdsa::SecKey::gensk(&mut rng); .set(|_| panic!("Unexpected user presence check in CTAP1"));
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); let sk = crypto::ecdsa::SecKey::gensk(env.rng());
let mut ctap_state = CtapState::new(&mut env, 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());
let key_handle = ctap_state.encrypt_key_handle(sk, &application).unwrap(); let key_handle = ctap_state
.encrypt_key_handle(&mut env, sk, &application)
.unwrap();
let message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle); let message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE);
assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED)); assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED));
} }
#[test] #[test]
fn test_process_authenticate_check_only_wrong_rp() { fn test_process_authenticate_check_only_wrong_rp() {
let mut rng = ThreadRng256 {}; let mut env = TestEnv::new();
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); env.user_presence()
let sk = crypto::ecdsa::SecKey::gensk(&mut rng); .set(|_| panic!("Unexpected user presence check in CTAP1"));
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); let sk = crypto::ecdsa::SecKey::gensk(env.rng());
let mut ctap_state = CtapState::new(&mut env, 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());
let key_handle = ctap_state.encrypt_key_handle(sk, &application).unwrap(); let key_handle = ctap_state
.encrypt_key_handle(&mut env, sk, &application)
.unwrap();
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 = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE);
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_DATA)); assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_DATA));
} }
#[test] #[test]
fn test_process_authenticate_check_only_wrong_length() { fn test_process_authenticate_check_only_wrong_length() {
let mut rng = ThreadRng256 {}; let mut env = TestEnv::new();
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); env.user_presence()
let sk = crypto::ecdsa::SecKey::gensk(&mut rng); .set(|_| panic!("Unexpected user presence check in CTAP1"));
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); let sk = crypto::ecdsa::SecKey::gensk(env.rng());
let mut ctap_state = CtapState::new(&mut env, 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());
let key_handle = ctap_state.encrypt_key_handle(sk, &application).unwrap(); let key_handle = ctap_state
.encrypt_key_handle(&mut env, sk, &application)
.unwrap();
let mut message = create_authenticate_message( let mut message = create_authenticate_message(
&application, &application,
Ctap1Flags::DontEnforceUpAndSign, Ctap1Flags::DontEnforceUpAndSign,
@@ -549,73 +559,89 @@ mod test {
); );
message.push(0x00); message.push(0x00);
let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE);
assert!(response.is_ok()); assert!(response.is_ok());
message.push(0x00); message.push(0x00);
let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE);
assert!(response.is_ok()); assert!(response.is_ok());
message.push(0x00); message.push(0x00);
let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE);
assert!(response.is_ok()); assert!(response.is_ok());
message.push(0x00); message.push(0x00);
let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE);
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_LENGTH)); assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_LENGTH));
} }
#[test] #[test]
fn test_process_authenticate_check_only_wrong_cla() { fn test_process_authenticate_check_only_wrong_cla() {
let mut rng = ThreadRng256 {}; let mut env = TestEnv::new();
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); env.user_presence()
let sk = crypto::ecdsa::SecKey::gensk(&mut rng); .set(|_| panic!("Unexpected user presence check in CTAP1"));
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); let sk = crypto::ecdsa::SecKey::gensk(env.rng());
let mut ctap_state = CtapState::new(&mut env, 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());
let key_handle = ctap_state.encrypt_key_handle(sk, &application).unwrap(); let key_handle = ctap_state
.encrypt_key_handle(&mut env, sk, &application)
.unwrap();
let mut message = let mut message =
create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle); create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
message[0] = 0xEE; message[0] = 0xEE;
let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE);
assert_eq!(response, Err(Ctap1StatusCode::SW_CLA_INVALID)); assert_eq!(response, Err(Ctap1StatusCode::SW_CLA_INVALID));
} }
#[test] #[test]
fn test_process_authenticate_check_only_wrong_ins() { fn test_process_authenticate_check_only_wrong_ins() {
let mut rng = ThreadRng256 {}; let mut env = TestEnv::new();
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); env.user_presence()
let sk = crypto::ecdsa::SecKey::gensk(&mut rng); .set(|_| panic!("Unexpected user presence check in CTAP1"));
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); let sk = crypto::ecdsa::SecKey::gensk(env.rng());
let mut ctap_state = CtapState::new(&mut env, 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());
let key_handle = ctap_state.encrypt_key_handle(sk, &application).unwrap(); let key_handle = ctap_state
.encrypt_key_handle(&mut env, sk, &application)
.unwrap();
let mut message = let mut message =
create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle); create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
message[1] = 0xEE; message[1] = 0xEE;
let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE);
assert_eq!(response, Err(Ctap1StatusCode::SW_INS_INVALID)); assert_eq!(response, Err(Ctap1StatusCode::SW_INS_INVALID));
} }
#[test] #[test]
fn test_process_authenticate_check_only_wrong_flags() { fn test_process_authenticate_check_only_wrong_flags() {
let mut rng = ThreadRng256 {}; let mut env = TestEnv::new();
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); env.user_presence()
let sk = crypto::ecdsa::SecKey::gensk(&mut rng); .set(|_| panic!("Unexpected user presence check in CTAP1"));
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); let sk = crypto::ecdsa::SecKey::gensk(env.rng());
let mut ctap_state = CtapState::new(&mut env, 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());
let key_handle = ctap_state.encrypt_key_handle(sk, &application).unwrap(); let key_handle = ctap_state
.encrypt_key_handle(&mut env, sk, &application)
.unwrap();
let mut message = let mut message =
create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle); create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
message[2] = 0xEE; message[2] = 0xEE;
let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE);
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_DATA)); assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_DATA));
} }
@@ -629,21 +655,25 @@ mod test {
#[test] #[test]
fn test_process_authenticate_enforce() { fn test_process_authenticate_enforce() {
let mut rng = ThreadRng256 {}; let mut env = TestEnv::new();
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); env.user_presence()
let sk = crypto::ecdsa::SecKey::gensk(&mut rng); .set(|_| panic!("Unexpected user presence check in CTAP1"));
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); let sk = crypto::ecdsa::SecKey::gensk(env.rng());
let mut ctap_state = CtapState::new(&mut env, 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());
let key_handle = ctap_state.encrypt_key_handle(sk, &application).unwrap(); let key_handle = ctap_state
.encrypt_key_handle(&mut env, sk, &application)
.unwrap();
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(START_CLOCK_VALUE); ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE);
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE); ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
let response = let response =
Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE).unwrap(); Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE)
.unwrap();
assert_eq!(response[0], 0x01); assert_eq!(response[0], 0x01);
check_signature_counter( check_signature_counter(
array_ref!(response, 1, 4), array_ref!(response, 1, 4),
@@ -656,14 +686,17 @@ mod test {
#[test] #[test]
fn test_process_authenticate_dont_enforce() { fn test_process_authenticate_dont_enforce() {
let mut rng = ThreadRng256 {}; let mut env = TestEnv::new();
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); env.user_presence()
let sk = crypto::ecdsa::SecKey::gensk(&mut rng); .set(|_| panic!("Unexpected user presence check in CTAP1"));
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); let sk = crypto::ecdsa::SecKey::gensk(env.rng());
let mut ctap_state = CtapState::new(&mut env, 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());
let key_handle = ctap_state.encrypt_key_handle(sk, &application).unwrap(); let key_handle = ctap_state
.encrypt_key_handle(&mut env, sk, &application)
.unwrap();
let message = create_authenticate_message( let message = create_authenticate_message(
&application, &application,
Ctap1Flags::DontEnforceUpAndSign, Ctap1Flags::DontEnforceUpAndSign,
@@ -671,7 +704,8 @@ mod test {
); );
let response = let response =
Ctap1Command::process_command(&message, &mut ctap_state, TIMEOUT_CLOCK_VALUE).unwrap(); Ctap1Command::process_command(&mut env, &message, &mut ctap_state, TIMEOUT_CLOCK_VALUE)
.unwrap();
assert_eq!(response[0], 0x01); assert_eq!(response[0], 0x01);
check_signature_counter( check_signature_counter(
array_ref!(response, 1, 4), array_ref!(response, 1, 4),
@@ -689,13 +723,15 @@ mod test {
let message = let message =
create_authenticate_message(&application, Ctap1Flags::EnforceUpAndSign, &key_handle); create_authenticate_message(&application, Ctap1Flags::EnforceUpAndSign, &key_handle);
let mut rng = ThreadRng256 {}; let mut env = TestEnv::new();
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); env.user_presence()
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); .set(|_| panic!("Unexpected user presence check in CTAP1"));
let mut ctap_state = CtapState::new(&mut env, 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);
let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); let response =
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, START_CLOCK_VALUE);
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_DATA)); assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_DATA));
} }
@@ -706,14 +742,15 @@ mod test {
let message = let message =
create_authenticate_message(&application, Ctap1Flags::EnforceUpAndSign, &key_handle); create_authenticate_message(&application, Ctap1Flags::EnforceUpAndSign, &key_handle);
let mut rng = ThreadRng256 {}; let mut env = TestEnv::new();
let dummy_user_presence = |_| panic!("Unexpected user presence check in CTAP1"); env.user_presence()
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE); .set(|_| panic!("Unexpected user presence check in CTAP1"));
let mut ctap_state = CtapState::new(&mut env, 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);
let response = let response =
Ctap1Command::process_command(&message, &mut ctap_state, TIMEOUT_CLOCK_VALUE); Ctap1Command::process_command(&mut env, &message, &mut ctap_state, TIMEOUT_CLOCK_VALUE);
assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED)); assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED));
} }
} }

View File

@@ -22,12 +22,12 @@ use super::ctap1;
use super::status_code::Ctap2StatusCode; use super::status_code::Ctap2StatusCode;
use super::timed_permission::TimedPermission; use super::timed_permission::TimedPermission;
use super::CtapState; use super::CtapState;
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};
#[cfg(feature = "debug_ctap")] #[cfg(feature = "debug_ctap")]
use core::fmt::Write; use core::fmt::Write;
use crypto::rng256::Rng256;
#[cfg(feature = "debug_ctap")] #[cfg(feature = "debug_ctap")]
use libtock_drivers::console::Console; use libtock_drivers::console::Console;
use libtock_drivers::timer::{ClockValue, Duration, Timestamp}; use libtock_drivers::timer::{ClockValue, Duration, Timestamp};
@@ -146,16 +146,13 @@ impl CtapHid {
// Process an incoming USB HID packet, and optionally returns a list of outgoing packets to // Process an incoming USB HID packet, and optionally returns a list of outgoing packets to
// send as a reply. // send as a reply.
pub fn process_hid_packet<R, CheckUserPresence>( pub fn process_hid_packet(
&mut self, &mut self,
env: &mut impl Env,
packet: &HidPacket, packet: &HidPacket,
clock_value: ClockValue, clock_value: ClockValue,
ctap_state: &mut CtapState<R, CheckUserPresence>, ctap_state: &mut CtapState,
) -> HidPacketIterator ) -> HidPacketIterator {
where
R: Rng256,
CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>,
{
// TODO: Send COMMAND_KEEPALIVE every 100ms? // TODO: Send COMMAND_KEEPALIVE every 100ms?
match self match self
.assembler .assembler
@@ -183,6 +180,7 @@ impl CtapHid {
#[cfg(feature = "with_ctap1")] #[cfg(feature = "with_ctap1")]
match ctap1::Ctap1Command::process_command( match ctap1::Ctap1Command::process_command(
env,
&message.payload, &message.payload,
ctap_state, ctap_state,
clock_value, clock_value,
@@ -200,7 +198,7 @@ impl CtapHid {
// 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 = let response =
ctap_state.process_command(&message.payload, cid, clock_value); ctap_state.process_command(env, &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,
@@ -433,27 +431,25 @@ impl CtapHid {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crypto::rng256::ThreadRng256; use crate::env::test::TestEnv;
const CLOCK_FREQUENCY_HZ: usize = 32768; const CLOCK_FREQUENCY_HZ: usize = 32768;
// Except for tests for timeouts (done in ctap1.rs), transactions are time independant. // Except for tests for timeouts (done in ctap1.rs), transactions are time independant.
const DUMMY_CLOCK_VALUE: ClockValue = ClockValue::new(0, CLOCK_FREQUENCY_HZ); const DUMMY_CLOCK_VALUE: ClockValue = ClockValue::new(0, CLOCK_FREQUENCY_HZ);
const DUMMY_TIMESTAMP: Timestamp<isize> = Timestamp::from_ms(0); const DUMMY_TIMESTAMP: Timestamp<isize> = Timestamp::from_ms(0);
fn process_messages<CheckUserPresence>( fn process_messages(
env: &mut impl Env,
ctap_hid: &mut CtapHid, ctap_hid: &mut CtapHid,
ctap_state: &mut CtapState<ThreadRng256, CheckUserPresence>, ctap_state: &mut CtapState,
request: Vec<Message>, request: Vec<Message>,
) -> Option<Vec<Message>> ) -> Option<Vec<Message>> {
where
CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>,
{
let mut result = Vec::new(); let mut result = Vec::new();
let mut assembler_reply = MessageAssembler::new(); let mut assembler_reply = MessageAssembler::new();
for msg_request in request { for msg_request in request {
for pkt_request in HidPacketIterator::new(msg_request).unwrap() { for pkt_request in HidPacketIterator::new(msg_request).unwrap() {
for pkt_reply in for pkt_reply in
ctap_hid.process_hid_packet(&pkt_request, DUMMY_CLOCK_VALUE, ctap_state) ctap_hid.process_hid_packet(env, &pkt_request, DUMMY_CLOCK_VALUE, ctap_state)
{ {
match assembler_reply.parse_packet(&pkt_reply, DUMMY_TIMESTAMP) { match assembler_reply.parse_packet(&pkt_reply, DUMMY_TIMESTAMP) {
Ok(Some(message)) => result.push(message), Ok(Some(message)) => result.push(message),
@@ -466,15 +462,14 @@ mod test {
Some(result) Some(result)
} }
fn cid_from_init<CheckUserPresence>( fn cid_from_init(
env: &mut impl Env,
ctap_hid: &mut CtapHid, ctap_hid: &mut CtapHid,
ctap_state: &mut CtapState<ThreadRng256, CheckUserPresence>, ctap_state: &mut CtapState,
) -> ChannelID ) -> ChannelID {
where
CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>,
{
let nonce = vec![0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]; let nonce = vec![0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0];
let reply = process_messages( let reply = process_messages(
env,
ctap_hid, ctap_hid,
ctap_state, ctap_state,
vec![Message { vec![Message {
@@ -521,15 +516,16 @@ mod test {
#[test] #[test]
fn test_spurious_continuation_packet() { fn test_spurious_continuation_packet() {
let mut rng = ThreadRng256 {}; let mut env = TestEnv::new();
let user_immediately_present = |_| Ok(()); let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
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];
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]);
let mut assembler_reply = MessageAssembler::new(); let mut assembler_reply = MessageAssembler::new();
for pkt_reply in ctap_hid.process_hid_packet(&packet, DUMMY_CLOCK_VALUE, &mut ctap_state) { for pkt_reply in
ctap_hid.process_hid_packet(&mut env, &packet, DUMMY_CLOCK_VALUE, &mut ctap_state)
{
// Continuation packets are silently ignored. // Continuation packets are silently ignored.
assert_eq!( assert_eq!(
assembler_reply assembler_reply
@@ -542,12 +538,12 @@ mod test {
#[test] #[test]
fn test_command_init() { fn test_command_init() {
let mut rng = ThreadRng256 {}; let mut env = TestEnv::new();
let user_immediately_present = |_| Ok(()); let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
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(
&mut env,
&mut ctap_hid, &mut ctap_hid,
&mut ctap_state, &mut ctap_state,
vec![Message { vec![Message {
@@ -587,11 +583,10 @@ mod test {
#[test] #[test]
fn test_command_init_for_sync() { fn test_command_init_for_sync() {
let mut rng = ThreadRng256 {}; let mut env = TestEnv::new();
let user_immediately_present = |_| Ok(()); let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
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 env, &mut ctap_hid, &mut ctap_state);
// 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];
@@ -606,9 +601,12 @@ mod test {
let mut result = Vec::new(); let mut result = Vec::new();
let mut assembler_reply = MessageAssembler::new(); let mut assembler_reply = MessageAssembler::new();
for pkt_request in &[packet1, packet2] { for pkt_request in &[packet1, packet2] {
for pkt_reply in for pkt_reply in ctap_hid.process_hid_packet(
ctap_hid.process_hid_packet(pkt_request, DUMMY_CLOCK_VALUE, &mut ctap_state) &mut env,
{ pkt_request,
DUMMY_CLOCK_VALUE,
&mut ctap_state,
) {
if let Some(message) = assembler_reply if let Some(message) = assembler_reply
.parse_packet(&pkt_reply, DUMMY_TIMESTAMP) .parse_packet(&pkt_reply, DUMMY_TIMESTAMP)
.unwrap() .unwrap()
@@ -647,13 +645,13 @@ mod test {
#[test] #[test]
fn test_command_ping() { fn test_command_ping() {
let mut rng = ThreadRng256 {}; let mut env = TestEnv::new();
let user_immediately_present = |_| Ok(()); let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
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 env, &mut ctap_hid, &mut ctap_state);
let reply = process_messages( let reply = process_messages(
&mut env,
&mut ctap_hid, &mut ctap_hid,
&mut ctap_state, &mut ctap_state,
vec![Message { vec![Message {

File diff suppressed because it is too large Load Diff

View File

@@ -16,7 +16,7 @@
// For now, only the CTAP2 codes are here, the CTAP1 are not included. // For now, only the CTAP2 codes are here, the CTAP1 are not included.
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
pub enum Ctap2StatusCode { pub enum Ctap2StatusCode {
CTAP2_OK = 0x00, CTAP2_OK = 0x00,
CTAP1_ERR_INVALID_COMMAND = 0x01, CTAP1_ERR_INVALID_COMMAND = 0x01,

18
src/env/mod.rs vendored Normal file
View File

@@ -0,0 +1,18 @@
use crate::ctap::hid::ChannelID;
use crate::ctap::status_code::Ctap2StatusCode;
use crypto::rng256::Rng256;
#[cfg(feature = "std")]
pub mod test;
pub trait UserPresence {
fn check(&self, cid: ChannelID) -> Result<(), Ctap2StatusCode>;
}
pub trait Env {
type Rng: Rng256;
type UserPresence: UserPresence;
fn rng(&mut self) -> &mut Self::Rng;
fn user_presence(&mut self) -> &mut Self::UserPresence;
}

48
src/env/test.rs vendored Normal file
View File

@@ -0,0 +1,48 @@
use crate::ctap::hid::ChannelID;
use crate::ctap::status_code::Ctap2StatusCode;
use crate::env::{Env, UserPresence};
use crypto::rng256::ThreadRng256;
pub struct TestEnv {
rng: ThreadRng256,
user_presence: TestUserPresence,
}
pub struct TestUserPresence {
check: Box<dyn Fn(ChannelID) -> Result<(), Ctap2StatusCode>>,
}
impl TestEnv {
pub fn new() -> Self {
let rng = ThreadRng256 {};
let user_presence = TestUserPresence {
check: Box::new(|_| Ok(())),
};
TestEnv { rng, user_presence }
}
}
impl TestUserPresence {
pub fn set(&mut self, check: impl Fn(ChannelID) -> Result<(), Ctap2StatusCode> + 'static) {
self.check = Box::new(check);
}
}
impl UserPresence for TestUserPresence {
fn check(&self, cid: ChannelID) -> Result<(), Ctap2StatusCode> {
(self.check)(cid)
}
}
impl Env for TestEnv {
type Rng = ThreadRng256;
type UserPresence = TestUserPresence;
fn rng(&mut self) -> &mut Self::Rng {
&mut self.rng
}
fn user_presence(&mut self) -> &mut Self::UserPresence {
&mut self.user_presence
}
}

View File

@@ -15,9 +15,44 @@
#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc; extern crate alloc;
#[macro_use]
extern crate arrayref;
use crate::ctap::hid::send::HidPacketIterator;
use crate::ctap::hid::{CtapHid, HidPacket};
use crate::ctap::CtapState;
use crate::env::Env;
use libtock_drivers::timer::ClockValue;
pub mod ctap; pub mod ctap;
pub mod embedded_flash; pub mod embedded_flash;
pub mod env;
#[macro_use] pub struct Ctap<E: Env> {
extern crate arrayref; env: E,
state: CtapState,
hid: CtapHid,
}
impl<E: Env> Ctap<E> {
// This should only take the environment, but it temporarily takes the boot time until the
// clock is part of the environment.
pub fn new(mut env: E, now: ClockValue) -> Self {
let state = CtapState::new(&mut env, now);
let hid = CtapHid::new();
Ctap { env, state, hid }
}
pub fn state(&mut self) -> &mut CtapState {
&mut self.state
}
pub fn hid(&mut self) -> &mut CtapHid {
&mut self.hid
}
pub fn process_hid_packet(&mut self, packet: &HidPacket, now: ClockValue) -> HidPacketIterator {
self.hid
.process_hid_packet(&mut self.env, packet, now, &mut self.state)
}
}

View File

@@ -15,23 +15,19 @@
#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc; extern crate alloc;
extern crate arrayref;
extern crate byteorder;
#[cfg(feature = "std")] #[cfg(feature = "std")]
extern crate core; extern crate core;
extern crate lang_items; extern crate lang_items;
#[macro_use]
extern crate arrayref;
extern crate byteorder;
mod ctap;
pub mod embedded_flash;
use core::cell::Cell; use core::cell::Cell;
#[cfg(feature = "debug_ctap")] #[cfg(feature = "debug_ctap")]
use core::fmt::Write; use core::fmt::Write;
use crypto::rng256::TockRng256; use crypto::rng256::TockRng256;
use ctap::hid::{ChannelID, CtapHid, KeepaliveStatus, ProcessedPacket}; use ctap2::ctap::hid::{ChannelID, CtapHid, KeepaliveStatus, ProcessedPacket};
use ctap::status_code::Ctap2StatusCode; use ctap2::ctap::status_code::Ctap2StatusCode;
use ctap::CtapState; use ctap2::env;
use libtock_core::result::{CommandError, EALREADY}; use libtock_core::result::{CommandError, EALREADY};
use libtock_drivers::buttons; use libtock_drivers::buttons;
use libtock_drivers::buttons::ButtonState; use libtock_drivers::buttons::ButtonState;
@@ -53,6 +49,31 @@ const KEEPALIVE_DELAY_MS: isize = 100;
const KEEPALIVE_DELAY: Duration<isize> = Duration::from_ms(KEEPALIVE_DELAY_MS); const KEEPALIVE_DELAY: Duration<isize> = Duration::from_ms(KEEPALIVE_DELAY_MS);
const SEND_TIMEOUT: Duration<isize> = Duration::from_ms(1000); const SEND_TIMEOUT: Duration<isize> = Duration::from_ms(1000);
struct TockEnv {
rng: TockRng256,
user_presence: UserPresence,
}
struct UserPresence;
impl env::UserPresence for UserPresence {
fn check(&self, cid: ChannelID) -> Result<(), Ctap2StatusCode> {
check_user_presence(cid)
}
}
impl env::Env for TockEnv {
type Rng = TockRng256;
type UserPresence = UserPresence;
fn rng(&mut self) -> &mut Self::Rng {
&mut self.rng
}
fn user_presence(&mut self) -> &mut Self::UserPresence {
&mut self.user_presence
}
}
fn main() { fn main() {
// Setup the timer with a dummy callback (we only care about reading the current time, but the // Setup the timer with a dummy callback (we only care about reading the current time, but the
// API forces us to set an alarm callback too). // API forces us to set an alarm callback too).
@@ -65,9 +86,11 @@ fn main() {
} }
let boot_time = timer.get_current_clock().flex_unwrap(); let boot_time = timer.get_current_clock().flex_unwrap();
let mut rng = TockRng256 {}; let env = TockEnv {
let mut ctap_state = CtapState::new(&mut rng, check_user_presence, boot_time); rng: TockRng256 {},
let mut ctap_hid = CtapHid::new(); user_presence: UserPresence,
};
let mut ctap = ctap2::Ctap::new(env, boot_time);
let mut led_counter = 0; let mut led_counter = 0;
let mut last_led_increment = boot_time; let mut last_led_increment = boot_time;
@@ -109,7 +132,7 @@ fn main() {
#[cfg(feature = "with_ctap1")] #[cfg(feature = "with_ctap1")]
{ {
if button_touched.get() { if button_touched.get() {
ctap_state.u2f_up_state.grant_up(now); ctap.state().u2f_up_state.grant_up(now);
} }
// 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
@@ -123,11 +146,11 @@ 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_state.update_timeouts(now); ctap.state().update_timeouts(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 {
let reply = ctap_hid.process_hid_packet(&pkt_request, now, &mut ctap_state); let reply = ctap.process_hid_packet(&pkt_request, now);
// This block handles sending packets. // This block handles sending packets.
for mut pkt_reply in reply { for mut pkt_reply in reply {
let status = usb_ctap_hid::send_or_recv_with_timeout(&mut pkt_reply, SEND_TIMEOUT); let status = usb_ctap_hid::send_or_recv_with_timeout(&mut pkt_reply, SEND_TIMEOUT);
@@ -167,14 +190,14 @@ fn main() {
last_led_increment = now; last_led_increment = now;
} }
if ctap_hid.wink_permission.is_granted(now) { if ctap.hid().wink_permission.is_granted(now) {
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_up_state.is_up_needed(now) { if ctap.state().u2f_up_state.is_up_needed(now) {
// 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);
@@ -315,7 +338,8 @@ fn switch_off_leds() {
fn check_user_presence(cid: ChannelID) -> Result<(), Ctap2StatusCode> { fn check_user_presence(cid: ChannelID) -> Result<(), Ctap2StatusCode> {
// The timeout is N times the keepalive delay. // The timeout is N times the keepalive delay.
const TIMEOUT_ITERATIONS: usize = ctap::TOUCH_TIMEOUT_MS as usize / KEEPALIVE_DELAY_MS as usize; const TIMEOUT_ITERATIONS: usize =
ctap2::ctap::TOUCH_TIMEOUT_MS as usize / KEEPALIVE_DELAY_MS as usize;
// First, send a keep-alive packet to notify that the keep-alive status has changed. // First, send a keep-alive packet to notify that the keep-alive status has changed.
send_keepalive_up_needed(cid, KEEPALIVE_DELAY)?; send_keepalive_up_needed(cid, KEEPALIVE_DELAY)?;