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

@@ -119,15 +119,6 @@ pub const ENTERPRISE_ATTESTATION_MODE: Option<EnterpriseAttestationMode> = None;
/// VendorFacilitated.
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.
///
/// # Invariant
@@ -252,8 +243,6 @@ mod test {
} else {
assert!(ENTERPRISE_RP_ID_LIST.is_empty());
}
assert!(MAX_MSG_SIZE >= 1024);
assert!(MAX_MSG_SIZE <= 7609);
assert!(MAX_PIN_RETRIES <= 8);
assert!(MAX_CRED_BLOB_LENGTH >= 32);
if let Some(count) = MAX_CREDENTIAL_COUNT_IN_LIST {

View File

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

View File

@@ -14,9 +14,9 @@
use super::client_pin::{ClientPin, PinPermission};
use super::command::AuthenticatorLargeBlobsParameters;
use super::customization::MAX_MSG_SIZE;
use super::response::{AuthenticatorLargeBlobsResponse, ResponseData};
use super::status_code::Ctap2StatusCode;
use crate::api::customization::Customization;
use crate::ctap::storage;
use crate::env::Env;
use alloc::vec;
@@ -60,10 +60,10 @@ impl LargeBlobs {
pin_uv_auth_protocol,
} = 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 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);
}
let config = storage::get_large_blob_array(env, offset, get)?;
@@ -73,7 +73,7 @@ impl LargeBlobs {
}
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);
}
if offset == 0 {

View File

@@ -20,7 +20,7 @@ mod credential_management;
mod crypto_wrapper;
#[cfg(feature = "with_ctap1")]
mod ctap1;
mod customization;
pub mod customization;
pub mod data_formats;
pub mod hid;
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::customization::{
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,
};
use self::data_formats::{
@@ -66,6 +66,7 @@ use self::status_code::Ctap2StatusCode;
use self::timed_permission::TimedPermission;
#[cfg(feature = "with_ctap1")]
use self::timed_permission::U2fUserPresenceState;
use crate::api::customization::Customization;
use crate::api::firmware_protection::FirmwareProtection;
use crate::api::upgrade_storage::UpgradeStorage;
use crate::clock::{ClockInt, CtapInstant};
@@ -1207,7 +1208,7 @@ impl CtapState {
]),
aaguid: storage::aaguid(env)?,
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.
pin_protocols: Some(vec![
PinUvAuthProtocol::V2 as u64,
@@ -1519,7 +1520,7 @@ mod test {
"setMinPINLength" => true,
"makeCredUvNotRqd" => true,
},
0x05 => MAX_MSG_SIZE as u64,
0x05 => env.customization().max_msg_size() as u64,
0x06 => cbor_array![2, 1],
0x07 => MAX_CREDENTIAL_COUNT_IN_LIST.map(|c| c as u64),
0x08 => CREDENTIAL_ID_SIZE as u64,