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:
committed by
Julien Cretin
parent
8a2e99960f
commit
18faf9f38f
@@ -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.
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
479
src/ctap/mod.rs
479
src/ctap/mod.rs
File diff suppressed because it is too large
Load Diff
@@ -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
18
src/env/mod.rs
vendored
Normal 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
48
src/env/test.rs
vendored
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/lib.rs
39
src/lib.rs
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
62
src/main.rs
62
src/main.rs
@@ -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)?;
|
||||||
|
|||||||
Reference in New Issue
Block a user