diff --git a/src/ctap/customization.rs b/src/ctap/customization.rs index 1aebadf..7c28a2f 100644 --- a/src/ctap/customization.rs +++ b/src/ctap/customization.rs @@ -119,6 +119,15 @@ pub const ENTERPRISE_ATTESTATION_MODE: Option = 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 @@ -256,6 +265,8 @@ 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 { diff --git a/src/ctap/hid/mod.rs b/src/ctap/hid/mod.rs index 03cb637..c6fc418 100644 --- a/src/ctap/hid/mod.rs +++ b/src/ctap/hid/mod.rs @@ -322,6 +322,9 @@ impl CtapHid { receive::Error::UnexpectedSeq => { CtapHid::error_message(cid, CtapHid::ERR_INVALID_SEQ) } + receive::Error::UnexpectedLen => { + CtapHid::error_message(cid, CtapHid::ERR_INVALID_LEN) + } receive::Error::Timeout => { CtapHid::error_message(cid, CtapHid::ERR_MSG_TIMEOUT) } diff --git a/src/ctap/hid/receive.rs b/src/ctap/hid/receive.rs index 8efdb1c..caf9ffc 100644 --- a/src/ctap/hid/receive.rs +++ b/src/ctap/hid/receive.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use super::super::customization::MAX_MSG_SIZE; use super::{ChannelID, CtapHid, HidPacket, Message, ProcessedPacket}; use alloc::vec::Vec; use core::mem::swap; @@ -45,6 +46,8 @@ pub enum Error { UnexpectedContinuation, // Expected a continuation packet with a specific sequence number, got another sequence number. UnexpectedSeq, + // The length of a message is too big. + UnexpectedLen, // This packet arrived after a timeout. Timeout, } @@ -107,7 +110,7 @@ impl MessageAssembler { // Expecting an initialization packet. match processed_packet { ProcessedPacket::InitPacket { cmd, len, data } => { - Ok(self.accept_init_packet(*cid, cmd, len, data, timestamp)) + self.parse_init_packet(*cid, cmd, len, data, timestamp) } ProcessedPacket::ContinuationPacket { .. } => { // CTAP specification (version 20190130) section 8.1.5.4 @@ -129,7 +132,7 @@ impl MessageAssembler { ProcessedPacket::InitPacket { cmd, len, data } => { self.reset(); if cmd == CtapHid::COMMAND_INIT { - Ok(self.accept_init_packet(*cid, cmd, len, data, timestamp)) + self.parse_init_packet(*cid, cmd, len, data, timestamp) } else { Err((*cid, Error::UnexpectedInit)) } @@ -151,24 +154,25 @@ impl MessageAssembler { } } - fn accept_init_packet( + fn parse_init_packet( &mut self, cid: ChannelID, cmd: u8, len: usize, data: &[u8], timestamp: Timestamp, - ) -> Option { - // TODO: Should invalid commands/payload lengths be rejected early, i.e. as soon as the - // initialization packet is received, or should we build a message and then catch the - // error? - // The specification (version 20190130) isn't clear on this point. + ) -> Result, (ChannelID, Error)> { + // Reject invalid lengths early to reduce the risk of running out of memory. + // TODO: also reject invalid commands early? + if len > MAX_MSG_SIZE { + return Err((cid, Error::UnexpectedLen)); + } self.cid = cid; self.last_timestamp = timestamp; self.cmd = cmd; self.seq = 0; self.remaining_payload_len = len; - self.append_payload(data) + Ok(self.append_payload(data)) } fn append_payload(&mut self, data: &[u8]) -> Option { diff --git a/src/ctap/large_blobs.rs b/src/ctap/large_blobs.rs index ffb98cb..846bc33 100644 --- a/src/ctap/large_blobs.rs +++ b/src/ctap/large_blobs.rs @@ -14,6 +14,7 @@ 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 super::storage::PersistentStore; @@ -23,10 +24,6 @@ use byteorder::{ByteOrder, LittleEndian}; use crypto::sha256::Sha256; use crypto::Hash256; -/// This is maximum message size supported by the authenticator. 1024 is the default. -/// Increasing this values can speed up commands with longer responses, but lead to -/// packets dropping or unexpected failures. -pub const MAX_MSG_SIZE: usize = 1024; /// The length of the truncated hash that as appended to the large blob data. const TRUNCATED_HASH_LEN: usize = 16; diff --git a/src/ctap/mod.rs b/src/ctap/mod.rs index 1ecac53..548523a 100644 --- a/src/ctap/mod.rs +++ b/src/ctap/mod.rs @@ -42,7 +42,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_CREDENTIAL_COUNT_IN_LIST, MAX_CRED_BLOB_LENGTH, MAX_LARGE_BLOB_ARRAY_SIZE, MAX_MSG_SIZE, MAX_RP_IDS_LENGTH, USE_BATCH_ATTESTATION, USE_SIGNATURE_COUNTER, }; use self::data_formats::{ @@ -52,7 +52,7 @@ use self::data_formats::{ PublicKeyCredentialType, PublicKeyCredentialUserEntity, SignatureAlgorithm, }; use self::hid::ChannelID; -use self::large_blobs::{LargeBlobs, MAX_MSG_SIZE}; +use self::large_blobs::LargeBlobs; use self::response::{ AuthenticatorGetAssertionResponse, AuthenticatorGetInfoResponse, AuthenticatorMakeCredentialResponse, AuthenticatorVendorResponse, ResponseData,