Merge pull request #223 from kaczmarczyck/get-next-assertion
GetNextAssertion
This commit is contained in:
@@ -147,7 +147,7 @@ fn process_message<CheckUserPresence>(
|
||||
pub fn process_ctap_any_type(data: &[u8]) {
|
||||
// Initialize ctap state and hid and get the allocated cid.
|
||||
let mut rng = ThreadRng256 {};
|
||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||
let mut ctap_hid = CtapHid::new();
|
||||
let cid = initialize(&mut ctap_state, &mut ctap_hid);
|
||||
// Wrap input as message with the allocated cid.
|
||||
@@ -165,7 +165,7 @@ pub fn process_ctap_specific_type(data: &[u8], input_type: InputType) {
|
||||
}
|
||||
// Initialize ctap state and hid and get the allocated cid.
|
||||
let mut rng = ThreadRng256 {};
|
||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||
let mut ctap_hid = CtapHid::new();
|
||||
let cid = initialize(&mut ctap_state, &mut ctap_hid);
|
||||
// Wrap input as message with allocated cid and command type.
|
||||
|
||||
@@ -57,8 +57,8 @@ impl Command {
|
||||
const AUTHENTICATOR_GET_INFO: u8 = 0x04;
|
||||
const AUTHENTICATOR_CLIENT_PIN: u8 = 0x06;
|
||||
const AUTHENTICATOR_RESET: u8 = 0x07;
|
||||
// TODO(kaczmarczyck) use or remove those constants
|
||||
const AUTHENTICATOR_GET_NEXT_ASSERTION: u8 = 0x08;
|
||||
// TODO(kaczmarczyck) use or remove those constants
|
||||
const AUTHENTICATOR_BIO_ENROLLMENT: u8 = 0x09;
|
||||
const AUTHENTICATOR_CREDENTIAL_MANAGEMENT: u8 = 0xA0;
|
||||
const AUTHENTICATOR_SELECTION: u8 = 0xB0;
|
||||
|
||||
@@ -440,7 +440,7 @@ mod 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);
|
||||
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE);
|
||||
|
||||
let application = [0x0A; 32];
|
||||
let message = create_register_message(&application);
|
||||
@@ -490,7 +490,7 @@ mod 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);
|
||||
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE);
|
||||
|
||||
let application = [0x0A; 32];
|
||||
let message = create_register_message(&application);
|
||||
@@ -510,7 +510,7 @@ mod test {
|
||||
|
||||
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);
|
||||
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE);
|
||||
|
||||
ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE);
|
||||
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
|
||||
@@ -524,7 +524,7 @@ mod test {
|
||||
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);
|
||||
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE);
|
||||
|
||||
let rp_id = "example.com";
|
||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||
@@ -542,7 +542,7 @@ mod test {
|
||||
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);
|
||||
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE);
|
||||
|
||||
let rp_id = "example.com";
|
||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||
@@ -561,7 +561,7 @@ mod test {
|
||||
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);
|
||||
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE);
|
||||
|
||||
let rp_id = "example.com";
|
||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||
@@ -587,7 +587,7 @@ mod test {
|
||||
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);
|
||||
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE);
|
||||
|
||||
let rp_id = "example.com";
|
||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||
@@ -607,7 +607,7 @@ mod test {
|
||||
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);
|
||||
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE);
|
||||
|
||||
let rp_id = "example.com";
|
||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||
@@ -627,7 +627,7 @@ mod test {
|
||||
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);
|
||||
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE);
|
||||
|
||||
let rp_id = "example.com";
|
||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||
@@ -647,7 +647,7 @@ mod test {
|
||||
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);
|
||||
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE);
|
||||
|
||||
let rp_id = "example.com";
|
||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||
@@ -674,7 +674,7 @@ mod test {
|
||||
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);
|
||||
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE);
|
||||
|
||||
let rp_id = "example.com";
|
||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||
@@ -706,7 +706,7 @@ mod test {
|
||||
|
||||
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);
|
||||
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE);
|
||||
|
||||
ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE);
|
||||
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
|
||||
@@ -723,7 +723,7 @@ mod test {
|
||||
|
||||
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);
|
||||
let mut ctap_state = CtapState::new(&mut rng, dummy_user_presence, START_CLOCK_VALUE);
|
||||
|
||||
ctap_state.u2f_up_state.consume_up(START_CLOCK_VALUE);
|
||||
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
|
||||
|
||||
@@ -308,7 +308,8 @@ impl TryFrom<cbor::Value> for GetAssertionExtensions {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Clone, Debug, PartialEq))]
|
||||
#[derive(Clone)]
|
||||
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug, PartialEq))]
|
||||
pub struct GetAssertionHmacSecretInput {
|
||||
pub key_agreement: CoseKey,
|
||||
pub salt_enc: Vec<u8>,
|
||||
@@ -499,6 +500,7 @@ pub struct PublicKeyCredentialSource {
|
||||
pub other_ui: Option<String>,
|
||||
pub cred_random: Option<Vec<u8>>,
|
||||
pub cred_protect_policy: Option<CredentialProtectionPolicy>,
|
||||
pub creation_order: u64,
|
||||
}
|
||||
|
||||
// We serialize credentials for the persistent storage using CBOR maps. Each field of a credential
|
||||
@@ -511,6 +513,7 @@ enum PublicKeyCredentialSourceField {
|
||||
OtherUi = 4,
|
||||
CredRandom = 5,
|
||||
CredProtectPolicy = 6,
|
||||
CreationOrder = 7,
|
||||
// When a field is removed, its tag should be reserved and not used for new fields. We document
|
||||
// those reserved tags below.
|
||||
// Reserved tags: none.
|
||||
@@ -534,6 +537,7 @@ impl From<PublicKeyCredentialSource> for cbor::Value {
|
||||
PublicKeyCredentialSourceField::OtherUi => credential.other_ui,
|
||||
PublicKeyCredentialSourceField::CredRandom => credential.cred_random,
|
||||
PublicKeyCredentialSourceField::CredProtectPolicy => credential.cred_protect_policy,
|
||||
PublicKeyCredentialSourceField::CreationOrder => credential.creation_order,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -551,6 +555,7 @@ impl TryFrom<cbor::Value> for PublicKeyCredentialSource {
|
||||
PublicKeyCredentialSourceField::OtherUi => other_ui,
|
||||
PublicKeyCredentialSourceField::CredRandom => cred_random,
|
||||
PublicKeyCredentialSourceField::CredProtectPolicy => cred_protect_policy,
|
||||
PublicKeyCredentialSourceField::CreationOrder => creation_order,
|
||||
} = extract_map(cbor_value)?;
|
||||
}
|
||||
|
||||
@@ -568,6 +573,7 @@ impl TryFrom<cbor::Value> for PublicKeyCredentialSource {
|
||||
let cred_protect_policy = cred_protect_policy
|
||||
.map(CredentialProtectionPolicy::try_from)
|
||||
.transpose()?;
|
||||
let creation_order = creation_order.map(extract_unsigned).unwrap_or(Ok(0))?;
|
||||
// We don't return whether there were unknown fields in the CBOR value. This means that
|
||||
// deserialization is not injective. In particular deserialization is only an inverse of
|
||||
// serialization at a given version of OpenSK. This is not a problem because:
|
||||
@@ -587,6 +593,7 @@ impl TryFrom<cbor::Value> for PublicKeyCredentialSource {
|
||||
other_ui,
|
||||
cred_random,
|
||||
cred_protect_policy,
|
||||
creation_order,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -603,7 +610,8 @@ impl PublicKeyCredentialSource {
|
||||
// TODO(kaczmarczyck) we could decide to split this data type up
|
||||
// It depends on the algorithm though, I think.
|
||||
// So before creating a mess, this is my workaround.
|
||||
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Clone, Debug, PartialEq))]
|
||||
#[derive(Clone)]
|
||||
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug, PartialEq))]
|
||||
pub struct CoseKey(pub BTreeMap<cbor::KeyType, cbor::Value>);
|
||||
|
||||
// This is the algorithm specifier that is supposed to be used in a COSE key
|
||||
@@ -1355,6 +1363,7 @@ mod test {
|
||||
other_ui: None,
|
||||
cred_random: None,
|
||||
cred_protect_policy: None,
|
||||
creation_order: 0,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
|
||||
@@ -200,7 +200,8 @@ impl CtapHid {
|
||||
// Each transaction is atomic, so we process the command directly here and
|
||||
// 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);
|
||||
let response =
|
||||
ctap_state.process_command(&message.payload, cid, clock_value);
|
||||
if let Some(iterator) = CtapHid::split_message(Message {
|
||||
cid,
|
||||
cmd: CtapHid::COMMAND_CBOR,
|
||||
@@ -520,7 +521,7 @@ mod 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);
|
||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||
let mut ctap_hid = CtapHid::new();
|
||||
|
||||
let mut packet = [0x00; 64];
|
||||
@@ -541,7 +542,7 @@ mod 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);
|
||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||
let mut ctap_hid = CtapHid::new();
|
||||
|
||||
let reply = process_messages(
|
||||
@@ -586,7 +587,7 @@ mod 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);
|
||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||
let mut ctap_hid = CtapHid::new();
|
||||
let cid = cid_from_init(&mut ctap_hid, &mut ctap_state);
|
||||
|
||||
@@ -646,7 +647,7 @@ mod 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);
|
||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||
let mut ctap_hid = CtapHid::new();
|
||||
let cid = cid_from_init(&mut ctap_hid, &mut ctap_state);
|
||||
|
||||
|
||||
661
src/ctap/mod.rs
661
src/ctap/mod.rs
File diff suppressed because it is too large
Load Diff
@@ -342,6 +342,26 @@ impl PersistentStore {
|
||||
.count())
|
||||
}
|
||||
|
||||
pub fn new_creation_order(&self) -> Result<u64, Ctap2StatusCode> {
|
||||
Ok(self
|
||||
.store
|
||||
.find_all(&Key::Credential {
|
||||
rp_id: None,
|
||||
credential_id: None,
|
||||
user_handle: None,
|
||||
})
|
||||
.filter_map(|(_, entry)| {
|
||||
debug_assert_eq!(entry.tag, TAG_CREDENTIAL);
|
||||
let credential = deserialize_credential(entry.data);
|
||||
debug_assert!(credential.is_some());
|
||||
credential
|
||||
})
|
||||
.map(|c| c.creation_order)
|
||||
.max()
|
||||
.unwrap_or(0)
|
||||
.wrapping_add(1))
|
||||
}
|
||||
|
||||
pub fn global_signature_counter(&self) -> Result<u32, Ctap2StatusCode> {
|
||||
Ok(self
|
||||
.store
|
||||
@@ -702,6 +722,7 @@ mod test {
|
||||
other_ui: None,
|
||||
cred_random: None,
|
||||
cred_protect_policy: None,
|
||||
creation_order: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -737,6 +758,21 @@ mod test {
|
||||
assert!(persistent_store.count_credentials().unwrap() > 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_credential_order() {
|
||||
let mut rng = ThreadRng256 {};
|
||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
||||
let credential_source = create_credential_source(&mut rng, "example.com", vec![]);
|
||||
let current_latest_creation = credential_source.creation_order;
|
||||
assert!(persistent_store.store_credential(credential_source).is_ok());
|
||||
let mut credential_source = create_credential_source(&mut rng, "example.com", vec![]);
|
||||
credential_source.creation_order = persistent_store.new_creation_order().unwrap();
|
||||
assert!(credential_source.creation_order > current_latest_creation);
|
||||
let current_latest_creation = credential_source.creation_order;
|
||||
assert!(persistent_store.store_credential(credential_source).is_ok());
|
||||
assert!(persistent_store.new_creation_order().unwrap() > current_latest_creation);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::assertions_on_constants)]
|
||||
fn test_fill_store() {
|
||||
@@ -865,6 +901,7 @@ mod test {
|
||||
cred_protect_policy: Some(
|
||||
CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList,
|
||||
),
|
||||
creation_order: 0,
|
||||
};
|
||||
assert!(persistent_store.store_credential(credential).is_ok());
|
||||
|
||||
@@ -906,6 +943,7 @@ mod test {
|
||||
other_ui: None,
|
||||
cred_random: None,
|
||||
cred_protect_policy: None,
|
||||
creation_order: 0,
|
||||
};
|
||||
assert_eq!(found_credential, Some(expected_credential));
|
||||
}
|
||||
@@ -925,6 +963,7 @@ mod test {
|
||||
other_ui: None,
|
||||
cred_random: None,
|
||||
cred_protect_policy: Some(CredentialProtectionPolicy::UserVerificationRequired),
|
||||
creation_order: 0,
|
||||
};
|
||||
assert!(persistent_store.store_credential(credential).is_ok());
|
||||
|
||||
@@ -1102,6 +1141,7 @@ mod test {
|
||||
other_ui: None,
|
||||
cred_random: None,
|
||||
cred_protect_policy: None,
|
||||
creation_order: 0,
|
||||
};
|
||||
let serialized = serialize_credential(credential.clone()).unwrap();
|
||||
let reconstructed = deserialize_credential(&serialized).unwrap();
|
||||
|
||||
11
src/main.rs
11
src/main.rs
@@ -37,9 +37,11 @@ use libtock_drivers::console::Console;
|
||||
use libtock_drivers::led;
|
||||
use libtock_drivers::result::{FlexUnwrap, TockError};
|
||||
use libtock_drivers::timer;
|
||||
use libtock_drivers::timer::Duration;
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
use libtock_drivers::timer::Timer;
|
||||
use libtock_drivers::timer::{Duration, Timestamp};
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
use libtock_drivers::timer::Timestamp;
|
||||
use libtock_drivers::usb_ctap_hid;
|
||||
|
||||
const KEEPALIVE_DELAY_MS: isize = 100;
|
||||
@@ -57,12 +59,13 @@ fn main() {
|
||||
panic!("Cannot setup USB driver");
|
||||
}
|
||||
|
||||
let boot_time = timer.get_current_clock().flex_unwrap();
|
||||
let mut rng = TockRng256 {};
|
||||
let mut ctap_state = CtapState::new(&mut rng, check_user_presence);
|
||||
let mut ctap_state = CtapState::new(&mut rng, check_user_presence, boot_time);
|
||||
let mut ctap_hid = CtapHid::new();
|
||||
|
||||
let mut led_counter = 0;
|
||||
let mut last_led_increment = timer.get_current_clock().flex_unwrap();
|
||||
let mut last_led_increment = boot_time;
|
||||
|
||||
// Main loop. If CTAP1 is used, we register button presses for U2F while receiving and waiting.
|
||||
// The way TockOS and apps currently interact, callbacks need a yield syscall to execute,
|
||||
@@ -115,7 +118,7 @@ fn main() {
|
||||
|
||||
// These calls are making sure that even for long inactivity, wrapping clock values
|
||||
// never randomly wink or grant user presence for U2F.
|
||||
ctap_state.check_disable_reset(Timestamp::<isize>::from_clock_value(now));
|
||||
ctap_state.update_command_permission(now);
|
||||
ctap_hid.wink_permission = ctap_hid.wink_permission.check_expiration(now);
|
||||
|
||||
if has_packet {
|
||||
|
||||
Reference in New Issue
Block a user