diff --git a/README.md b/README.md index d6c47f5..6abf799 100644 --- a/README.md +++ b/README.md @@ -92,45 +92,19 @@ If you build your own security key, depending on the hardware you use, there are a few things you can personalize: 1. If you have multiple buttons, choose the buttons responsible for user - presence in `main.rs`. -1. Decide whether you want to use batch attestation. There is a boolean flag in - `ctap/mod.rs`. It is mandatory for U2F, and you can create your own - self-signed certificate. The flag is used for FIDO2 and has some privacy - implications. Please check - [WebAuthn](https://www.w3.org/TR/webauthn/#attestation) for more - information. -1. Decide whether you want to use signature counters. Currently, only global - signature counters are implemented, as they are the default option for U2F. - The flag in `ctap/mod.rs` only turns them off for FIDO2. The most privacy - preserving solution is individual or no signature counters. Again, please - check [WebAuthn](https://www.w3.org/TR/webauthn/#signature-counter) for - documentation. -1. Depending on your available flash storage, choose an appropriate maximum - number of supported resident keys and number of pages in `ctap/storage.rs`. -1. Change the default level for the credProtect extension in `ctap/mod.rs`. - When changing the default, resident credentials become undiscoverable without - user verification. This helps privacy, but can make usage less comfortable - for credentials that need less protection. -1. Increase the default minimum length for PINs in `ctap/storage.rs`. - The current minimum is 4. Values from 4 to 63 are allowed. Requiring longer - PINs can help establish trust between users and relying parties. It makes - user verification harder to break, but less convenient. - NIST recommends at least 6-digit PINs in section 5.1.9.1: - https://pages.nist.gov/800-63-3/sp800-63b.html - You can add relying parties to the list of readers of the minimum PIN length. -1. In an enterprise setting, you can adapt `DEFAULT_MIN_PIN_LENGTH_RP_IDS` and - `MAX_RP_IDS_LENGTH` for tuning the `minPinLength` extension. The former - allows some relying parties to read the minimum PIN length by default. The - latter allows storing more relying parties that may check the minimum PIN - length. -1. Increase the `MAX_CRED_BLOB_LENGTH` in `ctap/mod.rs`, if you expect blobs - bigger than the default value. -1. Implement enterprise attestation. This can be as easy as setting - ENTERPRISE_ATTESTATION_MODE in `ctap/mod.rs`. If you want to use a different - attestation type than batch attestation, you have to implement it first. -1. If a certification (additional to FIDO's) requires that all requests are - protected with user verification, set `ENFORCE_ALWAYS_UV` in - `ctap/config_mod.rs` to `true`. + presence in `src/main.rs`. +1. If you have colored LEDs, like different blinking patterns and want to play + around with the code in `src/main.rs` more, take a look at e.g. `wink_leds`. +1. You find more options and documentation in `src/ctap/customization.rs`, + including: + - The default level for the credProtect extension. + - The default minimum PIN length, and what relying parties can set it. + - Whether you want to enforce alwaysUv. + - Settings for enterprise attestation. + - The maximum PIN retries. + - Whether you want to use batch attestation. + - Whether you want to use signature counters. + - Various constants to adapt to different hardware. ### 3D printed enclosure diff --git a/deploy.py b/deploy.py index e8d5ffd..0c8d998 100755 --- a/deploy.py +++ b/deploy.py @@ -352,6 +352,7 @@ class OpenSKInstaller: def build_opensk(self): info("Building OpenSK application") + self._check_invariants() self._build_app_or_example(is_example=False) def _build_app_or_example(self, is_example): @@ -390,6 +391,11 @@ class OpenSKInstaller: # Create a TAB file self.create_tab_file({props.arch: app_path}) + def _check_invariants(self): + print("Testing invariants in customization.rs...") + self.checked_command_output( + ["cargo", "test", "--features=std", "--lib", "customization"]) + def generate_crypto_materials(self, force_regenerate): has_error = subprocess.call([ os.path.join("tools", "gen_key_materials.sh"), diff --git a/src/ctap/command.rs b/src/ctap/command.rs index eb16a1f..78a80aa 100644 --- a/src/ctap/command.rs +++ b/src/ctap/command.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use super::customization::{MAX_CREDENTIAL_COUNT_IN_LIST, MAX_LARGE_BLOB_ARRAY_SIZE}; use super::data_formats::{ extract_array, extract_bool, extract_byte_string, extract_map, extract_text_string, extract_unsigned, ok_or_missing, ClientPinSubCommand, ConfigSubCommand, ConfigSubCommandParams, @@ -22,18 +23,12 @@ use super::data_formats::{ }; use super::key_material; use super::status_code::Ctap2StatusCode; -use super::storage::MAX_LARGE_BLOB_ARRAY_SIZE; use alloc::string::String; use alloc::vec::Vec; use arrayref::array_ref; use cbor::destructure_cbor_map; use core::convert::TryFrom; -// Depending on your memory, you can use Some(n) to limit request sizes in -// MakeCredential and GetAssertion. This affects allowList and excludeList. -// You might also want to set the max credential size in process_get_info then. -pub const MAX_CREDENTIAL_COUNT_IN_LIST: Option = None; - // This constant is a consequence of the structure of messages. const MIN_LARGE_BLOB_LEN: usize = 17; diff --git a/src/ctap/config_command.rs b/src/ctap/config_command.rs index c65fc56..cf98889 100644 --- a/src/ctap/config_command.rs +++ b/src/ctap/config_command.rs @@ -127,7 +127,7 @@ pub fn process_config( #[cfg(test)] mod test { use super::*; - use crate::ctap::ENFORCE_ALWAYS_UV; + use crate::ctap::customization::ENFORCE_ALWAYS_UV; use crypto::rng256::ThreadRng256; #[test] diff --git a/src/ctap/customization.rs b/src/ctap/customization.rs new file mode 100644 index 0000000..1aebadf --- /dev/null +++ b/src/ctap/customization.rs @@ -0,0 +1,269 @@ +// Copyright 2021 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. + +use crate::ctap::data_formats::{CredentialProtectionPolicy, EnterpriseAttestationMode}; + +// ########################################################################### +// Constants for adjusting privacy and protection levels. +// ########################################################################### + +/// Changes the default level for the credProtect extension. +/// +/// You can change this value to one of the following for more privacy: +/// - CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList +/// - CredentialProtectionPolicy::UserVerificationRequired +/// +/// UserVerificationOptionalWithCredentialIdList +/// Resident credentials are discoverable with +/// - an allowList, +/// - an excludeList, +/// - user verification. +/// +/// UserVerificationRequired +/// Resident credentials are discoverable with user verification only. +/// +/// This can improve privacy, but can make usage less comfortable. +pub const DEFAULT_CRED_PROTECT: Option = None; + +/// Sets the initial minimum PIN length in code points. +/// +/// # Invariant +/// +/// - The minimum PIN length must be at least 4. +/// - The minimum PIN length must be at most 63. +/// - DEFAULT_MIN_PIN_LENGTH_RP_IDS must be non-empty if MAX_RP_IDS_LENGTH is 0. +/// +/// Requiring longer PINs can help establish trust between users and relying +/// parties. It makes user verification harder to break, but less convenient. +/// NIST recommends at least 6-digit PINs in section 5.1.9.1: +/// https://pages.nist.gov/800-63-3/sp800-63b.html +/// +/// Reset reverts the minimum PIN length to this DEFAULT_MIN_PIN_LENGTH. +pub const DEFAULT_MIN_PIN_LENGTH: u8 = 4; + +/// Lists relying parties that can read the minimum PIN length. +/// +/// # Invariant +/// +/// - DEFAULT_MIN_PIN_LENGTH_RP_IDS must be non-empty if MAX_RP_IDS_LENGTH is 0 +/// +/// Only the RP IDs listed in DEFAULT_MIN_PIN_LENGTH_RP_IDS are allowed to read +/// the minimum PIN length with the minPinLength extension. +pub const DEFAULT_MIN_PIN_LENGTH_RP_IDS: &[&str] = &[]; + +/// Enforces the alwaysUv option. +/// +/// When setting to true, commands require a PIN. +/// Also, alwaysUv can not be disabled by commands. +/// +/// A certification (additional to FIDO Alliance's) might require enforcing +/// alwaysUv. Otherwise, users should have the choice to configure alwaysUv. +/// Calling toggleAlwaysUv is preferred over enforcing alwaysUv here. +pub const ENFORCE_ALWAYS_UV: bool = false; + +/// Allows usage of enterprise attestation. +/// +/// # Invariant +/// +/// - Enterprise and batch attestation can not both be active. +/// - If the mode is VendorFacilitated, ENTERPRISE_RP_ID_LIST must be non-empty. +/// +/// For privacy reasons, it is disabled by default. You can choose between: +/// - EnterpriseAttestationMode::VendorFacilitated +/// - EnterpriseAttestationMode::PlatformManaged +/// +/// VendorFacilitated +/// Enterprise attestation is restricted to ENTERPRISE_RP_ID_LIST. Add your +/// enterprises domain, e.g. "example.com", to the list below. +/// +/// PlatformManaged +/// All relying parties can request an enterprise attestation. The authenticator +/// trusts the platform to filter requests. +/// +/// To enable the feature, send the subcommand enableEnterpriseAttestation in +/// AuthenticatorConfig. An enterprise might want to customize the type of +/// attestation that is used. OpenSK defaults to batch attestation. Configuring +/// individual certificates then makes authenticators identifiable. +/// +/// OpenSK prevents activating batch and enterprise attestation together. The +/// current implementation uses the same key material at the moment, and these +/// two modes have conflicting privacy guarantees. +/// If you implement your own enterprise attestation mechanism, and you want +/// batch attestation at the same time, proceed carefully and remove the +/// assertion. +pub const ENTERPRISE_ATTESTATION_MODE: Option = None; + +/// Lists relying party IDs that can perform enterprise attestation. +/// +/// # Invariant +/// +/// - If the mode is VendorFacilitated, ENTERPRISE_RP_ID_LIST must be non-empty. +/// +/// This list is only considered if the enterprise attestation mode is +/// VendorFacilitated. +pub const ENTERPRISE_RP_ID_LIST: &[&str] = &[]; + +/// Sets the number of consecutive failed PINs before blocking interaction. +/// +/// # Invariant +/// +/// - CTAP2.0: Maximum PIN retries must be 8. +/// - CTAP2.1: Maximum PIN retries must be 8 at most. +/// +/// The fail retry counter is reset after entering the correct PIN. +pub const MAX_PIN_RETRIES: u8 = 8; + +/// Enables or disables basic attestation for FIDO2. +/// +/// # Invariant +/// +/// - Enterprise and batch attestation can not both be active (see above). +/// +/// The basic attestation uses the signing key configured with a vendor command +/// as a batch key. If you turn batch attestation on, be aware that it is your +/// responsibility to safely generate and store the key material. Also, the +/// batches must have size of at least 100k authenticators before using new key +/// material. +/// U2F is unaffected by this setting. +/// +/// https://www.w3.org/TR/webauthn/#attestation +pub const USE_BATCH_ATTESTATION: bool = false; + +/// Enables or disables signature counters. +/// +/// The signature counter is currently implemented as a global counter. +/// The specification strongly suggests to have per-credential counters. +/// Implementing those means you can't have an infinite amount of server-side +/// credentials anymore. Also, since counters need frequent writes on the +/// persistent storage, we might need a flash friendly implementation. This +/// solution is a compromise to be compatible with U2F and not wasting storage. +/// +/// https://www.w3.org/TR/webauthn/#signature-counter +pub const USE_SIGNATURE_COUNTER: bool = true; + +// ########################################################################### +// Constants for performance optimization or adapting to different hardware. +// +// Those constants may be modified before compilation to tune the behavior of +// the key. +// ########################################################################### + +/// Sets the maximum blob size stored with the credBlob extension. +/// +/// # Invariant +/// +/// - The length must be at least 32. +pub const MAX_CRED_BLOB_LENGTH: usize = 32; + +/// Limits the number of considered entries in credential lists. +/// +/// # Invariant +/// +/// - This value, if present, must be at least 1 (more is preferred). +/// +/// Depending on your memory, you can use Some(n) to limit request sizes in +/// MakeCredential and GetAssertion. This affects allowList and excludeList. +pub const MAX_CREDENTIAL_COUNT_IN_LIST: Option = None; + +/// Limits the size of largeBlobs the authenticator stores. +/// +/// # Invariant +/// +/// - The allowed size must be at least 1024. +/// - The array must fit into the shards reserved in storage/key.rs. +pub const MAX_LARGE_BLOB_ARRAY_SIZE: usize = 2048; + +/// Limits the number of RP IDs that can change the minimum PIN length. +/// +/// # Invariant +/// +/// - If this value is 0, DEFAULT_MIN_PIN_LENGTH_RP_IDS must be non-empty. +/// +/// You can use this constant to have an upper limit in storage requirements. +/// This might be useful if you want to more reliably predict the remaining +/// storage. Stored string can still be of arbitrary length though, until RP ID +/// truncation is implemented. +/// Outside of memory considerations, you can set this value to 0 if only RP IDs +/// in DEFAULT_MIN_PIN_LENGTH_RP_IDS should be allowed to change the minimum PIN +/// length. +pub const MAX_RP_IDS_LENGTH: usize = 8; + +/// Sets the number of resident keys you can store. +/// +/// # Invariant +/// +/// - The storage key CREDENTIALS must fit at least this number of credentials. +/// +/// This value has implications on the flash lifetime, please see the +/// documentation for NUM_PAGES below. +pub const MAX_SUPPORTED_RESIDENT_KEYS: usize = 150; + +/// Sets the number of pages used for persistent storage. +/// +/// The number of pages should be at least 3 and at most what the flash can +/// hold. There should be no reason to put a small number here, except that the +/// latency of flash operations is linear in the number of pages. This may +/// improve in the future. Currently, using 20 pages gives between 20ms and +/// 240ms per operation. The rule of thumb is between 1ms and 12ms per +/// additional page. +/// +/// Limiting the number of resident keys permits to ensure a minimum number of +/// counter increments. +/// Let: +/// - P the number of pages (NUM_PAGES) +/// - K the maximum number of resident keys (MAX_SUPPORTED_RESIDENT_KEYS) +/// - S the maximum size of a resident key (about 500) +/// - C the number of erase cycles (10000) +/// - I the minimum number of counter increments +/// +/// We have: I = (P * 4084 - 5107 - K * S) / 8 * C +/// +/// With P=20 and K=150, we have I=2M which is enough for 500 increments per day +/// for 10 years. +pub const NUM_PAGES: usize = 20; + +#[cfg(test)] +mod test { + use super::*; + + #[test] + #[allow(clippy::assertions_on_constants)] + fn test_invariants() { + // Two invariants are currently tested in different files: + // - storage.rs: if MAX_LARGE_BLOB_ARRAY_SIZE fits the shards + // - storage/key.rs: if MAX_SUPPORTED_RESIDENT_KEYS fits CREDENTIALS + assert!(DEFAULT_MIN_PIN_LENGTH >= 4); + assert!(DEFAULT_MIN_PIN_LENGTH <= 63); + assert!(!USE_BATCH_ATTESTATION || ENTERPRISE_ATTESTATION_MODE.is_none()); + if let Some(EnterpriseAttestationMode::VendorFacilitated) = ENTERPRISE_ATTESTATION_MODE { + assert!(!ENTERPRISE_RP_ID_LIST.is_empty()); + } else { + assert!(ENTERPRISE_RP_ID_LIST.is_empty()); + } + assert!(MAX_PIN_RETRIES <= 8); + assert!(MAX_CRED_BLOB_LENGTH >= 32); + if let Some(count) = MAX_CREDENTIAL_COUNT_IN_LIST { + assert!(count >= 1); + } + assert!(MAX_LARGE_BLOB_ARRAY_SIZE >= 1024); + if MAX_RP_IDS_LENGTH == 0 { + assert!(!DEFAULT_MIN_PIN_LENGTH_RP_IDS.is_empty()); + } + } +} diff --git a/src/ctap/data_formats.rs b/src/ctap/data_formats.rs index 04e9a36..97ee858 100644 --- a/src/ctap/data_formats.rs +++ b/src/ctap/data_formats.rs @@ -504,11 +504,18 @@ impl TryFrom for SignatureAlgorithm { } } +/// The credProtect extension's policies for resident credentials. #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] #[cfg_attr(test, derive(IntoEnumIterator))] pub enum CredentialProtectionPolicy { + /// The credential is always discoverable, as if it had no protection level. UserVerificationOptional = 0x01, + /// The credential is discoverable with + /// - an allowList, + /// - an excludeList, + /// - user verification. UserVerificationOptionalWithCredentialIdList = 0x02, + /// The credentials is discoverable with user verification only. UserVerificationRequired = 0x03, } @@ -939,9 +946,14 @@ impl From for cbor::Value { } } +/// The level of enterprise attestation allowed in MakeCredential. #[derive(Debug, PartialEq)] pub enum EnterpriseAttestationMode { + /// Enterprise attestation is restricted to a list of RP IDs. Add your + /// enterprises domain, e.g. "example.com", to the list below. VendorFacilitated = 0x01, + /// All relying parties can request an enterprise attestation. The authenticator + /// trusts the platform to filter requests. PlatformManaged = 0x02, } diff --git a/src/ctap/mod.rs b/src/ctap/mod.rs index 18b0345..f74b16e 100644 --- a/src/ctap/mod.rs +++ b/src/ctap/mod.rs @@ -18,6 +18,7 @@ mod config_command; mod credential_management; #[cfg(feature = "with_ctap1")] mod ctap1; +mod customization; pub mod data_formats; pub mod hid; mod key_material; @@ -31,10 +32,14 @@ mod timed_permission; use self::command::{ AuthenticatorClientPinParameters, AuthenticatorGetAssertionParameters, AuthenticatorMakeCredentialParameters, AuthenticatorVendorConfigureParameters, Command, - MAX_CREDENTIAL_COUNT_IN_LIST, }; use self::config_command::process_config; use self::credential_management::process_credential_management; +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_RP_IDS_LENGTH, USE_BATCH_ATTESTATION, USE_SIGNATURE_COUNTER, +}; use self::data_formats::{ AuthenticatorTransport, CoseKey, CredentialProtectionPolicy, EnterpriseAttestationMode, GetAssertionExtensions, PackedAttestationStatement, PublicKeyCredentialDescriptor, @@ -49,7 +54,7 @@ use self::response::{ AuthenticatorMakeCredentialResponse, AuthenticatorVendorResponse, ResponseData, }; use self::status_code::Ctap2StatusCode; -use self::storage::{PersistentStore, MAX_LARGE_BLOB_ARRAY_SIZE, MAX_RP_IDS_LENGTH}; +use self::storage::PersistentStore; use self::timed_permission::TimedPermission; #[cfg(feature = "with_ctap1")] use self::timed_permission::U2fUserPresenceState; @@ -74,36 +79,7 @@ use libtock_drivers::console::Console; use libtock_drivers::crp; use libtock_drivers::timer::{ClockValue, Duration}; -// This flag enables or disables basic attestation for FIDO2. U2F is unaffected by -// this setting. The basic attestation uses the signing key configured with a -// vendor command as a batch key. If you turn batch attestation on, be aware that -// it is your responsibility to safely generate and store the key material. Also, -// the batches must have size of at least 100k authenticators before using new -// key material. -const USE_BATCH_ATTESTATION: bool = false; -// The signature counter is currently implemented as a global counter, if you set -// this flag to true. The spec strongly suggests to have per-credential-counters, -// but it means you can't have an infinite amount of credentials anymore. Also, -// since this is the only piece of information that needs writing often, we might -// need a flash storage friendly way to implement this feature. The implemented -// solution is a compromise to be compatible with U2F and not wasting storage. -const USE_SIGNATURE_COUNTER: bool = true; pub const INITIAL_SIGNATURE_COUNTER: u32 = 1; -// This flag allows usage of enterprise attestation. For privacy reasons, it is -// disabled by default. You can choose between -// - EnterpriseAttestationMode::VendorFacilitated, -// - EnterpriseAttestationMode::PlatformManaged. -// For VendorFacilitated, choose an appriopriate ENTERPRISE_RP_ID_LIST. -// To enable the feature, send the subcommand enableEnterpriseAttestation in -// AuthenticatorConfig. An enterprise might want to customize the type of -// attestation that is used. OpenSK defaults to batch attestation. Configuring -// individual certificates then makes authenticators identifiable. Do NOT set -// USE_BATCH_ATTESTATION to true at the same time in this case! The code asserts -// that you don't use the same key material for batch and enterprise attestation. -// If you implement your own enterprise attestation mechanism, and you want batch -// attestation at the same time, proceed carefully and remove the assertion. -pub const ENTERPRISE_ATTESTATION_MODE: Option = None; -const ENTERPRISE_RP_ID_LIST: &[&str] = &[]; // Our credential ID consists of // - 16 byte initialization vector for AES-256, // - 32 byte ECDSA private key for the credential, @@ -141,15 +117,6 @@ pub const ES256_CRED_PARAM: PublicKeyCredentialParameter = PublicKeyCredentialPa cred_type: PublicKeyCredentialType::PublicKey, alg: SignatureAlgorithm::ES256, }; -// You can change this value to one of the following for more privacy. -// - Some(CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList) -// - Some(CredentialProtectionPolicy::UserVerificationRequired) -const DEFAULT_CRED_PROTECT: Option = None; -// Maximum size stored with the credBlob extension. Must be at least 32. -const MAX_CRED_BLOB_LENGTH: usize = 32; -// Enforce the alwaysUv option. With this constant set to true, commands require -// a PIN to be set up. alwaysUv can not be disabled by commands. -pub const ENFORCE_ALWAYS_UV: bool = false; // Checks the PIN protocol parameter against all supported versions. pub fn check_pin_uv_auth_protocol( @@ -329,11 +296,6 @@ where check_user_presence: CheckUserPresence, now: ClockValue, ) -> CtapState<'a, R, CheckUserPresence> { - #[allow(clippy::assertions_on_constants)] - { - assert!(!USE_BATCH_ATTESTATION || ENTERPRISE_ATTESTATION_MODE.is_none()); - } - let persistent_store = PersistentStore::new(rng); let pin_protocol_v1 = PinProtocolV1::new(rng); CtapState { diff --git a/src/ctap/storage.rs b/src/ctap/storage.rs index b1aa4ec..0f6657f 100644 --- a/src/ctap/storage.rs +++ b/src/ctap/storage.rs @@ -14,14 +14,19 @@ mod key; +use crate::ctap::customization::{ + DEFAULT_MIN_PIN_LENGTH, DEFAULT_MIN_PIN_LENGTH_RP_IDS, ENFORCE_ALWAYS_UV, + MAX_LARGE_BLOB_ARRAY_SIZE, MAX_PIN_RETRIES, MAX_RP_IDS_LENGTH, MAX_SUPPORTED_RESIDENT_KEYS, + NUM_PAGES, +}; use crate::ctap::data_formats::{ extract_array, extract_text_string, CredentialProtectionPolicy, PublicKeyCredentialSource, PublicKeyCredentialUserEntity, }; +use crate::ctap::key_material; use crate::ctap::pin_protocol_v1::PIN_AUTH_LENGTH; use crate::ctap::status_code::Ctap2StatusCode; use crate::ctap::INITIAL_SIGNATURE_COUNTER; -use crate::ctap::{key_material, ENFORCE_ALWAYS_UV}; use crate::embedded_flash::{new_storage, Storage}; use alloc::string::String; use alloc::vec; @@ -33,35 +38,6 @@ use core::convert::TryInto; use crypto::rng256::Rng256; use persistent_store::{fragment, StoreUpdate}; -// Those constants may be modified before compilation to tune the behavior of the key. -// -// The number of pages should be at least 3 and at most what the flash can hold. There should be no -// reason to put a small number here, except that the latency of flash operations is linear in the -// number of pages. This may improve in the future. Currently, using 20 pages gives between 20ms and -// 240ms per operation. The rule of thumb is between 1ms and 12ms per additional page. -// -// Limiting the number of resident keys permits to ensure a minimum number of counter increments. -// Let: -// - P the number of pages (NUM_PAGES) -// - K the maximum number of resident keys (MAX_SUPPORTED_RESIDENT_KEYS) -// - S the maximum size of a resident key (about 500) -// - C the number of erase cycles (10000) -// - I the minimum number of counter increments -// -// We have: I = (P * 4084 - 5107 - K * S) / 8 * C -// -// With P=20 and K=150, we have I=2M which is enough for 500 increments per day for 10 years. -const NUM_PAGES: usize = 20; -const MAX_SUPPORTED_RESIDENT_KEYS: usize = 150; - -const MAX_PIN_RETRIES: u8 = 8; -const DEFAULT_MIN_PIN_LENGTH: u8 = 4; -const DEFAULT_MIN_PIN_LENGTH_RP_IDS: &[&str] = &[]; -// This constant is an attempt to limit storage requirements. If you don't set it to 0, -// the stored strings can still be unbounded, but that is true for all RP IDs. -pub const MAX_RP_IDS_LENGTH: usize = 8; -pub const MAX_LARGE_BLOB_ARRAY_SIZE: usize = 2048; - /// Wrapper for master keys. pub struct MasterKeys { /// Master encryption key. @@ -825,7 +801,7 @@ mod test { let mut credential_ids = vec![]; for i in 0..MAX_SUPPORTED_RESIDENT_KEYS { - let user_handle = i.to_ne_bytes().to_vec(); + let user_handle = (i as u32).to_ne_bytes().to_vec(); let credential_source = create_credential_source(&mut rng, "example.com", user_handle); credential_ids.push(credential_source.credential_id.clone()); assert!(persistent_store.store_credential(credential_source).is_ok()); @@ -899,7 +875,7 @@ mod test { assert_eq!(persistent_store.count_credentials().unwrap(), 0); for i in 0..MAX_SUPPORTED_RESIDENT_KEYS { - let user_handle = i.to_ne_bytes().to_vec(); + let user_handle = (i as u32).to_ne_bytes().to_vec(); let credential_source = create_credential_source(&mut rng, "example.com", user_handle); assert!(persistent_store.store_credential(credential_source).is_ok()); assert_eq!(persistent_store.count_credentials().unwrap(), i + 1); @@ -948,7 +924,7 @@ mod test { let mut persistent_store = PersistentStore::new(&mut rng); for i in 0..MAX_SUPPORTED_RESIDENT_KEYS { - let user_handle = i.to_ne_bytes().to_vec(); + let user_handle = (i as u32).to_ne_bytes().to_vec(); let credential_source = create_credential_source(&mut rng, "example.com", user_handle); assert!(persistent_store.store_credential(credential_source).is_ok()); assert_eq!(persistent_store.count_credentials().unwrap(), i + 1); @@ -1247,10 +1223,6 @@ mod test { let mut rng = ThreadRng256 {}; let persistent_store = PersistentStore::new(&mut rng); - #[allow(clippy::assertions_on_constants)] - { - assert!(MAX_LARGE_BLOB_ARRAY_SIZE >= 1024); - } assert!( MAX_LARGE_BLOB_ARRAY_SIZE <= persistent_store.store.max_value_length() diff --git a/src/ctap/storage/key.rs b/src/ctap/storage/key.rs index 7c6bea5..38a2a8b 100644 --- a/src/ctap/storage/key.rs +++ b/src/ctap/storage/key.rs @@ -141,7 +141,7 @@ mod test { #[test] fn enough_credentials() { - use super::super::MAX_SUPPORTED_RESIDENT_KEYS; + use crate::ctap::customization::MAX_SUPPORTED_RESIDENT_KEYS; assert!(MAX_SUPPORTED_RESIDENT_KEYS <= CREDENTIALS.end - CREDENTIALS.start); }