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 core::convert::TryFrom;
use crypto::rng256::ThreadRng256;
use ctap2::ctap::cbor_read;
use ctap2::ctap::command::{
AuthenticatorClientPinParameters, AuthenticatorGetAssertionParameters,
AuthenticatorMakeCredentialParameters,
};
use ctap2::ctap::hid::receive::MessageAssembler;
use ctap2::ctap::hid::send::HidPacketIterator;
use ctap2::ctap::hid::{ChannelID, CtapHid, HidPacket, Message};
use ctap2::ctap::status_code::Ctap2StatusCode;
use ctap2::ctap::{cbor_read, CtapState};
use ctap2::ctap::hid::{ChannelID, HidPacket, Message};
use ctap2::env::test::TestEnv;
use ctap2::Ctap;
use libtock_drivers::timer::{ClockValue, Timestamp};
const COMMAND_INIT: u8 = 0x06;
@@ -46,10 +46,6 @@ pub enum InputType {
Ctap1,
}
fn user_immediately_present(_: ChannelID) -> Result<(), Ctap2StatusCode> {
Ok(())
}
// Converts a byte slice into Message
fn raw_to_message(data: &[u8]) -> Message {
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
// after processing the init command.
fn initialize<CheckUserPresence>(
ctap_state: &mut CtapState<ThreadRng256, CheckUserPresence>,
ctap_hid: &mut CtapHid,
) -> ChannelID
where
CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>,
{
fn initialize(ctap: &mut Ctap<TestEnv>) -> ChannelID {
let nonce = vec![0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0];
let message = Message {
cid: CHANNEL_BROADCAST,
@@ -87,7 +77,7 @@ where
let mut assembler_reply = MessageAssembler::new();
let mut result_cid: ChannelID = Default::default();
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) {
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
// invokes message splitting, packet processing at CTAP HID level and response assembling.
fn process_message<CheckUserPresence>(
data: &[u8],
ctap_state: &mut CtapState<ThreadRng256, CheckUserPresence>,
ctap_hid: &mut CtapHid,
) where
CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>,
{
fn process_message(data: &[u8], ctap: &mut Ctap<TestEnv>) {
let message = raw_to_message(data);
if let Some(hid_packet_iterator) = HidPacketIterator::new(message) {
let mut assembler_reply = MessageAssembler::new();
for pkt_request in hid_packet_iterator {
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) {
// Only checks for assembling crashes, not for semantics.
let _ = assembler_reply.parse_packet(&pkt_reply, DUMMY_TIMESTAMP);
}
@@ -146,14 +128,12 @@ fn process_message<CheckUserPresence>(
// using an initialized and allocated channel.
pub fn process_ctap_any_type(data: &[u8]) {
// Initialize ctap state and hid and get the allocated cid.
let mut rng = ThreadRng256 {};
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
let mut ctap_hid = CtapHid::new();
let cid = initialize(&mut ctap_state, &mut ctap_hid);
let mut ctap = Ctap::new(TestEnv::new(), DUMMY_CLOCK_VALUE);
let cid = initialize(&mut ctap);
// Wrap input as message with the allocated cid.
let mut command = cid.to_vec();
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
@@ -164,10 +144,8 @@ pub fn process_ctap_specific_type(data: &[u8], input_type: InputType) {
return;
}
// Initialize ctap state and hid and get the allocated cid.
let mut rng = ThreadRng256 {};
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
let mut ctap_hid = CtapHid::new();
let cid = initialize(&mut ctap_state, &mut ctap_hid);
let mut ctap = Ctap::new(TestEnv::new(), DUMMY_CLOCK_VALUE);
let cid = initialize(&mut ctap);
// Wrap input as message with allocated cid and command type.
let mut command = cid.to_vec();
match input_type {
@@ -185,7 +163,7 @@ pub fn process_ctap_specific_type(data: &[u8], input_type: InputType) {
}
}
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.

View File

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

View File

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

View File

@@ -22,12 +22,12 @@ use super::ctap1;
use super::status_code::Ctap2StatusCode;
use super::timed_permission::TimedPermission;
use super::CtapState;
use crate::env::Env;
use alloc::vec;
use alloc::vec::Vec;
use arrayref::{array_ref, array_refs};
#[cfg(feature = "debug_ctap")]
use core::fmt::Write;
use crypto::rng256::Rng256;
#[cfg(feature = "debug_ctap")]
use libtock_drivers::console::Console;
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
// send as a reply.
pub fn process_hid_packet<R, CheckUserPresence>(
pub fn process_hid_packet(
&mut self,
env: &mut impl Env,
packet: &HidPacket,
clock_value: ClockValue,
ctap_state: &mut CtapState<R, CheckUserPresence>,
) -> HidPacketIterator
where
R: Rng256,
CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>,
{
ctap_state: &mut CtapState,
) -> HidPacketIterator {
// TODO: Send COMMAND_KEEPALIVE every 100ms?
match self
.assembler
@@ -183,6 +180,7 @@ impl CtapHid {
#[cfg(feature = "with_ctap1")]
match ctap1::Ctap1Command::process_command(
env,
&message.payload,
ctap_state,
clock_value,
@@ -200,7 +198,7 @@ impl CtapHid {
// don't handle any other packet in the meantime.
// TODO: Send keep-alive packets in the meantime.
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 {
cid,
cmd: CtapHid::COMMAND_CBOR,
@@ -433,27 +431,25 @@ impl CtapHid {
#[cfg(test)]
mod test {
use super::*;
use crypto::rng256::ThreadRng256;
use crate::env::test::TestEnv;
const CLOCK_FREQUENCY_HZ: usize = 32768;
// Except for tests for timeouts (done in ctap1.rs), transactions are time independant.
const DUMMY_CLOCK_VALUE: ClockValue = ClockValue::new(0, CLOCK_FREQUENCY_HZ);
const DUMMY_TIMESTAMP: Timestamp<isize> = Timestamp::from_ms(0);
fn process_messages<CheckUserPresence>(
fn process_messages(
env: &mut impl Env,
ctap_hid: &mut CtapHid,
ctap_state: &mut CtapState<ThreadRng256, CheckUserPresence>,
ctap_state: &mut CtapState,
request: Vec<Message>,
) -> Option<Vec<Message>>
where
CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>,
{
) -> Option<Vec<Message>> {
let mut result = Vec::new();
let mut assembler_reply = MessageAssembler::new();
for msg_request in request {
for pkt_request in HidPacketIterator::new(msg_request).unwrap() {
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) {
Ok(Some(message)) => result.push(message),
@@ -466,15 +462,14 @@ mod test {
Some(result)
}
fn cid_from_init<CheckUserPresence>(
fn cid_from_init(
env: &mut impl Env,
ctap_hid: &mut CtapHid,
ctap_state: &mut CtapState<ThreadRng256, CheckUserPresence>,
) -> ChannelID
where
CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>,
{
ctap_state: &mut CtapState,
) -> ChannelID {
let nonce = vec![0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0];
let reply = process_messages(
env,
ctap_hid,
ctap_state,
vec![Message {
@@ -521,15 +516,16 @@ mod test {
#[test]
fn test_spurious_continuation_packet() {
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 env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_hid = CtapHid::new();
let mut packet = [0x00; 64];
packet[0..7].copy_from_slice(&[0xC1, 0xC1, 0xC1, 0xC1, 0x00, 0x51, 0x51]);
let mut assembler_reply = MessageAssembler::new();
for pkt_reply in ctap_hid.process_hid_packet(&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.
assert_eq!(
assembler_reply
@@ -542,12 +538,12 @@ mod test {
#[test]
fn test_command_init() {
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 env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
let mut ctap_hid = CtapHid::new();
let reply = process_messages(
&mut env,
&mut ctap_hid,
&mut ctap_state,
vec![Message {
@@ -587,11 +583,10 @@ mod test {
#[test]
fn test_command_init_for_sync() {
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 env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
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.
let mut packet1 = [0x51; 64];
@@ -606,9 +601,12 @@ mod test {
let mut result = Vec::new();
let mut assembler_reply = MessageAssembler::new();
for pkt_request in &[packet1, packet2] {
for pkt_reply in
ctap_hid.process_hid_packet(pkt_request, DUMMY_CLOCK_VALUE, &mut ctap_state)
{
for pkt_reply in ctap_hid.process_hid_packet(
&mut env,
pkt_request,
DUMMY_CLOCK_VALUE,
&mut ctap_state,
) {
if let Some(message) = assembler_reply
.parse_packet(&pkt_reply, DUMMY_TIMESTAMP)
.unwrap()
@@ -647,13 +645,13 @@ mod test {
#[test]
fn test_command_ping() {
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 env = TestEnv::new();
let mut ctap_state = CtapState::new(&mut env, DUMMY_CLOCK_VALUE);
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(
&mut env,
&mut ctap_hid,
&mut ctap_state,
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.
#[allow(non_camel_case_types)]
#[allow(dead_code)]
#[derive(Debug, PartialEq)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Ctap2StatusCode {
CTAP2_OK = 0x00,
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)]
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 embedded_flash;
pub mod env;
#[macro_use]
extern crate arrayref;
pub struct Ctap<E: Env> {
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)]
extern crate alloc;
extern crate arrayref;
extern crate byteorder;
#[cfg(feature = "std")]
extern crate core;
extern crate lang_items;
#[macro_use]
extern crate arrayref;
extern crate byteorder;
mod ctap;
pub mod embedded_flash;
use core::cell::Cell;
#[cfg(feature = "debug_ctap")]
use core::fmt::Write;
use crypto::rng256::TockRng256;
use ctap::hid::{ChannelID, CtapHid, KeepaliveStatus, ProcessedPacket};
use ctap::status_code::Ctap2StatusCode;
use ctap::CtapState;
use ctap2::ctap::hid::{ChannelID, CtapHid, KeepaliveStatus, ProcessedPacket};
use ctap2::ctap::status_code::Ctap2StatusCode;
use ctap2::env;
use libtock_core::result::{CommandError, EALREADY};
use libtock_drivers::buttons;
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 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() {
// 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).
@@ -65,9 +86,11 @@ fn main() {
}
let boot_time = timer.get_current_clock().flex_unwrap();
let mut rng = TockRng256 {};
let mut ctap_state = CtapState::new(&mut rng, check_user_presence, boot_time);
let mut ctap_hid = CtapHid::new();
let env = TockEnv {
rng: TockRng256 {},
user_presence: UserPresence,
};
let mut ctap = ctap2::Ctap::new(env, boot_time);
let mut led_counter = 0;
let mut last_led_increment = boot_time;
@@ -109,7 +132,7 @@ fn main() {
#[cfg(feature = "with_ctap1")]
{
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.
// 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
// don't cause problems with timers.
ctap_state.update_timeouts(now);
ctap_hid.wink_permission = ctap_hid.wink_permission.check_expiration(now);
ctap.state().update_timeouts(now);
ctap.hid().wink_permission = ctap.hid().wink_permission.check_expiration(now);
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.
for mut pkt_reply in reply {
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;
}
if ctap_hid.wink_permission.is_granted(now) {
if ctap.hid().wink_permission.is_granted(now) {
wink_leds(led_counter);
} else {
#[cfg(not(feature = "with_ctap1"))]
switch_off_leds();
#[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
// delay caused by processing and sending of packets.
blink_leds(led_counter);
@@ -315,7 +338,8 @@ fn switch_off_leds() {
fn check_user_presence(cid: ChannelID) -> Result<(), Ctap2StatusCode> {
// 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.
send_keepalive_up_needed(cid, KEEPALIVE_DELAY)?;