Improved documentation for customization (#287)

* move constants to new file, and update documentation

* documentation improvements, deploy checks tests

* fix pylint

* improved code style

* swap build and check
This commit is contained in:
kaczmarczyck
2021-02-19 14:20:23 +01:00
committed by GitHub
parent 6480682d95
commit f11a838cc7
9 changed files with 319 additions and 129 deletions

View File

@@ -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: a few things you can personalize:
1. If you have multiple buttons, choose the buttons responsible for user 1. If you have multiple buttons, choose the buttons responsible for user
presence in `main.rs`. presence in `src/main.rs`.
1. Decide whether you want to use batch attestation. There is a boolean flag in 1. If you have colored LEDs, like different blinking patterns and want to play
`ctap/mod.rs`. It is mandatory for U2F, and you can create your own around with the code in `src/main.rs` more, take a look at e.g. `wink_leds`.
self-signed certificate. The flag is used for FIDO2 and has some privacy 1. You find more options and documentation in `src/ctap/customization.rs`,
implications. Please check including:
[WebAuthn](https://www.w3.org/TR/webauthn/#attestation) for more - The default level for the credProtect extension.
information. - The default minimum PIN length, and what relying parties can set it.
1. Decide whether you want to use signature counters. Currently, only global - Whether you want to enforce alwaysUv.
signature counters are implemented, as they are the default option for U2F. - Settings for enterprise attestation.
The flag in `ctap/mod.rs` only turns them off for FIDO2. The most privacy - The maximum PIN retries.
preserving solution is individual or no signature counters. Again, please - Whether you want to use batch attestation.
check [WebAuthn](https://www.w3.org/TR/webauthn/#signature-counter) for - Whether you want to use signature counters.
documentation. - Various constants to adapt to different hardware.
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`.
### 3D printed enclosure ### 3D printed enclosure

View File

@@ -352,6 +352,7 @@ class OpenSKInstaller:
def build_opensk(self): def build_opensk(self):
info("Building OpenSK application") info("Building OpenSK application")
self._check_invariants()
self._build_app_or_example(is_example=False) self._build_app_or_example(is_example=False)
def _build_app_or_example(self, is_example): def _build_app_or_example(self, is_example):
@@ -390,6 +391,11 @@ class OpenSKInstaller:
# Create a TAB file # Create a TAB file
self.create_tab_file({props.arch: app_path}) 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): def generate_crypto_materials(self, force_regenerate):
has_error = subprocess.call([ has_error = subprocess.call([
os.path.join("tools", "gen_key_materials.sh"), os.path.join("tools", "gen_key_materials.sh"),

View File

@@ -12,6 +12,7 @@
// 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 super::customization::{MAX_CREDENTIAL_COUNT_IN_LIST, MAX_LARGE_BLOB_ARRAY_SIZE};
use super::data_formats::{ use super::data_formats::{
extract_array, extract_bool, extract_byte_string, extract_map, extract_text_string, extract_array, extract_bool, extract_byte_string, extract_map, extract_text_string,
extract_unsigned, ok_or_missing, ClientPinSubCommand, ConfigSubCommand, ConfigSubCommandParams, extract_unsigned, ok_or_missing, ClientPinSubCommand, ConfigSubCommand, ConfigSubCommandParams,
@@ -22,18 +23,12 @@ use super::data_formats::{
}; };
use super::key_material; use super::key_material;
use super::status_code::Ctap2StatusCode; use super::status_code::Ctap2StatusCode;
use super::storage::MAX_LARGE_BLOB_ARRAY_SIZE;
use alloc::string::String; use alloc::string::String;
use alloc::vec::Vec; use alloc::vec::Vec;
use arrayref::array_ref; use arrayref::array_ref;
use cbor::destructure_cbor_map; use cbor::destructure_cbor_map;
use core::convert::TryFrom; 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<usize> = None;
// This constant is a consequence of the structure of messages. // This constant is a consequence of the structure of messages.
const MIN_LARGE_BLOB_LEN: usize = 17; const MIN_LARGE_BLOB_LEN: usize = 17;

View File

@@ -127,7 +127,7 @@ pub fn process_config(
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crate::ctap::ENFORCE_ALWAYS_UV; use crate::ctap::customization::ENFORCE_ALWAYS_UV;
use crypto::rng256::ThreadRng256; use crypto::rng256::ThreadRng256;
#[test] #[test]

269
src/ctap/customization.rs Normal file
View File

@@ -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<CredentialProtectionPolicy> = 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<EnterpriseAttestationMode> = 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<usize> = 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());
}
}
}

View File

@@ -504,11 +504,18 @@ impl TryFrom<cbor::Value> for SignatureAlgorithm {
} }
} }
/// The credProtect extension's policies for resident credentials.
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
#[cfg_attr(test, derive(IntoEnumIterator))] #[cfg_attr(test, derive(IntoEnumIterator))]
pub enum CredentialProtectionPolicy { pub enum CredentialProtectionPolicy {
/// The credential is always discoverable, as if it had no protection level.
UserVerificationOptional = 0x01, UserVerificationOptional = 0x01,
/// The credential is discoverable with
/// - an allowList,
/// - an excludeList,
/// - user verification.
UserVerificationOptionalWithCredentialIdList = 0x02, UserVerificationOptionalWithCredentialIdList = 0x02,
/// The credentials is discoverable with user verification only.
UserVerificationRequired = 0x03, UserVerificationRequired = 0x03,
} }
@@ -939,9 +946,14 @@ impl From<SetMinPinLengthParams> for cbor::Value {
} }
} }
/// The level of enterprise attestation allowed in MakeCredential.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum EnterpriseAttestationMode { 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, VendorFacilitated = 0x01,
/// All relying parties can request an enterprise attestation. The authenticator
/// trusts the platform to filter requests.
PlatformManaged = 0x02, PlatformManaged = 0x02,
} }

View File

@@ -18,6 +18,7 @@ mod config_command;
mod credential_management; mod credential_management;
#[cfg(feature = "with_ctap1")] #[cfg(feature = "with_ctap1")]
mod ctap1; mod ctap1;
mod customization;
pub mod data_formats; pub mod data_formats;
pub mod hid; pub mod hid;
mod key_material; mod key_material;
@@ -31,10 +32,14 @@ mod timed_permission;
use self::command::{ use self::command::{
AuthenticatorClientPinParameters, AuthenticatorGetAssertionParameters, AuthenticatorClientPinParameters, AuthenticatorGetAssertionParameters,
AuthenticatorMakeCredentialParameters, AuthenticatorVendorConfigureParameters, Command, AuthenticatorMakeCredentialParameters, AuthenticatorVendorConfigureParameters, Command,
MAX_CREDENTIAL_COUNT_IN_LIST,
}; };
use self::config_command::process_config; use self::config_command::process_config;
use self::credential_management::process_credential_management; 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::{ use self::data_formats::{
AuthenticatorTransport, CoseKey, CredentialProtectionPolicy, EnterpriseAttestationMode, AuthenticatorTransport, CoseKey, CredentialProtectionPolicy, EnterpriseAttestationMode,
GetAssertionExtensions, PackedAttestationStatement, PublicKeyCredentialDescriptor, GetAssertionExtensions, PackedAttestationStatement, PublicKeyCredentialDescriptor,
@@ -49,7 +54,7 @@ use self::response::{
AuthenticatorMakeCredentialResponse, AuthenticatorVendorResponse, ResponseData, AuthenticatorMakeCredentialResponse, AuthenticatorVendorResponse, ResponseData,
}; };
use self::status_code::Ctap2StatusCode; 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; use self::timed_permission::TimedPermission;
#[cfg(feature = "with_ctap1")] #[cfg(feature = "with_ctap1")]
use self::timed_permission::U2fUserPresenceState; use self::timed_permission::U2fUserPresenceState;
@@ -74,36 +79,7 @@ use libtock_drivers::console::Console;
use libtock_drivers::crp; use libtock_drivers::crp;
use libtock_drivers::timer::{ClockValue, Duration}; 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; 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<EnterpriseAttestationMode> = None;
const ENTERPRISE_RP_ID_LIST: &[&str] = &[];
// Our credential ID consists of // Our credential ID consists of
// - 16 byte initialization vector for AES-256, // - 16 byte initialization vector for AES-256,
// - 32 byte ECDSA private key for the credential, // - 32 byte ECDSA private key for the credential,
@@ -141,15 +117,6 @@ pub const ES256_CRED_PARAM: PublicKeyCredentialParameter = PublicKeyCredentialPa
cred_type: PublicKeyCredentialType::PublicKey, cred_type: PublicKeyCredentialType::PublicKey,
alg: SignatureAlgorithm::ES256, 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<CredentialProtectionPolicy> = 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. // Checks the PIN protocol parameter against all supported versions.
pub fn check_pin_uv_auth_protocol( pub fn check_pin_uv_auth_protocol(
@@ -329,11 +296,6 @@ where
check_user_presence: CheckUserPresence, check_user_presence: CheckUserPresence,
now: ClockValue, now: ClockValue,
) -> CtapState<'a, R, CheckUserPresence> { ) -> 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 persistent_store = PersistentStore::new(rng);
let pin_protocol_v1 = PinProtocolV1::new(rng); let pin_protocol_v1 = PinProtocolV1::new(rng);
CtapState { CtapState {

View File

@@ -14,14 +14,19 @@
mod key; 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::{ use crate::ctap::data_formats::{
extract_array, extract_text_string, CredentialProtectionPolicy, PublicKeyCredentialSource, extract_array, extract_text_string, CredentialProtectionPolicy, PublicKeyCredentialSource,
PublicKeyCredentialUserEntity, PublicKeyCredentialUserEntity,
}; };
use crate::ctap::key_material;
use crate::ctap::pin_protocol_v1::PIN_AUTH_LENGTH; use crate::ctap::pin_protocol_v1::PIN_AUTH_LENGTH;
use crate::ctap::status_code::Ctap2StatusCode; use crate::ctap::status_code::Ctap2StatusCode;
use crate::ctap::INITIAL_SIGNATURE_COUNTER; use crate::ctap::INITIAL_SIGNATURE_COUNTER;
use crate::ctap::{key_material, ENFORCE_ALWAYS_UV};
use crate::embedded_flash::{new_storage, Storage}; use crate::embedded_flash::{new_storage, Storage};
use alloc::string::String; use alloc::string::String;
use alloc::vec; use alloc::vec;
@@ -33,35 +38,6 @@ use core::convert::TryInto;
use crypto::rng256::Rng256; use crypto::rng256::Rng256;
use persistent_store::{fragment, StoreUpdate}; 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. /// Wrapper for master keys.
pub struct MasterKeys { pub struct MasterKeys {
/// Master encryption key. /// Master encryption key.
@@ -825,7 +801,7 @@ mod test {
let mut credential_ids = vec![]; let mut credential_ids = vec![];
for i in 0..MAX_SUPPORTED_RESIDENT_KEYS { 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); let credential_source = create_credential_source(&mut rng, "example.com", user_handle);
credential_ids.push(credential_source.credential_id.clone()); credential_ids.push(credential_source.credential_id.clone());
assert!(persistent_store.store_credential(credential_source).is_ok()); assert!(persistent_store.store_credential(credential_source).is_ok());
@@ -899,7 +875,7 @@ mod test {
assert_eq!(persistent_store.count_credentials().unwrap(), 0); assert_eq!(persistent_store.count_credentials().unwrap(), 0);
for i in 0..MAX_SUPPORTED_RESIDENT_KEYS { 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); let credential_source = create_credential_source(&mut rng, "example.com", user_handle);
assert!(persistent_store.store_credential(credential_source).is_ok()); assert!(persistent_store.store_credential(credential_source).is_ok());
assert_eq!(persistent_store.count_credentials().unwrap(), i + 1); assert_eq!(persistent_store.count_credentials().unwrap(), i + 1);
@@ -948,7 +924,7 @@ mod test {
let mut persistent_store = PersistentStore::new(&mut rng); let mut persistent_store = PersistentStore::new(&mut rng);
for i in 0..MAX_SUPPORTED_RESIDENT_KEYS { 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); let credential_source = create_credential_source(&mut rng, "example.com", user_handle);
assert!(persistent_store.store_credential(credential_source).is_ok()); assert!(persistent_store.store_credential(credential_source).is_ok());
assert_eq!(persistent_store.count_credentials().unwrap(), i + 1); assert_eq!(persistent_store.count_credentials().unwrap(), i + 1);
@@ -1247,10 +1223,6 @@ mod test {
let mut rng = ThreadRng256 {}; let mut rng = ThreadRng256 {};
let persistent_store = PersistentStore::new(&mut rng); let persistent_store = PersistentStore::new(&mut rng);
#[allow(clippy::assertions_on_constants)]
{
assert!(MAX_LARGE_BLOB_ARRAY_SIZE >= 1024);
}
assert!( assert!(
MAX_LARGE_BLOB_ARRAY_SIZE MAX_LARGE_BLOB_ARRAY_SIZE
<= persistent_store.store.max_value_length() <= persistent_store.store.max_value_length()

View File

@@ -141,7 +141,7 @@ mod test {
#[test] #[test]
fn enough_credentials() { 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); assert!(MAX_SUPPORTED_RESIDENT_KEYS <= CREDENTIALS.end - CREDENTIALS.start);
} }