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
@@ -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();
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
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.
|
||||
#[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
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)]
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
62
src/main.rs
62
src/main.rs
@@ -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)?;
|
||||
|
||||
Reference in New Issue
Block a user