Move three dependent customizations into new file
* default_min_pin_length(_rp_ids) and max_rp_ids_length * Did some backing store tricks to make the list configurable in TestCustomization.
This commit is contained in:
@@ -42,6 +42,32 @@ pub trait Customization {
|
||||
/// This can improve privacy, but can make usage less comfortable.
|
||||
fn default_cred_protect(&self) -> Option<CredentialProtectionPolicy>;
|
||||
|
||||
/// 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.
|
||||
fn default_min_pin_length(&self) -> u8;
|
||||
|
||||
/// 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.
|
||||
fn default_min_pin_length_rp_ids(&self) -> &[&str];
|
||||
|
||||
/// Maximum message size send for CTAP commands.
|
||||
///
|
||||
/// The maximum value is 7609, as HID packets can not encode longer messages.
|
||||
@@ -50,17 +76,45 @@ pub trait Customization {
|
||||
/// If long commands are too unreliable on your hardware, consider decreasing
|
||||
/// this value.
|
||||
fn max_msg_size(&self) -> usize;
|
||||
|
||||
// ###########################################################################
|
||||
// Constants for performance optimization or adapting to different hardware.
|
||||
//
|
||||
// Those constants may be modified before compilation to tune the behavior of
|
||||
// the key.
|
||||
// ###########################################################################
|
||||
|
||||
/// 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.
|
||||
fn max_rp_ids_length(&self) -> usize;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CustomizationImpl {
|
||||
pub default_min_pin_length: u8,
|
||||
pub default_min_pin_length_rp_ids: &'static [&'static str],
|
||||
pub default_cred_protect: Option<CredentialProtectionPolicy>,
|
||||
pub max_msg_size: usize,
|
||||
pub max_rp_ids_length: usize,
|
||||
}
|
||||
|
||||
pub const DEFAULT_CUSTOMIZATION: CustomizationImpl = CustomizationImpl {
|
||||
default_min_pin_length: 4,
|
||||
default_min_pin_length_rp_ids: &[],
|
||||
default_cred_protect: None,
|
||||
max_msg_size: 7609,
|
||||
max_rp_ids_length: 8,
|
||||
};
|
||||
|
||||
impl Customization for CustomizationImpl {
|
||||
@@ -68,9 +122,21 @@ impl Customization for CustomizationImpl {
|
||||
self.default_cred_protect
|
||||
}
|
||||
|
||||
fn default_min_pin_length(&self) -> u8 {
|
||||
self.default_min_pin_length
|
||||
}
|
||||
|
||||
fn default_min_pin_length_rp_ids(&self) -> &[&str] {
|
||||
self.default_min_pin_length_rp_ids
|
||||
}
|
||||
|
||||
fn max_msg_size(&self) -> usize {
|
||||
self.max_msg_size
|
||||
}
|
||||
|
||||
fn max_rp_ids_length(&self) -> usize {
|
||||
self.max_rp_ids_length
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
@@ -79,6 +145,19 @@ pub fn is_valid(customization: &impl Customization) -> bool {
|
||||
if customization.max_msg_size() < 1024 || customization.max_msg_size() > 7609 {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Default min pin length must be between 4 and 63.
|
||||
if customization.default_min_pin_length() < 4 || customization.default_min_pin_length() > 63 {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Default min pin length rp ids must be non-empty if max rp ids length is 0.
|
||||
if customization.max_rp_ids_length() == 0
|
||||
&& customization.default_min_pin_length_rp_ids().is_empty()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
|
||||
@@ -19,36 +19,6 @@
|
||||
|
||||
use crate::ctap::data_formats::EnterpriseAttestationMode;
|
||||
|
||||
// ###########################################################################
|
||||
// Constants for adjusting privacy and protection levels.
|
||||
// ###########################################################################
|
||||
|
||||
/// 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.
|
||||
@@ -171,21 +141,6 @@ pub const MAX_CREDENTIAL_COUNT_IN_LIST: Option<usize> = None;
|
||||
/// - 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
|
||||
@@ -217,8 +172,6 @@ mod test {
|
||||
// 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());
|
||||
@@ -231,8 +184,5 @@ mod test {
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,8 +45,7 @@ use self::credential_management::process_credential_management;
|
||||
use self::crypto_wrapper::{aes256_cbc_decrypt, aes256_cbc_encrypt};
|
||||
use self::customization::{
|
||||
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,
|
||||
MAX_CRED_BLOB_LENGTH, MAX_LARGE_BLOB_ARRAY_SIZE, USE_BATCH_ATTESTATION, USE_SIGNATURE_COUNTER,
|
||||
};
|
||||
use self::data_formats::{
|
||||
AuthenticatorTransport, CoseKey, CoseSignature, CredentialProtectionPolicy,
|
||||
@@ -1224,7 +1223,9 @@ impl CtapState {
|
||||
min_pin_length: storage::min_pin_length(env)?,
|
||||
firmware_version: None,
|
||||
max_cred_blob_length: Some(MAX_CRED_BLOB_LENGTH as u64),
|
||||
max_rp_ids_for_set_min_pin_length: Some(MAX_RP_IDS_LENGTH as u64),
|
||||
max_rp_ids_for_set_min_pin_length: Some(
|
||||
env.customization().max_rp_ids_length() as u64
|
||||
),
|
||||
certifications: None,
|
||||
remaining_discoverable_credentials: Some(
|
||||
storage::remaining_credentials(env)? as u64
|
||||
@@ -1531,7 +1532,7 @@ mod test {
|
||||
0x0C => false,
|
||||
0x0D => storage::min_pin_length(&mut env).unwrap() as u64,
|
||||
0x0F => MAX_CRED_BLOB_LENGTH as u64,
|
||||
0x10 => MAX_RP_IDS_LENGTH as u64,
|
||||
0x10 => env.customization().max_rp_ids_length() as u64,
|
||||
0x14 => storage::remaining_credentials(&mut env).unwrap() as u64,
|
||||
};
|
||||
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
|
||||
mod key;
|
||||
|
||||
use crate::api::customization::Customization;
|
||||
use crate::ctap::client_pin::PIN_AUTH_LENGTH;
|
||||
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,
|
||||
ENFORCE_ALWAYS_UV, MAX_LARGE_BLOB_ARRAY_SIZE, MAX_PIN_RETRIES, MAX_SUPPORTED_RESIDENT_KEYS,
|
||||
};
|
||||
use crate::ctap::data_formats::{
|
||||
extract_array, extract_text_string, CredentialProtectionPolicy, PublicKeyCredentialSource,
|
||||
@@ -384,7 +384,7 @@ pub fn reset_pin_retries(env: &mut impl Env) -> Result<(), Ctap2StatusCode> {
|
||||
/// Returns the minimum PIN length.
|
||||
pub fn min_pin_length(env: &mut impl Env) -> Result<u8, Ctap2StatusCode> {
|
||||
match env.store().find(key::MIN_PIN_LENGTH)? {
|
||||
None => Ok(DEFAULT_MIN_PIN_LENGTH),
|
||||
None => Ok(env.customization().default_min_pin_length()),
|
||||
Some(value) if value.len() == 1 => Ok(value[0]),
|
||||
_ => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR),
|
||||
}
|
||||
@@ -401,7 +401,8 @@ pub fn min_pin_length_rp_ids(env: &mut impl Env) -> Result<Vec<String>, Ctap2Sta
|
||||
let rp_ids = env.store().find(key::MIN_PIN_LENGTH_RP_IDS)?.map_or_else(
|
||||
|| {
|
||||
Some(
|
||||
DEFAULT_MIN_PIN_LENGTH_RP_IDS
|
||||
env.customization()
|
||||
.default_min_pin_length_rp_ids()
|
||||
.iter()
|
||||
.map(|&s| String::from(s))
|
||||
.collect(),
|
||||
@@ -419,13 +420,13 @@ pub fn set_min_pin_length_rp_ids(
|
||||
min_pin_length_rp_ids: Vec<String>,
|
||||
) -> Result<(), Ctap2StatusCode> {
|
||||
let mut min_pin_length_rp_ids = min_pin_length_rp_ids;
|
||||
for &rp_id in DEFAULT_MIN_PIN_LENGTH_RP_IDS.iter() {
|
||||
for &rp_id in env.customization().default_min_pin_length_rp_ids().iter() {
|
||||
let rp_id = String::from(rp_id);
|
||||
if !min_pin_length_rp_ids.contains(&rp_id) {
|
||||
min_pin_length_rp_ids.push(rp_id);
|
||||
}
|
||||
}
|
||||
if min_pin_length_rp_ids.len() > MAX_RP_IDS_LENGTH {
|
||||
if min_pin_length_rp_ids.len() > env.customization().max_rp_ids_length() {
|
||||
return Err(Ctap2StatusCode::CTAP2_ERR_KEY_STORE_FULL);
|
||||
}
|
||||
Ok(env.store().insert(
|
||||
@@ -1111,7 +1112,10 @@ mod test {
|
||||
let mut env = TestEnv::new();
|
||||
|
||||
// The minimum PIN length is initially at the default.
|
||||
assert_eq!(min_pin_length(&mut env).unwrap(), DEFAULT_MIN_PIN_LENGTH);
|
||||
assert_eq!(
|
||||
min_pin_length(&mut env).unwrap(),
|
||||
env.customization().default_min_pin_length()
|
||||
);
|
||||
|
||||
// Changes by the setter are reflected by the getter..
|
||||
let new_min_pin_length = 8;
|
||||
@@ -1126,13 +1130,13 @@ mod test {
|
||||
// The minimum PIN length RP IDs are initially at the default.
|
||||
assert_eq!(
|
||||
min_pin_length_rp_ids(&mut env).unwrap(),
|
||||
DEFAULT_MIN_PIN_LENGTH_RP_IDS
|
||||
env.customization().default_min_pin_length_rp_ids()
|
||||
);
|
||||
|
||||
// Changes by the setter are reflected by the getter.
|
||||
let mut rp_ids = vec![String::from("example.com")];
|
||||
assert_eq!(set_min_pin_length_rp_ids(&mut env, rp_ids.clone()), Ok(()));
|
||||
for &rp_id in DEFAULT_MIN_PIN_LENGTH_RP_IDS.iter() {
|
||||
for &rp_id in env.customization().default_min_pin_length_rp_ids().iter() {
|
||||
let rp_id = String::from(rp_id);
|
||||
if !rp_ids.contains(&rp_id) {
|
||||
rp_ids.push(rp_id);
|
||||
|
||||
@@ -110,7 +110,7 @@ make_partition! {
|
||||
|
||||
/// The minimum PIN length.
|
||||
///
|
||||
/// If the entry is absent, the minimum PIN length is `DEFAULT_MIN_PIN_LENGTH`.
|
||||
/// If the entry is absent, the minimum PIN length is `Customization::default_min_pin_length()`.
|
||||
MIN_PIN_LENGTH = 2043;
|
||||
|
||||
/// The number of PIN retries.
|
||||
|
||||
82
src/env/test/customization.rs
vendored
Normal file
82
src/env/test/customization.rs
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
use std::slice::from_raw_parts;
|
||||
|
||||
use crate::api::customization::{Customization, CustomizationImpl};
|
||||
use crate::ctap::data_formats::CredentialProtectionPolicy;
|
||||
|
||||
pub struct TestCustomization {
|
||||
pub default_min_pin_length: u8,
|
||||
default_min_pin_length_rp_ids_backing_store: Vec<String>,
|
||||
default_min_pin_length_rp_ids: Vec<*const str>,
|
||||
pub default_cred_protect: Option<CredentialProtectionPolicy>,
|
||||
pub max_msg_size: usize,
|
||||
pub max_rp_ids_length: usize,
|
||||
}
|
||||
|
||||
impl TestCustomization {
|
||||
pub fn set_default_min_pin_length_rp_ids(&mut self, rp_ids: Vec<String>) {
|
||||
self.default_min_pin_length_rp_ids_backing_store = rp_ids;
|
||||
self.default_min_pin_length_rp_ids = self
|
||||
.default_min_pin_length_rp_ids_backing_store
|
||||
.iter()
|
||||
.map(|s| s.as_ref() as *const str)
|
||||
.collect::<Vec<_>>();
|
||||
}
|
||||
}
|
||||
|
||||
impl Customization for TestCustomization {
|
||||
fn default_cred_protect(&self) -> Option<CredentialProtectionPolicy> {
|
||||
self.default_cred_protect
|
||||
}
|
||||
|
||||
fn default_min_pin_length(&self) -> u8 {
|
||||
self.default_min_pin_length
|
||||
}
|
||||
|
||||
fn default_min_pin_length_rp_ids(&self) -> &[&str] {
|
||||
let length = self.default_min_pin_length_rp_ids.len();
|
||||
let rp_ids = self.default_min_pin_length_rp_ids.as_ptr() as *const &str;
|
||||
unsafe { from_raw_parts(rp_ids, length) }
|
||||
}
|
||||
|
||||
fn max_msg_size(&self) -> usize {
|
||||
self.max_msg_size
|
||||
}
|
||||
|
||||
fn max_rp_ids_length(&self) -> usize {
|
||||
self.max_rp_ids_length
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CustomizationImpl> for TestCustomization {
|
||||
fn from(c: CustomizationImpl) -> Self {
|
||||
let CustomizationImpl {
|
||||
default_min_pin_length,
|
||||
default_min_pin_length_rp_ids,
|
||||
default_cred_protect,
|
||||
max_msg_size,
|
||||
max_rp_ids_length,
|
||||
} = c;
|
||||
|
||||
let default_min_pin_length_rp_ids_backing_store = default_min_pin_length_rp_ids
|
||||
.iter()
|
||||
.map(|s| (*s).to_owned())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut ret = Self {
|
||||
default_min_pin_length,
|
||||
default_min_pin_length_rp_ids_backing_store,
|
||||
default_min_pin_length_rp_ids: vec![],
|
||||
default_cred_protect,
|
||||
max_msg_size,
|
||||
max_rp_ids_length,
|
||||
};
|
||||
|
||||
ret.default_min_pin_length_rp_ids = ret
|
||||
.default_min_pin_length_rp_ids_backing_store
|
||||
.iter()
|
||||
.map(|s| s.as_ref() as *const str)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
ret
|
||||
}
|
||||
}
|
||||
12
src/env/test/mod.rs
vendored
12
src/env/test/mod.rs
vendored
@@ -1,12 +1,14 @@
|
||||
use self::upgrade_storage::BufferUpgradeStorage;
|
||||
use crate::api::customization::{CustomizationImpl, DEFAULT_CUSTOMIZATION};
|
||||
use crate::api::customization::DEFAULT_CUSTOMIZATION;
|
||||
use crate::api::firmware_protection::FirmwareProtection;
|
||||
use crate::ctap::status_code::Ctap2StatusCode;
|
||||
use crate::ctap::Channel;
|
||||
use crate::env::{Env, UserPresence};
|
||||
use crypto::rng256::ThreadRng256;
|
||||
use customization::TestCustomization;
|
||||
use persistent_store::{BufferOptions, BufferStorage, Store};
|
||||
|
||||
mod customization;
|
||||
mod upgrade_storage;
|
||||
|
||||
pub struct TestEnv {
|
||||
@@ -14,7 +16,7 @@ pub struct TestEnv {
|
||||
user_presence: TestUserPresence,
|
||||
store: Store<BufferStorage>,
|
||||
upgrade_storage: Option<BufferUpgradeStorage>,
|
||||
customization: CustomizationImpl,
|
||||
customization: TestCustomization,
|
||||
}
|
||||
|
||||
pub struct TestUserPresence {
|
||||
@@ -53,7 +55,7 @@ impl TestEnv {
|
||||
let storage = new_storage();
|
||||
let store = Store::new(storage).ok().unwrap();
|
||||
let upgrade_storage = Some(BufferUpgradeStorage::new().unwrap());
|
||||
let customization = DEFAULT_CUSTOMIZATION.clone();
|
||||
let customization = DEFAULT_CUSTOMIZATION.into();
|
||||
TestEnv {
|
||||
rng,
|
||||
user_presence,
|
||||
@@ -67,7 +69,7 @@ impl TestEnv {
|
||||
self.upgrade_storage = None;
|
||||
}
|
||||
|
||||
pub fn customization_mut(&mut self) -> &mut CustomizationImpl {
|
||||
pub fn customization_mut(&mut self) -> &mut TestCustomization {
|
||||
&mut self.customization
|
||||
}
|
||||
}
|
||||
@@ -97,7 +99,7 @@ impl Env for TestEnv {
|
||||
type UpgradeStorage = BufferUpgradeStorage;
|
||||
type FirmwareProtection = Self;
|
||||
type Write = TestWrite;
|
||||
type Customization = CustomizationImpl;
|
||||
type Customization = TestCustomization;
|
||||
|
||||
fn rng(&mut self) -> &mut Self::Rng {
|
||||
&mut self.rng
|
||||
|
||||
Reference in New Issue
Block a user