Introduce Customization struct (#458)

* Introduce Customization trait

* Introduce Customization trait including the customization accessors
  that control various behaviors.

* Expose Customization through a getter API in Env, and make the code
  that directly access the constants currently switch to accessing the
  customizations via Env.

* TockEnv's customization getter implementation directly returns the
  reference of the global DEFAULT_CUSTOMIZATION constant, so the
  constant values are still inlined and dead code won't be compiled.

* We'll add the customizations from global constants to the struct
  one-by-one, only MAX_MSG_SIZE in this commit.

* Small fixes

* Fix deploy script
* put is_valid under std gate
This commit is contained in:
hcyang
2022-04-14 14:57:18 +08:00
committed by GitHub
parent 81996f650e
commit 1ef9a4447d
12 changed files with 174 additions and 33 deletions

View File

@@ -76,7 +76,8 @@ fn initialize(ctap: &mut Ctap<TestEnv>) -> ChannelID {
for pkt_reply in for pkt_reply in
ctap.process_hid_packet(&pkt_request, Transport::MainHid, CtapInstant::new(0)) ctap.process_hid_packet(&pkt_request, Transport::MainHid, CtapInstant::new(0))
{ {
if let Ok(Some(result)) = assembler_reply.parse_packet(&pkt_reply, CtapInstant::new(0)) if let Ok(Some(result)) =
assembler_reply.parse_packet(ctap.env(), &pkt_reply, CtapInstant::new(0))
{ {
result_cid.copy_from_slice(&result.payload[8..12]); result_cid.copy_from_slice(&result.payload[8..12]);
} }
@@ -118,7 +119,7 @@ fn process_message(data: &[u8], ctap: &mut Ctap<TestEnv>) {
ctap.process_hid_packet(&pkt_request, Transport::MainHid, CtapInstant::new(0)) ctap.process_hid_packet(&pkt_request, Transport::MainHid, CtapInstant::new(0))
{ {
// Only checks for assembling crashes, not for semantics. // Only checks for assembling crashes, not for semantics.
let _ = assembler_reply.parse_packet(&pkt_reply, CtapInstant::new(0)); let _ = assembler_reply.parse_packet(ctap.env(), &pkt_reply, CtapInstant::new(0));
} }
} }
} }
@@ -202,6 +203,7 @@ pub fn process_ctap_structured(data: &[u8], input_type: InputType) -> arbitrary:
// 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.
pub fn split_assemble_hid_packets(data: &[u8]) { pub fn split_assemble_hid_packets(data: &[u8]) {
let mut env = TestEnv::new();
let message = raw_to_message(data); let message = raw_to_message(data);
if let Some(hid_packet_iterator) = HidPacketIterator::new(message.clone()) { if let Some(hid_packet_iterator) = HidPacketIterator::new(message.clone()) {
let mut assembler = MessageAssembler::new(); let mut assembler = MessageAssembler::new();
@@ -209,12 +211,12 @@ pub fn split_assemble_hid_packets(data: &[u8]) {
if let Some((last_packet, first_packets)) = packets.split_last() { if let Some((last_packet, first_packets)) = packets.split_last() {
for packet in first_packets { for packet in first_packets {
assert_eq!( assert_eq!(
assembler.parse_packet(packet, CtapInstant::new(0)), assembler.parse_packet(&mut env, packet, CtapInstant::new(0)),
Ok(None) Ok(None)
); );
} }
assert_eq!( assert_eq!(
assembler.parse_packet(last_packet, CtapInstant::new(0)), assembler.parse_packet(&mut env, last_packet, CtapInstant::new(0)),
Ok(Some(message)) Ok(Some(message))
); );
} }

62
src/api/customization.rs Normal file
View File

@@ -0,0 +1,62 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! This file contains all customizable constants.
//!
//! If you adapt them, make sure to run the tests before flashing the firmware.
//! Our deploy script enforces the invariants.
pub trait Customization {
/// Maximum message size send for CTAP commands.
///
/// The maximum value is 7609, as HID packets can not encode longer messages.
/// 1024 is the default mentioned in the authenticatorLargeBlobs commands.
/// Larger values are preferred, as that allows more parameters in commands.
/// If long commands are too unreliable on your hardware, consider decreasing
/// this value.
fn max_msg_size(&self) -> usize;
}
#[derive(Clone)]
pub struct CustomizationImpl {
pub max_msg_size: usize,
}
pub const DEFAULT_CUSTOMIZATION: CustomizationImpl = CustomizationImpl { max_msg_size: 7609 };
impl Customization for CustomizationImpl {
fn max_msg_size(&self) -> usize {
self.max_msg_size
}
}
#[cfg(feature = "std")]
pub fn is_valid(customization: &impl Customization) -> bool {
// Max message size must be between 1024 and 7609.
if customization.max_msg_size() < 1024 || customization.max_msg_size() > 7609 {
return false;
}
true
}
#[cfg(test)]
mod test {
use super::*;
#[test]
#[allow(clippy::assertions_on_constants)]
fn test_invariants() {
assert!(is_valid(&DEFAULT_CUSTOMIZATION));
}
}

View File

@@ -3,5 +3,6 @@
//! The [environment](crate::env::Env) is split into components. Each component has an API described //! The [environment](crate::env::Env) is split into components. Each component has an API described
//! by a trait. This module gathers the API of those components. //! by a trait. This module gathers the API of those components.
pub mod customization;
pub mod firmware_protection; pub mod firmware_protection;
pub mod upgrade_storage; pub mod upgrade_storage;

View File

@@ -119,15 +119,6 @@ pub const ENTERPRISE_ATTESTATION_MODE: Option<EnterpriseAttestationMode> = None;
/// VendorFacilitated. /// VendorFacilitated.
pub const ENTERPRISE_RP_ID_LIST: &[&str] = &[]; pub const ENTERPRISE_RP_ID_LIST: &[&str] = &[];
/// Maximum message size send for CTAP commands.
///
/// The maximum value is 7609, as HID packets can not encode longer messages.
/// 1024 is the default mentioned in the authenticatorLargeBlobs commands.
/// Larger values are preferred, as that allows more parameters in commands.
/// If long commands are too unreliable on your hardware, consider decreasing
/// this value.
pub const MAX_MSG_SIZE: usize = 7609;
/// Sets the number of consecutive failed PINs before blocking interaction. /// Sets the number of consecutive failed PINs before blocking interaction.
/// ///
/// # Invariant /// # Invariant
@@ -252,8 +243,6 @@ mod test {
} else { } else {
assert!(ENTERPRISE_RP_ID_LIST.is_empty()); assert!(ENTERPRISE_RP_ID_LIST.is_empty());
} }
assert!(MAX_MSG_SIZE >= 1024);
assert!(MAX_MSG_SIZE <= 7609);
assert!(MAX_PIN_RETRIES <= 8); assert!(MAX_PIN_RETRIES <= 8);
assert!(MAX_CRED_BLOB_LENGTH >= 32); assert!(MAX_CRED_BLOB_LENGTH >= 32);
if let Some(count) = MAX_CREDENTIAL_COUNT_IN_LIST { if let Some(count) = MAX_CREDENTIAL_COUNT_IN_LIST {

View File

@@ -235,7 +235,7 @@ impl CtapHid {
packet: &HidPacket, packet: &HidPacket,
clock_value: CtapInstant, clock_value: CtapInstant,
) -> Option<Message> { ) -> Option<Message> {
match self.assembler.parse_packet(packet, clock_value) { match self.assembler.parse_packet(env, packet, clock_value) {
Ok(Some(message)) => { Ok(Some(message)) => {
debug_ctap!(env, "Received message: {:02x?}", message); debug_ctap!(env, "Received message: {:02x?}", message);
self.preprocess_message(message) self.preprocess_message(message)
@@ -420,6 +420,7 @@ mod test {
#[test] #[test]
fn test_split_assemble() { fn test_split_assemble() {
let mut env = TestEnv::new();
for payload_len in 0..7609 { for payload_len in 0..7609 {
let message = Message { let message = Message {
cid: [0x12, 0x34, 0x56, 0x78], cid: [0x12, 0x34, 0x56, 0x78],
@@ -430,7 +431,7 @@ mod test {
let mut messages = Vec::new(); let mut messages = Vec::new();
let mut assembler = MessageAssembler::new(); let mut assembler = MessageAssembler::new();
for packet in HidPacketIterator::new(message.clone()).unwrap() { for packet in HidPacketIterator::new(message.clone()).unwrap() {
match assembler.parse_packet(&packet, CtapInstant::new(0)) { match assembler.parse_packet(&mut env, &packet, CtapInstant::new(0)) {
Ok(Some(msg)) => messages.push(msg), Ok(Some(msg)) => messages.push(msg),
Ok(None) => (), Ok(None) => (),
Err(_) => panic!("Couldn't assemble packet: {:02x?}", &packet as &[u8]), Err(_) => panic!("Couldn't assemble packet: {:02x?}", &packet as &[u8]),

View File

@@ -12,12 +12,12 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use crate::clock::CtapInstant;
use super::super::customization::MAX_MSG_SIZE;
use super::{ use super::{
ChannelID, CtapHid, CtapHidCommand, CtapHidError, HidPacket, Message, ProcessedPacket, ChannelID, CtapHid, CtapHidCommand, CtapHidError, HidPacket, Message, ProcessedPacket,
}; };
use crate::api::customization::Customization;
use crate::clock::CtapInstant;
use crate::env::Env;
use alloc::vec::Vec; use alloc::vec::Vec;
use core::mem::swap; use core::mem::swap;
@@ -73,6 +73,7 @@ impl MessageAssembler {
// packet was received. // packet was received.
pub fn parse_packet( pub fn parse_packet(
&mut self, &mut self,
env: &mut impl Env,
packet: &HidPacket, packet: &HidPacket,
timestamp: CtapInstant, timestamp: CtapInstant,
) -> Result<Option<Message>, (ChannelID, CtapHidError)> { ) -> Result<Option<Message>, (ChannelID, CtapHidError)> {
@@ -97,7 +98,7 @@ impl MessageAssembler {
// Expecting an initialization packet. // Expecting an initialization packet.
match processed_packet { match processed_packet {
ProcessedPacket::InitPacket { cmd, len, data } => { ProcessedPacket::InitPacket { cmd, len, data } => {
self.parse_init_packet(*cid, cmd, len, data, timestamp) self.parse_init_packet(env, *cid, cmd, len, data, timestamp)
} }
ProcessedPacket::ContinuationPacket { .. } => { ProcessedPacket::ContinuationPacket { .. } => {
// CTAP specification (version 20190130) section 8.1.5.4 // CTAP specification (version 20190130) section 8.1.5.4
@@ -119,7 +120,7 @@ impl MessageAssembler {
ProcessedPacket::InitPacket { cmd, len, data } => { ProcessedPacket::InitPacket { cmd, len, data } => {
self.reset(); self.reset();
if cmd == CtapHidCommand::Init as u8 { if cmd == CtapHidCommand::Init as u8 {
self.parse_init_packet(*cid, cmd, len, data, timestamp) self.parse_init_packet(env, *cid, cmd, len, data, timestamp)
} else { } else {
Err((*cid, CtapHidError::InvalidSeq)) Err((*cid, CtapHidError::InvalidSeq))
} }
@@ -143,6 +144,7 @@ impl MessageAssembler {
fn parse_init_packet( fn parse_init_packet(
&mut self, &mut self,
env: &mut impl Env,
cid: ChannelID, cid: ChannelID,
cmd: u8, cmd: u8,
len: usize, len: usize,
@@ -151,7 +153,7 @@ impl MessageAssembler {
) -> Result<Option<Message>, (ChannelID, CtapHidError)> { ) -> Result<Option<Message>, (ChannelID, CtapHidError)> {
// Reject invalid lengths early to reduce the risk of running out of memory. // Reject invalid lengths early to reduce the risk of running out of memory.
// TODO: also reject invalid commands early? // TODO: also reject invalid commands early?
if len > MAX_MSG_SIZE { if len > env.customization().max_msg_size() {
return Err((cid, CtapHidError::InvalidLen)); return Err((cid, CtapHidError::InvalidLen));
} }
self.cid = cid; self.cid = cid;
@@ -186,6 +188,7 @@ impl MessageAssembler {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::ctap::hid::CtapHid; use crate::ctap::hid::CtapHid;
use crate::env::test::TestEnv;
use embedded_time::duration::Milliseconds; use embedded_time::duration::Milliseconds;
use super::*; use super::*;
@@ -207,11 +210,13 @@ mod test {
#[test] #[test]
fn test_empty_payload() { fn test_empty_payload() {
let mut env = TestEnv::new();
let mut assembler = MessageAssembler::new(); let mut assembler = MessageAssembler::new();
// Except for tests that exercise timeouts, all packets are synchronized at the same dummy // Except for tests that exercise timeouts, all packets are synchronized at the same dummy
// timestamp. // timestamp.
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x90]), &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x90]),
CtapInstant::new(0) CtapInstant::new(0)
), ),
@@ -225,9 +230,11 @@ mod test {
#[test] #[test]
fn test_one_packet() { fn test_one_packet() {
let mut env = TestEnv::new();
let mut assembler = MessageAssembler::new(); let mut assembler = MessageAssembler::new();
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x90, 0x00, 0x10]), &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x90, 0x00, 0x10]),
CtapInstant::new(0) CtapInstant::new(0)
), ),
@@ -241,12 +248,14 @@ mod test {
#[test] #[test]
fn test_nonzero_padding() { fn test_nonzero_padding() {
let mut env = TestEnv::new();
// CTAP specification (version 20190130) section 8.1.4 // CTAP specification (version 20190130) section 8.1.4
// It is written that "Unused bytes SHOULD be set to zero", so we test that non-zero // It is written that "Unused bytes SHOULD be set to zero", so we test that non-zero
// padding is accepted as well. // padding is accepted as well.
let mut assembler = MessageAssembler::new(); let mut assembler = MessageAssembler::new();
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x90, 0x00, 0x10], 0xFF), &byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x90, 0x00, 0x10], 0xFF),
CtapInstant::new(0) CtapInstant::new(0)
), ),
@@ -260,9 +269,11 @@ mod test {
#[test] #[test]
fn test_two_packets() { fn test_two_packets() {
let mut env = TestEnv::new();
let mut assembler = MessageAssembler::new(); let mut assembler = MessageAssembler::new();
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]), &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]),
CtapInstant::new(0) CtapInstant::new(0)
), ),
@@ -270,6 +281,7 @@ mod test {
); );
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]), &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]),
CtapInstant::new(0) CtapInstant::new(0)
), ),
@@ -283,9 +295,11 @@ mod test {
#[test] #[test]
fn test_three_packets() { fn test_three_packets() {
let mut env = TestEnv::new();
let mut assembler = MessageAssembler::new(); let mut assembler = MessageAssembler::new();
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x80]), &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x80]),
CtapInstant::new(0) CtapInstant::new(0)
), ),
@@ -293,6 +307,7 @@ mod test {
); );
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]), &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]),
CtapInstant::new(0) CtapInstant::new(0)
), ),
@@ -300,6 +315,7 @@ mod test {
); );
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x01]), &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x01]),
CtapInstant::new(0) CtapInstant::new(0)
), ),
@@ -313,9 +329,11 @@ mod test {
#[test] #[test]
fn test_max_packets() { fn test_max_packets() {
let mut env = TestEnv::new();
let mut assembler = MessageAssembler::new(); let mut assembler = MessageAssembler::new();
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x1D, 0xB9]), &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x1D, 0xB9]),
CtapInstant::new(0) CtapInstant::new(0)
), ),
@@ -324,6 +342,7 @@ mod test {
for seq in 0..0x7F { for seq in 0..0x7F {
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&zero_extend(&[0x12, 0x34, 0x56, 0x78, seq]), &zero_extend(&[0x12, 0x34, 0x56, 0x78, seq]),
CtapInstant::new(0) CtapInstant::new(0)
), ),
@@ -332,6 +351,7 @@ mod test {
} }
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x7F]), &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x7F]),
CtapInstant::new(0) CtapInstant::new(0)
), ),
@@ -345,6 +365,7 @@ mod test {
#[test] #[test]
fn test_multiple_messages() { fn test_multiple_messages() {
let mut env = TestEnv::new();
// Check that after yielding a message, the assembler is ready to process new messages. // Check that after yielding a message, the assembler is ready to process new messages.
let mut assembler = MessageAssembler::new(); let mut assembler = MessageAssembler::new();
for i in 0..10 { for i in 0..10 {
@@ -354,6 +375,7 @@ mod test {
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&byte_extend( &byte_extend(
&[0x12, 0x34, 0x56, 0x78, 0x80 | cmd as u8, 0x00, 0x80], &[0x12, 0x34, 0x56, 0x78, 0x80 | cmd as u8, 0x00, 0x80],
byte byte
@@ -364,6 +386,7 @@ mod test {
); );
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x00], byte), &byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x00], byte),
CtapInstant::new(0) CtapInstant::new(0)
), ),
@@ -371,6 +394,7 @@ mod test {
); );
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x01], byte), &byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x01], byte),
CtapInstant::new(0) CtapInstant::new(0)
), ),
@@ -385,6 +409,7 @@ mod test {
#[test] #[test]
fn test_channel_switch() { fn test_channel_switch() {
let mut env = TestEnv::new();
// Check that the assembler can process messages from multiple channels, sequentially. // Check that the assembler can process messages from multiple channels, sequentially.
let mut assembler = MessageAssembler::new(); let mut assembler = MessageAssembler::new();
for i in 0..10 { for i in 0..10 {
@@ -395,6 +420,7 @@ mod test {
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&byte_extend(&[0x12, 0x34, 0x56, cid, 0x80 | cmd as u8, 0x00, 0x80], byte), &byte_extend(&[0x12, 0x34, 0x56, cid, 0x80 | cmd as u8, 0x00, 0x80], byte),
CtapInstant::new(0) CtapInstant::new(0)
), ),
@@ -402,6 +428,7 @@ mod test {
); );
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&byte_extend(&[0x12, 0x34, 0x56, cid, 0x00], byte), &byte_extend(&[0x12, 0x34, 0x56, cid, 0x00], byte),
CtapInstant::new(0) CtapInstant::new(0)
), ),
@@ -409,6 +436,7 @@ mod test {
); );
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&byte_extend(&[0x12, 0x34, 0x56, cid, 0x01], byte), &byte_extend(&[0x12, 0x34, 0x56, cid, 0x01], byte),
CtapInstant::new(0) CtapInstant::new(0)
), ),
@@ -423,9 +451,11 @@ mod test {
#[test] #[test]
fn test_unexpected_channel() { fn test_unexpected_channel() {
let mut env = TestEnv::new();
let mut assembler = MessageAssembler::new(); let mut assembler = MessageAssembler::new();
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]), &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]),
CtapInstant::new(0) CtapInstant::new(0)
), ),
@@ -438,6 +468,7 @@ mod test {
for byte in 0..=0xFF { for byte in 0..=0xFF {
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&byte_extend(&[0x12, 0x34, 0x56, 0x9A, cmd as u8, 0x00], byte), &byte_extend(&[0x12, 0x34, 0x56, 0x9A, cmd as u8, 0x00], byte),
CtapInstant::new(0) CtapInstant::new(0)
), ),
@@ -448,6 +479,7 @@ mod test {
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]), &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]),
CtapInstant::new(0) CtapInstant::new(0)
), ),
@@ -461,6 +493,7 @@ mod test {
#[test] #[test]
fn test_spurious_continuation_packets() { fn test_spurious_continuation_packets() {
let mut env = TestEnv::new();
// CTAP specification (version 20190130) section 8.1.5.4 // CTAP specification (version 20190130) section 8.1.5.4
// Spurious continuation packets appearing without a prior initialization packet will be // Spurious continuation packets appearing without a prior initialization packet will be
// ignored. // ignored.
@@ -470,6 +503,7 @@ mod test {
let byte = 2 * i; let byte = 2 * i;
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x10], byte), &byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x10], byte),
CtapInstant::new(0) CtapInstant::new(0)
), ),
@@ -484,6 +518,7 @@ mod test {
let seq = i; let seq = i;
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&zero_extend(&[0x12, 0x34, 0x56, 0x78, seq]), &zero_extend(&[0x12, 0x34, 0x56, 0x78, seq]),
CtapInstant::new(0) CtapInstant::new(0)
), ),
@@ -497,9 +532,11 @@ mod test {
#[test] #[test]
fn test_unexpected_init() { fn test_unexpected_init() {
let mut env = TestEnv::new();
let mut assembler = MessageAssembler::new(); let mut assembler = MessageAssembler::new();
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]), &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]),
CtapInstant::new(0) CtapInstant::new(0)
), ),
@@ -507,6 +544,7 @@ mod test {
); );
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x80]), &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x80]),
CtapInstant::new(0) CtapInstant::new(0)
), ),
@@ -516,9 +554,11 @@ mod test {
#[test] #[test]
fn test_unexpected_seq() { fn test_unexpected_seq() {
let mut env = TestEnv::new();
let mut assembler = MessageAssembler::new(); let mut assembler = MessageAssembler::new();
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]), &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]),
CtapInstant::new(0) CtapInstant::new(0)
), ),
@@ -526,6 +566,7 @@ mod test {
); );
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x01]), &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x01]),
CtapInstant::new(0) CtapInstant::new(0)
), ),
@@ -535,9 +576,11 @@ mod test {
#[test] #[test]
fn test_timed_out_packet() { fn test_timed_out_packet() {
let mut env = TestEnv::new();
let mut assembler = MessageAssembler::new(); let mut assembler = MessageAssembler::new();
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]), &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]),
CtapInstant::new(0) CtapInstant::new(0)
), ),
@@ -545,6 +588,7 @@ mod test {
); );
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]), &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]),
CtapInstant::new(0) + CtapHid::TIMEOUT_DURATION CtapInstant::new(0) + CtapHid::TIMEOUT_DURATION
), ),
@@ -554,6 +598,7 @@ mod test {
#[test] #[test]
fn test_just_in_time_packets() { fn test_just_in_time_packets() {
let mut env = TestEnv::new();
let mut timestamp: CtapInstant = CtapInstant::new(0); let mut timestamp: CtapInstant = CtapInstant::new(0);
// Delay between each packet is just below the threshold. // Delay between each packet is just below the threshold.
let delay = CtapHid::TIMEOUT_DURATION - Milliseconds(1_u32); let delay = CtapHid::TIMEOUT_DURATION - Milliseconds(1_u32);
@@ -561,6 +606,7 @@ mod test {
let mut assembler = MessageAssembler::new(); let mut assembler = MessageAssembler::new();
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x1D, 0xB9]), &zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x1D, 0xB9]),
timestamp timestamp
), ),
@@ -569,13 +615,21 @@ mod test {
for seq in 0..0x7F { for seq in 0..0x7F {
timestamp = timestamp + delay; timestamp = timestamp + delay;
assert_eq!( assert_eq!(
assembler.parse_packet(&zero_extend(&[0x12, 0x34, 0x56, 0x78, seq]), timestamp), assembler.parse_packet(
&mut env,
&zero_extend(&[0x12, 0x34, 0x56, 0x78, seq]),
timestamp
),
Ok(None) Ok(None)
); );
} }
timestamp = timestamp + delay; timestamp = timestamp + delay;
assert_eq!( assert_eq!(
assembler.parse_packet(&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x7F]), timestamp), assembler.parse_packet(
&mut env,
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x7F]),
timestamp
),
Ok(Some(Message { Ok(Some(Message {
cid: [0x12, 0x34, 0x56, 0x78], cid: [0x12, 0x34, 0x56, 0x78],
cmd: CtapHidCommand::Ping, cmd: CtapHidCommand::Ping,
@@ -586,10 +640,12 @@ mod test {
#[test] #[test]
fn test_init_sync() { fn test_init_sync() {
let mut env = TestEnv::new();
let mut assembler = MessageAssembler::new(); let mut assembler = MessageAssembler::new();
// Ping packet with a length longer than one packet. // Ping packet with a length longer than one packet.
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x02, 0x00], 0x51), &byte_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x02, 0x00], 0x51),
CtapInstant::new(0) CtapInstant::new(0)
), ),
@@ -598,6 +654,7 @@ mod test {
// Init packet on the same channel. // Init packet on the same channel.
assert_eq!( assert_eq!(
assembler.parse_packet( assembler.parse_packet(
&mut env,
&zero_extend(&[ &zero_extend(&[
0x12, 0x34, 0x56, 0x78, 0x86, 0x00, 0x08, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0x12, 0x34, 0x56, 0x78, 0x86, 0x00, 0x08, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC,
0xDE, 0xF0 0xDE, 0xF0

View File

@@ -14,9 +14,9 @@
use super::client_pin::{ClientPin, PinPermission}; use super::client_pin::{ClientPin, PinPermission};
use super::command::AuthenticatorLargeBlobsParameters; use super::command::AuthenticatorLargeBlobsParameters;
use super::customization::MAX_MSG_SIZE;
use super::response::{AuthenticatorLargeBlobsResponse, ResponseData}; use super::response::{AuthenticatorLargeBlobsResponse, ResponseData};
use super::status_code::Ctap2StatusCode; use super::status_code::Ctap2StatusCode;
use crate::api::customization::Customization;
use crate::ctap::storage; use crate::ctap::storage;
use crate::env::Env; use crate::env::Env;
use alloc::vec; use alloc::vec;
@@ -60,10 +60,10 @@ impl LargeBlobs {
pin_uv_auth_protocol, pin_uv_auth_protocol,
} = large_blobs_params; } = large_blobs_params;
const MAX_FRAGMENT_LENGTH: usize = MAX_MSG_SIZE - 64; let max_fragment_size = env.customization().max_msg_size() - 64;
if let Some(get) = get { if let Some(get) = get {
if get > MAX_FRAGMENT_LENGTH || offset.checked_add(get).is_none() { if get > max_fragment_size || offset.checked_add(get).is_none() {
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_LENGTH); return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_LENGTH);
} }
let config = storage::get_large_blob_array(env, offset, get)?; let config = storage::get_large_blob_array(env, offset, get)?;
@@ -73,7 +73,7 @@ impl LargeBlobs {
} }
if let Some(mut set) = set { if let Some(mut set) = set {
if set.len() > MAX_FRAGMENT_LENGTH { if set.len() > max_fragment_size {
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_LENGTH); return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_LENGTH);
} }
if offset == 0 { if offset == 0 {

View File

@@ -20,7 +20,7 @@ mod credential_management;
mod crypto_wrapper; mod crypto_wrapper;
#[cfg(feature = "with_ctap1")] #[cfg(feature = "with_ctap1")]
mod ctap1; mod ctap1;
mod customization; pub mod customization;
pub mod data_formats; pub mod data_formats;
pub mod hid; pub mod hid;
mod key_material; mod key_material;
@@ -45,7 +45,7 @@ use self::credential_management::process_credential_management;
use self::crypto_wrapper::{aes256_cbc_decrypt, aes256_cbc_encrypt}; use self::crypto_wrapper::{aes256_cbc_decrypt, aes256_cbc_encrypt};
use self::customization::{ use self::customization::{
DEFAULT_CRED_PROTECT, ENTERPRISE_ATTESTATION_MODE, ENTERPRISE_RP_ID_LIST, DEFAULT_CRED_PROTECT, ENTERPRISE_ATTESTATION_MODE, ENTERPRISE_RP_ID_LIST,
MAX_CREDENTIAL_COUNT_IN_LIST, MAX_CRED_BLOB_LENGTH, MAX_LARGE_BLOB_ARRAY_SIZE, MAX_MSG_SIZE, MAX_CREDENTIAL_COUNT_IN_LIST, MAX_CRED_BLOB_LENGTH, MAX_LARGE_BLOB_ARRAY_SIZE,
MAX_RP_IDS_LENGTH, USE_BATCH_ATTESTATION, USE_SIGNATURE_COUNTER, MAX_RP_IDS_LENGTH, USE_BATCH_ATTESTATION, USE_SIGNATURE_COUNTER,
}; };
use self::data_formats::{ use self::data_formats::{
@@ -66,6 +66,7 @@ use self::status_code::Ctap2StatusCode;
use self::timed_permission::TimedPermission; use self::timed_permission::TimedPermission;
#[cfg(feature = "with_ctap1")] #[cfg(feature = "with_ctap1")]
use self::timed_permission::U2fUserPresenceState; use self::timed_permission::U2fUserPresenceState;
use crate::api::customization::Customization;
use crate::api::firmware_protection::FirmwareProtection; use crate::api::firmware_protection::FirmwareProtection;
use crate::api::upgrade_storage::UpgradeStorage; use crate::api::upgrade_storage::UpgradeStorage;
use crate::clock::{ClockInt, CtapInstant}; use crate::clock::{ClockInt, CtapInstant};
@@ -1207,7 +1208,7 @@ impl CtapState {
]), ]),
aaguid: storage::aaguid(env)?, aaguid: storage::aaguid(env)?,
options: Some(options), options: Some(options),
max_msg_size: Some(MAX_MSG_SIZE as u64), max_msg_size: Some(env.customization().max_msg_size() as u64),
// The order implies preference. We favor the new V2. // The order implies preference. We favor the new V2.
pin_protocols: Some(vec![ pin_protocols: Some(vec![
PinUvAuthProtocol::V2 as u64, PinUvAuthProtocol::V2 as u64,
@@ -1519,7 +1520,7 @@ mod test {
"setMinPINLength" => true, "setMinPINLength" => true,
"makeCredUvNotRqd" => true, "makeCredUvNotRqd" => true,
}, },
0x05 => MAX_MSG_SIZE as u64, 0x05 => env.customization().max_msg_size() as u64,
0x06 => cbor_array![2, 1], 0x06 => cbor_array![2, 1],
0x07 => MAX_CREDENTIAL_COUNT_IN_LIST.map(|c| c as u64), 0x07 => MAX_CREDENTIAL_COUNT_IN_LIST.map(|c| c as u64),
0x08 => CREDENTIAL_ID_SIZE as u64, 0x08 => CREDENTIAL_ID_SIZE as u64,

4
src/env/mod.rs vendored
View File

@@ -1,3 +1,4 @@
use crate::api::customization::Customization;
use crate::api::firmware_protection::FirmwareProtection; use crate::api::firmware_protection::FirmwareProtection;
use crate::api::upgrade_storage::UpgradeStorage; use crate::api::upgrade_storage::UpgradeStorage;
use crate::ctap::status_code::Ctap2StatusCode; use crate::ctap::status_code::Ctap2StatusCode;
@@ -24,6 +25,7 @@ pub trait Env {
type UpgradeStorage: UpgradeStorage; type UpgradeStorage: UpgradeStorage;
type FirmwareProtection: FirmwareProtection; type FirmwareProtection: FirmwareProtection;
type Write: core::fmt::Write; type Write: core::fmt::Write;
type Customization: Customization;
fn rng(&mut self) -> &mut Self::Rng; fn rng(&mut self) -> &mut Self::Rng;
fn user_presence(&mut self) -> &mut Self::UserPresence; fn user_presence(&mut self) -> &mut Self::UserPresence;
@@ -44,4 +46,6 @@ pub trait Env {
/// using the defmt crate) and ignore this API. Non-embedded environments may either use this /// using the defmt crate) and ignore this API. Non-embedded environments may either use this
/// API or use the log feature (to be implemented using the log crate). /// API or use the log feature (to be implemented using the log crate).
fn write(&mut self) -> Self::Write; fn write(&mut self) -> Self::Write;
fn customization(&self) -> &Self::Customization;
} }

13
src/env/test/mod.rs vendored
View File

@@ -1,4 +1,5 @@
use self::upgrade_storage::BufferUpgradeStorage; use self::upgrade_storage::BufferUpgradeStorage;
use crate::api::customization::{CustomizationImpl, DEFAULT_CUSTOMIZATION};
use crate::api::firmware_protection::FirmwareProtection; use crate::api::firmware_protection::FirmwareProtection;
use crate::ctap::status_code::Ctap2StatusCode; use crate::ctap::status_code::Ctap2StatusCode;
use crate::ctap::Channel; use crate::ctap::Channel;
@@ -13,6 +14,7 @@ pub struct TestEnv {
user_presence: TestUserPresence, user_presence: TestUserPresence,
store: Store<BufferStorage>, store: Store<BufferStorage>,
upgrade_storage: Option<BufferUpgradeStorage>, upgrade_storage: Option<BufferUpgradeStorage>,
customization: CustomizationImpl,
} }
pub struct TestUserPresence { pub struct TestUserPresence {
@@ -51,17 +53,23 @@ impl TestEnv {
let storage = new_storage(); let storage = new_storage();
let store = Store::new(storage).ok().unwrap(); let store = Store::new(storage).ok().unwrap();
let upgrade_storage = Some(BufferUpgradeStorage::new().unwrap()); let upgrade_storage = Some(BufferUpgradeStorage::new().unwrap());
let customization = DEFAULT_CUSTOMIZATION.clone();
TestEnv { TestEnv {
rng, rng,
user_presence, user_presence,
store, store,
upgrade_storage, upgrade_storage,
customization,
} }
} }
pub fn disable_upgrade_storage(&mut self) { pub fn disable_upgrade_storage(&mut self) {
self.upgrade_storage = None; self.upgrade_storage = None;
} }
pub fn customization_mut(&mut self) -> &mut CustomizationImpl {
&mut self.customization
}
} }
impl TestUserPresence { impl TestUserPresence {
@@ -89,6 +97,7 @@ impl Env for TestEnv {
type UpgradeStorage = BufferUpgradeStorage; type UpgradeStorage = BufferUpgradeStorage;
type FirmwareProtection = Self; type FirmwareProtection = Self;
type Write = TestWrite; type Write = TestWrite;
type Customization = CustomizationImpl;
fn rng(&mut self) -> &mut Self::Rng { fn rng(&mut self) -> &mut Self::Rng {
&mut self.rng &mut self.rng
@@ -113,4 +122,8 @@ impl Env for TestEnv {
fn write(&mut self) -> Self::Write { fn write(&mut self) -> Self::Write {
TestWrite TestWrite
} }
fn customization(&self) -> &Self::Customization {
&self.customization
}
} }

6
src/env/tock/mod.rs vendored
View File

@@ -1,4 +1,5 @@
pub use self::storage::{TockStorage, TockUpgradeStorage}; pub use self::storage::{TockStorage, TockUpgradeStorage};
use crate::api::customization::{CustomizationImpl, DEFAULT_CUSTOMIZATION};
use crate::api::firmware_protection::FirmwareProtection; use crate::api::firmware_protection::FirmwareProtection;
use crate::ctap::hid::{CtapHid, CtapHidCommand, KeepaliveStatus, ProcessedPacket}; use crate::ctap::hid::{CtapHid, CtapHidCommand, KeepaliveStatus, ProcessedPacket};
use crate::ctap::status_code::Ctap2StatusCode; use crate::ctap::status_code::Ctap2StatusCode;
@@ -80,6 +81,7 @@ impl Env for TockEnv {
type UpgradeStorage = TockUpgradeStorage; type UpgradeStorage = TockUpgradeStorage;
type FirmwareProtection = Self; type FirmwareProtection = Self;
type Write = Console; type Write = Console;
type Customization = CustomizationImpl;
fn rng(&mut self) -> &mut Self::Rng { fn rng(&mut self) -> &mut Self::Rng {
&mut self.rng &mut self.rng
@@ -104,6 +106,10 @@ impl Env for TockEnv {
fn write(&mut self) -> Self::Write { fn write(&mut self) -> Self::Write {
Console::new() Console::new()
} }
fn customization(&self) -> &Self::Customization {
&DEFAULT_CUSTOMIZATION
}
} }
// Returns whether the keepalive was sent, or false if cancelled. // Returns whether the keepalive was sent, or false if cancelled.

View File

@@ -88,6 +88,11 @@ impl<E: Env> Ctap<E> {
&mut self.hid &mut self.hid
} }
#[cfg(feature = "std")]
pub fn env(&mut self) -> &mut E {
&mut self.env
}
pub fn process_hid_packet( pub fn process_hid_packet(
&mut self, &mut self,
packet: &HidPacket, packet: &HidPacket,