Merge pull request #460 from hcyang-google/customization
Move three dependent customizations into new file
This commit is contained in:
@@ -18,6 +18,8 @@
|
|||||||
//! Our deploy script enforces the invariants.
|
//! Our deploy script enforces the invariants.
|
||||||
|
|
||||||
use crate::ctap::data_formats::CredentialProtectionPolicy;
|
use crate::ctap::data_formats::CredentialProtectionPolicy;
|
||||||
|
use alloc::string::String;
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
pub trait Customization {
|
pub trait Customization {
|
||||||
// ###########################################################################
|
// ###########################################################################
|
||||||
@@ -42,6 +44,42 @@ pub trait Customization {
|
|||||||
/// This can improve privacy, but can make usage less comfortable.
|
/// This can improve privacy, but can make usage less comfortable.
|
||||||
fn default_cred_protect(&self) -> Option<CredentialProtectionPolicy>;
|
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) -> Vec<String>;
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
fn enforce_always_uv(&self) -> bool;
|
||||||
|
|
||||||
/// Maximum message size send for CTAP commands.
|
/// Maximum message size send for CTAP commands.
|
||||||
///
|
///
|
||||||
/// The maximum value is 7609, as HID packets can not encode longer messages.
|
/// The maximum value is 7609, as HID packets can not encode longer messages.
|
||||||
@@ -50,17 +88,73 @@ pub trait Customization {
|
|||||||
/// If long commands are too unreliable on your hardware, consider decreasing
|
/// If long commands are too unreliable on your hardware, consider decreasing
|
||||||
/// this value.
|
/// this value.
|
||||||
fn max_msg_size(&self) -> usize;
|
fn max_msg_size(&self) -> usize;
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
fn max_pin_retries(&self) -> u8;
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
fn use_signature_counter(&self) -> bool;
|
||||||
|
|
||||||
|
// ###########################################################################
|
||||||
|
// 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)]
|
#[derive(Clone)]
|
||||||
pub struct CustomizationImpl {
|
pub struct CustomizationImpl {
|
||||||
pub default_cred_protect: Option<CredentialProtectionPolicy>,
|
pub default_cred_protect: Option<CredentialProtectionPolicy>,
|
||||||
|
pub default_min_pin_length: u8,
|
||||||
|
pub default_min_pin_length_rp_ids: &'static [&'static str],
|
||||||
|
pub enforce_always_uv: bool,
|
||||||
pub max_msg_size: usize,
|
pub max_msg_size: usize,
|
||||||
|
pub max_pin_retries: u8,
|
||||||
|
pub use_signature_counter: bool,
|
||||||
|
pub max_rp_ids_length: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const DEFAULT_CUSTOMIZATION: CustomizationImpl = CustomizationImpl {
|
pub const DEFAULT_CUSTOMIZATION: CustomizationImpl = CustomizationImpl {
|
||||||
|
default_min_pin_length: 4,
|
||||||
|
default_min_pin_length_rp_ids: &[],
|
||||||
|
enforce_always_uv: false,
|
||||||
default_cred_protect: None,
|
default_cred_protect: None,
|
||||||
max_msg_size: 7609,
|
max_msg_size: 7609,
|
||||||
|
max_pin_retries: 8,
|
||||||
|
use_signature_counter: true,
|
||||||
|
max_rp_ids_length: 8,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl Customization for CustomizationImpl {
|
impl Customization for CustomizationImpl {
|
||||||
@@ -68,9 +162,36 @@ impl Customization for CustomizationImpl {
|
|||||||
self.default_cred_protect
|
self.default_cred_protect
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_min_pin_length(&self) -> u8 {
|
||||||
|
self.default_min_pin_length
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_min_pin_length_rp_ids(&self) -> Vec<String> {
|
||||||
|
self.default_min_pin_length_rp_ids
|
||||||
|
.iter()
|
||||||
|
.map(|s| String::from(*s))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enforce_always_uv(&self) -> bool {
|
||||||
|
self.enforce_always_uv
|
||||||
|
}
|
||||||
|
|
||||||
fn max_msg_size(&self) -> usize {
|
fn max_msg_size(&self) -> usize {
|
||||||
self.max_msg_size
|
self.max_msg_size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn max_pin_retries(&self) -> u8 {
|
||||||
|
self.max_pin_retries
|
||||||
|
}
|
||||||
|
|
||||||
|
fn use_signature_counter(&self) -> bool {
|
||||||
|
self.use_signature_counter
|
||||||
|
}
|
||||||
|
|
||||||
|
fn max_rp_ids_length(&self) -> usize {
|
||||||
|
self.max_rp_ids_length
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
@@ -79,6 +200,24 @@ pub fn is_valid(customization: &impl Customization) -> bool {
|
|||||||
if customization.max_msg_size() < 1024 || customization.max_msg_size() > 7609 {
|
if customization.max_msg_size() < 1024 || customization.max_msg_size() > 7609 {
|
||||||
return false;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Max pin retries must be less or equal than 8.
|
||||||
|
if customization.max_pin_retries() > 8 {
|
||||||
|
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
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +226,6 @@ mod test {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[allow(clippy::assertions_on_constants)]
|
|
||||||
fn test_invariants() {
|
fn test_invariants() {
|
||||||
assert!(is_valid(&DEFAULT_CUSTOMIZATION));
|
assert!(is_valid(&DEFAULT_CUSTOMIZATION));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ pub fn process_config(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::ctap::customization::ENFORCE_ALWAYS_UV;
|
use crate::api::customization::Customization;
|
||||||
use crate::ctap::data_formats::PinUvAuthProtocol;
|
use crate::ctap::data_formats::PinUvAuthProtocol;
|
||||||
use crate::ctap::pin_protocol::authenticate_pin_uv_auth_token;
|
use crate::ctap::pin_protocol::authenticate_pin_uv_auth_token;
|
||||||
use crate::env::test::TestEnv;
|
use crate::env::test::TestEnv;
|
||||||
@@ -180,7 +180,7 @@ mod test {
|
|||||||
pin_uv_auth_protocol: None,
|
pin_uv_auth_protocol: None,
|
||||||
};
|
};
|
||||||
let config_response = process_config(&mut env, &mut client_pin, config_params);
|
let config_response = process_config(&mut env, &mut client_pin, config_params);
|
||||||
if ENFORCE_ALWAYS_UV {
|
if env.customization().enforce_always_uv() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
config_response,
|
config_response,
|
||||||
Err(Ctap2StatusCode::CTAP2_ERR_OPERATION_DENIED)
|
Err(Ctap2StatusCode::CTAP2_ERR_OPERATION_DENIED)
|
||||||
@@ -210,7 +210,7 @@ mod test {
|
|||||||
pin_uv_auth_protocol: Some(pin_uv_auth_protocol),
|
pin_uv_auth_protocol: Some(pin_uv_auth_protocol),
|
||||||
};
|
};
|
||||||
let config_response = process_config(&mut env, &mut client_pin, config_params);
|
let config_response = process_config(&mut env, &mut client_pin, config_params);
|
||||||
if ENFORCE_ALWAYS_UV {
|
if env.customization().enforce_always_uv() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
config_response,
|
config_response,
|
||||||
Err(Ctap2StatusCode::CTAP2_ERR_OPERATION_DENIED)
|
Err(Ctap2StatusCode::CTAP2_ERR_OPERATION_DENIED)
|
||||||
|
|||||||
@@ -344,8 +344,9 @@ impl Ctap1Command {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::super::{key_material, CREDENTIAL_ID_SIZE, USE_SIGNATURE_COUNTER};
|
use super::super::{key_material, CREDENTIAL_ID_SIZE};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::api::customization::Customization;
|
||||||
use crate::clock::TEST_CLOCK_FREQUENCY_HZ;
|
use crate::clock::TEST_CLOCK_FREQUENCY_HZ;
|
||||||
use crate::env::test::TestEnv;
|
use crate::env::test::TestEnv;
|
||||||
use crypto::Hash256;
|
use crypto::Hash256;
|
||||||
@@ -643,8 +644,8 @@ mod test {
|
|||||||
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_DATA));
|
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_DATA));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_signature_counter(response: &[u8; 4], signature_counter: u32) {
|
fn check_signature_counter(env: &mut impl Env, response: &[u8; 4], signature_counter: u32) {
|
||||||
if USE_SIGNATURE_COUNTER {
|
if env.customization().use_signature_counter() {
|
||||||
assert_eq!(u32::from_be_bytes(*response), signature_counter);
|
assert_eq!(u32::from_be_bytes(*response), signature_counter);
|
||||||
} else {
|
} else {
|
||||||
assert_eq!(response, &[0x00, 0x00, 0x00, 0x00]);
|
assert_eq!(response, &[0x00, 0x00, 0x00, 0x00]);
|
||||||
@@ -673,9 +674,11 @@ mod test {
|
|||||||
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0))
|
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(response[0], 0x01);
|
assert_eq!(response[0], 0x01);
|
||||||
|
let global_signature_counter = storage::global_signature_counter(&mut env).unwrap();
|
||||||
check_signature_counter(
|
check_signature_counter(
|
||||||
|
&mut env,
|
||||||
array_ref!(response, 1, 4),
|
array_ref!(response, 1, 4),
|
||||||
storage::global_signature_counter(&mut env).unwrap(),
|
global_signature_counter,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -706,9 +709,11 @@ mod test {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(response[0], 0x01);
|
assert_eq!(response[0], 0x01);
|
||||||
|
let global_signature_counter = storage::global_signature_counter(&mut env).unwrap();
|
||||||
check_signature_counter(
|
check_signature_counter(
|
||||||
|
&mut env,
|
||||||
array_ref!(response, 1, 4),
|
array_ref!(response, 1, 4),
|
||||||
storage::global_signature_counter(&mut env).unwrap(),
|
global_signature_counter,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,46 +19,6 @@
|
|||||||
|
|
||||||
use crate::ctap::data_formats::EnterpriseAttestationMode;
|
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.
|
|
||||||
/// 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.
|
/// Allows usage of enterprise attestation.
|
||||||
///
|
///
|
||||||
/// # Invariant
|
/// # Invariant
|
||||||
@@ -101,16 +61,6 @@ pub const ENTERPRISE_ATTESTATION_MODE: Option<EnterpriseAttestationMode> = None;
|
|||||||
/// VendorFacilitated.
|
/// VendorFacilitated.
|
||||||
pub const ENTERPRISE_RP_ID_LIST: &[&str] = &[];
|
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.
|
/// Enables or disables basic attestation for FIDO2.
|
||||||
///
|
///
|
||||||
/// # Invariant
|
/// # Invariant
|
||||||
@@ -127,18 +77,6 @@ pub const MAX_PIN_RETRIES: u8 = 8;
|
|||||||
/// https://www.w3.org/TR/webauthn/#attestation
|
/// https://www.w3.org/TR/webauthn/#attestation
|
||||||
pub const USE_BATCH_ATTESTATION: bool = false;
|
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.
|
// Constants for performance optimization or adapting to different hardware.
|
||||||
//
|
//
|
||||||
@@ -171,21 +109,6 @@ pub const MAX_CREDENTIAL_COUNT_IN_LIST: Option<usize> = None;
|
|||||||
/// - The array must fit into the shards reserved in storage/key.rs.
|
/// - The array must fit into the shards reserved in storage/key.rs.
|
||||||
pub const MAX_LARGE_BLOB_ARRAY_SIZE: usize = 2048;
|
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.
|
/// Sets the number of resident keys you can store.
|
||||||
///
|
///
|
||||||
/// # Invariant
|
/// # Invariant
|
||||||
@@ -217,22 +140,16 @@ mod test {
|
|||||||
// Two invariants are currently tested in different files:
|
// Two invariants are currently tested in different files:
|
||||||
// - storage.rs: if MAX_LARGE_BLOB_ARRAY_SIZE fits the shards
|
// - storage.rs: if MAX_LARGE_BLOB_ARRAY_SIZE fits the shards
|
||||||
// - storage/key.rs: if MAX_SUPPORTED_RESIDENT_KEYS fits CREDENTIALS
|
// - 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());
|
assert!(!USE_BATCH_ATTESTATION || ENTERPRISE_ATTESTATION_MODE.is_none());
|
||||||
if let Some(EnterpriseAttestationMode::VendorFacilitated) = ENTERPRISE_ATTESTATION_MODE {
|
if let Some(EnterpriseAttestationMode::VendorFacilitated) = ENTERPRISE_ATTESTATION_MODE {
|
||||||
assert!(!ENTERPRISE_RP_ID_LIST.is_empty());
|
assert!(!ENTERPRISE_RP_ID_LIST.is_empty());
|
||||||
} else {
|
} else {
|
||||||
assert!(ENTERPRISE_RP_ID_LIST.is_empty());
|
assert!(ENTERPRISE_RP_ID_LIST.is_empty());
|
||||||
}
|
}
|
||||||
assert!(MAX_PIN_RETRIES <= 8);
|
|
||||||
assert!(MAX_CRED_BLOB_LENGTH >= 32);
|
assert!(MAX_CRED_BLOB_LENGTH >= 32);
|
||||||
if let Some(count) = MAX_CREDENTIAL_COUNT_IN_LIST {
|
if let Some(count) = MAX_CREDENTIAL_COUNT_IN_LIST {
|
||||||
assert!(count >= 1);
|
assert!(count >= 1);
|
||||||
}
|
}
|
||||||
assert!(MAX_LARGE_BLOB_ARRAY_SIZE >= 1024);
|
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::crypto_wrapper::{aes256_cbc_decrypt, aes256_cbc_encrypt};
|
||||||
use self::customization::{
|
use self::customization::{
|
||||||
ENTERPRISE_ATTESTATION_MODE, ENTERPRISE_RP_ID_LIST, MAX_CREDENTIAL_COUNT_IN_LIST,
|
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,
|
MAX_CRED_BLOB_LENGTH, MAX_LARGE_BLOB_ARRAY_SIZE, USE_BATCH_ATTESTATION,
|
||||||
USE_SIGNATURE_COUNTER,
|
|
||||||
};
|
};
|
||||||
use self::data_formats::{
|
use self::data_formats::{
|
||||||
AuthenticatorTransport, CoseKey, CoseSignature, CredentialProtectionPolicy,
|
AuthenticatorTransport, CoseKey, CoseSignature, CredentialProtectionPolicy,
|
||||||
@@ -422,7 +421,7 @@ impl CtapState {
|
|||||||
&mut self,
|
&mut self,
|
||||||
env: &mut impl Env,
|
env: &mut impl Env,
|
||||||
) -> Result<(), Ctap2StatusCode> {
|
) -> Result<(), Ctap2StatusCode> {
|
||||||
if USE_SIGNATURE_COUNTER {
|
if env.customization().use_signature_counter() {
|
||||||
let increment = env.rng().gen_uniform_u32x8()[0] % 8 + 1;
|
let increment = env.rng().gen_uniform_u32x8()[0] % 8 + 1;
|
||||||
storage::incr_global_signature_counter(env, increment)?;
|
storage::incr_global_signature_counter(env, increment)?;
|
||||||
}
|
}
|
||||||
@@ -1224,7 +1223,9 @@ impl CtapState {
|
|||||||
min_pin_length: storage::min_pin_length(env)?,
|
min_pin_length: storage::min_pin_length(env)?,
|
||||||
firmware_version: None,
|
firmware_version: None,
|
||||||
max_cred_blob_length: Some(MAX_CRED_BLOB_LENGTH as u64),
|
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,
|
certifications: None,
|
||||||
remaining_discoverable_credentials: Some(
|
remaining_discoverable_credentials: Some(
|
||||||
storage::remaining_credentials(env)? as u64
|
storage::remaining_credentials(env)? as u64
|
||||||
@@ -1397,7 +1398,7 @@ impl CtapState {
|
|||||||
let mut auth_data = vec![];
|
let mut auth_data = vec![];
|
||||||
auth_data.extend(rp_id_hash);
|
auth_data.extend(rp_id_hash);
|
||||||
auth_data.push(flag_byte);
|
auth_data.push(flag_byte);
|
||||||
// The global counter is only increased if USE_SIGNATURE_COUNTER is true.
|
// The global counter is only increased if use_signature_counter() is true.
|
||||||
// It uses a big-endian representation.
|
// It uses a big-endian representation.
|
||||||
let mut signature_counter = [0u8; 4];
|
let mut signature_counter = [0u8; 4];
|
||||||
BigEndian::write_u32(
|
BigEndian::write_u32(
|
||||||
@@ -1531,7 +1532,7 @@ mod test {
|
|||||||
0x0C => false,
|
0x0C => false,
|
||||||
0x0D => storage::min_pin_length(&mut env).unwrap() as u64,
|
0x0D => storage::min_pin_length(&mut env).unwrap() as u64,
|
||||||
0x0F => MAX_CRED_BLOB_LENGTH 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,
|
0x14 => storage::remaining_credentials(&mut env).unwrap() as u64,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -14,11 +14,9 @@
|
|||||||
|
|
||||||
mod key;
|
mod key;
|
||||||
|
|
||||||
|
use crate::api::customization::Customization;
|
||||||
use crate::ctap::client_pin::PIN_AUTH_LENGTH;
|
use crate::ctap::client_pin::PIN_AUTH_LENGTH;
|
||||||
use crate::ctap::customization::{
|
use crate::ctap::customization::{MAX_LARGE_BLOB_ARRAY_SIZE, MAX_SUPPORTED_RESIDENT_KEYS};
|
||||||
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,
|
|
||||||
};
|
|
||||||
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,
|
||||||
@@ -360,7 +358,7 @@ pub fn set_pin(
|
|||||||
/// Returns the number of remaining PIN retries.
|
/// Returns the number of remaining PIN retries.
|
||||||
pub fn pin_retries(env: &mut impl Env) -> Result<u8, Ctap2StatusCode> {
|
pub fn pin_retries(env: &mut impl Env) -> Result<u8, Ctap2StatusCode> {
|
||||||
match env.store().find(key::PIN_RETRIES)? {
|
match env.store().find(key::PIN_RETRIES)? {
|
||||||
None => Ok(MAX_PIN_RETRIES),
|
None => Ok(env.customization().max_pin_retries()),
|
||||||
Some(value) if value.len() == 1 => Ok(value[0]),
|
Some(value) if value.len() == 1 => Ok(value[0]),
|
||||||
_ => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR),
|
_ => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR),
|
||||||
}
|
}
|
||||||
@@ -384,7 +382,7 @@ pub fn reset_pin_retries(env: &mut impl Env) -> Result<(), Ctap2StatusCode> {
|
|||||||
/// Returns the minimum PIN length.
|
/// Returns the minimum PIN length.
|
||||||
pub fn min_pin_length(env: &mut impl Env) -> Result<u8, Ctap2StatusCode> {
|
pub fn min_pin_length(env: &mut impl Env) -> Result<u8, Ctap2StatusCode> {
|
||||||
match env.store().find(key::MIN_PIN_LENGTH)? {
|
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]),
|
Some(value) if value.len() == 1 => Ok(value[0]),
|
||||||
_ => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR),
|
_ => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR),
|
||||||
}
|
}
|
||||||
@@ -399,14 +397,7 @@ pub fn set_min_pin_length(env: &mut impl Env, min_pin_length: u8) -> Result<(),
|
|||||||
/// allowed.
|
/// allowed.
|
||||||
pub fn min_pin_length_rp_ids(env: &mut impl Env) -> Result<Vec<String>, Ctap2StatusCode> {
|
pub fn min_pin_length_rp_ids(env: &mut impl Env) -> Result<Vec<String>, Ctap2StatusCode> {
|
||||||
let rp_ids = env.store().find(key::MIN_PIN_LENGTH_RP_IDS)?.map_or_else(
|
let rp_ids = env.store().find(key::MIN_PIN_LENGTH_RP_IDS)?.map_or_else(
|
||||||
|| {
|
|| Some(env.customization().default_min_pin_length_rp_ids()),
|
||||||
Some(
|
|
||||||
DEFAULT_MIN_PIN_LENGTH_RP_IDS
|
|
||||||
.iter()
|
|
||||||
.map(|&s| String::from(s))
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|value| deserialize_min_pin_length_rp_ids(&value),
|
|value| deserialize_min_pin_length_rp_ids(&value),
|
||||||
);
|
);
|
||||||
debug_assert!(rp_ids.is_some());
|
debug_assert!(rp_ids.is_some());
|
||||||
@@ -419,13 +410,12 @@ pub fn set_min_pin_length_rp_ids(
|
|||||||
min_pin_length_rp_ids: Vec<String>,
|
min_pin_length_rp_ids: Vec<String>,
|
||||||
) -> Result<(), Ctap2StatusCode> {
|
) -> Result<(), Ctap2StatusCode> {
|
||||||
let mut min_pin_length_rp_ids = min_pin_length_rp_ids;
|
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() {
|
||||||
let rp_id = String::from(rp_id);
|
|
||||||
if !min_pin_length_rp_ids.contains(&rp_id) {
|
if !min_pin_length_rp_ids.contains(&rp_id) {
|
||||||
min_pin_length_rp_ids.push(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);
|
return Err(Ctap2StatusCode::CTAP2_ERR_KEY_STORE_FULL);
|
||||||
}
|
}
|
||||||
Ok(env.store().insert(
|
Ok(env.store().insert(
|
||||||
@@ -595,7 +585,7 @@ pub fn enable_enterprise_attestation(env: &mut impl Env) -> Result<(), Ctap2Stat
|
|||||||
|
|
||||||
/// Returns whether alwaysUv is enabled.
|
/// Returns whether alwaysUv is enabled.
|
||||||
pub fn has_always_uv(env: &mut impl Env) -> Result<bool, Ctap2StatusCode> {
|
pub fn has_always_uv(env: &mut impl Env) -> Result<bool, Ctap2StatusCode> {
|
||||||
if ENFORCE_ALWAYS_UV {
|
if env.customization().enforce_always_uv() {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
match env.store().find(key::ALWAYS_UV)? {
|
match env.store().find(key::ALWAYS_UV)? {
|
||||||
@@ -607,7 +597,7 @@ pub fn has_always_uv(env: &mut impl Env) -> Result<bool, Ctap2StatusCode> {
|
|||||||
|
|
||||||
/// Enables alwaysUv, when disabled, and vice versa.
|
/// Enables alwaysUv, when disabled, and vice versa.
|
||||||
pub fn toggle_always_uv(env: &mut impl Env) -> Result<(), Ctap2StatusCode> {
|
pub fn toggle_always_uv(env: &mut impl Env) -> Result<(), Ctap2StatusCode> {
|
||||||
if ENFORCE_ALWAYS_UV {
|
if env.customization().enforce_always_uv() {
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_OPERATION_DENIED);
|
return Err(Ctap2StatusCode::CTAP2_ERR_OPERATION_DENIED);
|
||||||
}
|
}
|
||||||
if has_always_uv(env)? {
|
if has_always_uv(env)? {
|
||||||
@@ -1060,10 +1050,13 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
|
|
||||||
// The pin retries is initially at the maximum.
|
// The pin retries is initially at the maximum.
|
||||||
assert_eq!(pin_retries(&mut env), Ok(MAX_PIN_RETRIES));
|
assert_eq!(
|
||||||
|
pin_retries(&mut env),
|
||||||
|
Ok(env.customization().max_pin_retries())
|
||||||
|
);
|
||||||
|
|
||||||
// Decrementing the pin retries decrements the pin retries.
|
// Decrementing the pin retries decrements the pin retries.
|
||||||
for retries in (0..MAX_PIN_RETRIES).rev() {
|
for retries in (0..env.customization().max_pin_retries()).rev() {
|
||||||
decr_pin_retries(&mut env).unwrap();
|
decr_pin_retries(&mut env).unwrap();
|
||||||
assert_eq!(pin_retries(&mut env), Ok(retries));
|
assert_eq!(pin_retries(&mut env), Ok(retries));
|
||||||
}
|
}
|
||||||
@@ -1074,7 +1067,10 @@ mod test {
|
|||||||
|
|
||||||
// Resetting the pin retries resets the pin retries.
|
// Resetting the pin retries resets the pin retries.
|
||||||
reset_pin_retries(&mut env).unwrap();
|
reset_pin_retries(&mut env).unwrap();
|
||||||
assert_eq!(pin_retries(&mut env), Ok(MAX_PIN_RETRIES));
|
assert_eq!(
|
||||||
|
pin_retries(&mut env),
|
||||||
|
Ok(env.customization().max_pin_retries())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1111,7 +1107,10 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
|
|
||||||
// The minimum PIN length is initially at the default.
|
// 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..
|
// Changes by the setter are reflected by the getter..
|
||||||
let new_min_pin_length = 8;
|
let new_min_pin_length = 8;
|
||||||
@@ -1126,14 +1125,13 @@ mod test {
|
|||||||
// The minimum PIN length RP IDs are initially at the default.
|
// The minimum PIN length RP IDs are initially at the default.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
min_pin_length_rp_ids(&mut env).unwrap(),
|
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.
|
// Changes by the setter are reflected by the getter.
|
||||||
let mut rp_ids = vec![String::from("example.com")];
|
let mut rp_ids = vec![String::from("example.com")];
|
||||||
assert_eq!(set_min_pin_length_rp_ids(&mut env, rp_ids.clone()), Ok(()));
|
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() {
|
||||||
let rp_id = String::from(rp_id);
|
|
||||||
if !rp_ids.contains(&rp_id) {
|
if !rp_ids.contains(&rp_id) {
|
||||||
rp_ids.push(rp_id);
|
rp_ids.push(rp_id);
|
||||||
}
|
}
|
||||||
@@ -1246,7 +1244,7 @@ mod test {
|
|||||||
fn test_always_uv() {
|
fn test_always_uv() {
|
||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
|
|
||||||
if ENFORCE_ALWAYS_UV {
|
if env.customization().enforce_always_uv() {
|
||||||
assert!(has_always_uv(&mut env).unwrap());
|
assert!(has_always_uv(&mut env).unwrap());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
toggle_always_uv(&mut env),
|
toggle_always_uv(&mut env),
|
||||||
|
|||||||
@@ -110,12 +110,12 @@ make_partition! {
|
|||||||
|
|
||||||
/// The minimum PIN length.
|
/// 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;
|
MIN_PIN_LENGTH = 2043;
|
||||||
|
|
||||||
/// The number of PIN retries.
|
/// The number of PIN retries.
|
||||||
///
|
///
|
||||||
/// If the entry is absent, the number of PIN retries is `MAX_PIN_RETRIES`.
|
/// If the entry is absent, the number of PIN retries is `Customization::max_pin_retries()`.
|
||||||
PIN_RETRIES = 2044;
|
PIN_RETRIES = 2044;
|
||||||
|
|
||||||
/// The PIN hash and length.
|
/// The PIN hash and length.
|
||||||
|
|||||||
92
src/env/test/customization.rs
vendored
Normal file
92
src/env/test/customization.rs
vendored
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
use crate::api::customization::{Customization, CustomizationImpl};
|
||||||
|
use crate::ctap::data_formats::CredentialProtectionPolicy;
|
||||||
|
use alloc::string::String;
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
|
pub struct TestCustomization {
|
||||||
|
pub default_cred_protect: Option<CredentialProtectionPolicy>,
|
||||||
|
pub default_min_pin_length: u8,
|
||||||
|
pub default_min_pin_length_rp_ids: Vec<String>,
|
||||||
|
pub enforce_always_uv: bool,
|
||||||
|
pub max_msg_size: usize,
|
||||||
|
pub max_pin_retries: u8,
|
||||||
|
pub use_signature_counter: bool,
|
||||||
|
pub max_rp_ids_length: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
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) -> Vec<String> {
|
||||||
|
self.default_min_pin_length_rp_ids.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enforce_always_uv(&self) -> bool {
|
||||||
|
self.enforce_always_uv
|
||||||
|
}
|
||||||
|
|
||||||
|
fn max_msg_size(&self) -> usize {
|
||||||
|
self.max_msg_size
|
||||||
|
}
|
||||||
|
|
||||||
|
fn max_pin_retries(&self) -> u8 {
|
||||||
|
self.max_pin_retries
|
||||||
|
}
|
||||||
|
|
||||||
|
fn use_signature_counter(&self) -> bool {
|
||||||
|
self.use_signature_counter
|
||||||
|
}
|
||||||
|
|
||||||
|
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_cred_protect,
|
||||||
|
default_min_pin_length,
|
||||||
|
default_min_pin_length_rp_ids,
|
||||||
|
enforce_always_uv,
|
||||||
|
max_msg_size,
|
||||||
|
max_pin_retries,
|
||||||
|
use_signature_counter,
|
||||||
|
max_rp_ids_length,
|
||||||
|
} = c;
|
||||||
|
|
||||||
|
let default_min_pin_length_rp_ids = default_min_pin_length_rp_ids
|
||||||
|
.iter()
|
||||||
|
.map(|s| String::from(*s))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
default_cred_protect,
|
||||||
|
default_min_pin_length,
|
||||||
|
default_min_pin_length_rp_ids,
|
||||||
|
enforce_always_uv,
|
||||||
|
max_msg_size,
|
||||||
|
max_pin_retries,
|
||||||
|
use_signature_counter,
|
||||||
|
max_rp_ids_length,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::api::customization::{is_valid, DEFAULT_CUSTOMIZATION};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_invariants() {
|
||||||
|
let customization = TestCustomization::from(DEFAULT_CUSTOMIZATION.clone());
|
||||||
|
assert!(is_valid(&customization));
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/env/test/mod.rs
vendored
12
src/env/test/mod.rs
vendored
@@ -1,12 +1,14 @@
|
|||||||
use self::upgrade_storage::BufferUpgradeStorage;
|
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::api::firmware_protection::FirmwareProtection;
|
||||||
use crate::ctap::status_code::Ctap2StatusCode;
|
use crate::ctap::status_code::Ctap2StatusCode;
|
||||||
use crate::ctap::Channel;
|
use crate::ctap::Channel;
|
||||||
use crate::env::{Env, UserPresence};
|
use crate::env::{Env, UserPresence};
|
||||||
use crypto::rng256::ThreadRng256;
|
use crypto::rng256::ThreadRng256;
|
||||||
|
use customization::TestCustomization;
|
||||||
use persistent_store::{BufferOptions, BufferStorage, Store};
|
use persistent_store::{BufferOptions, BufferStorage, Store};
|
||||||
|
|
||||||
|
mod customization;
|
||||||
mod upgrade_storage;
|
mod upgrade_storage;
|
||||||
|
|
||||||
pub struct TestEnv {
|
pub struct TestEnv {
|
||||||
@@ -14,7 +16,7 @@ pub struct TestEnv {
|
|||||||
user_presence: TestUserPresence,
|
user_presence: TestUserPresence,
|
||||||
store: Store<BufferStorage>,
|
store: Store<BufferStorage>,
|
||||||
upgrade_storage: Option<BufferUpgradeStorage>,
|
upgrade_storage: Option<BufferUpgradeStorage>,
|
||||||
customization: CustomizationImpl,
|
customization: TestCustomization,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TestUserPresence {
|
pub struct TestUserPresence {
|
||||||
@@ -53,7 +55,7 @@ impl TestEnv {
|
|||||||
let storage = new_storage();
|
let storage = new_storage();
|
||||||
let store = Store::new(storage).ok().unwrap();
|
let store = Store::new(storage).ok().unwrap();
|
||||||
let upgrade_storage = Some(BufferUpgradeStorage::new().unwrap());
|
let upgrade_storage = Some(BufferUpgradeStorage::new().unwrap());
|
||||||
let customization = DEFAULT_CUSTOMIZATION.clone();
|
let customization = DEFAULT_CUSTOMIZATION.into();
|
||||||
TestEnv {
|
TestEnv {
|
||||||
rng,
|
rng,
|
||||||
user_presence,
|
user_presence,
|
||||||
@@ -67,7 +69,7 @@ impl TestEnv {
|
|||||||
self.upgrade_storage = None;
|
self.upgrade_storage = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn customization_mut(&mut self) -> &mut CustomizationImpl {
|
pub fn customization_mut(&mut self) -> &mut TestCustomization {
|
||||||
&mut self.customization
|
&mut self.customization
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,7 +99,7 @@ impl Env for TestEnv {
|
|||||||
type UpgradeStorage = BufferUpgradeStorage;
|
type UpgradeStorage = BufferUpgradeStorage;
|
||||||
type FirmwareProtection = Self;
|
type FirmwareProtection = Self;
|
||||||
type Write = TestWrite;
|
type Write = TestWrite;
|
||||||
type Customization = CustomizationImpl;
|
type Customization = TestCustomization;
|
||||||
|
|
||||||
fn rng(&mut self) -> &mut Self::Rng {
|
fn rng(&mut self) -> &mut Self::Rng {
|
||||||
&mut self.rng
|
&mut self.rng
|
||||||
|
|||||||
Reference in New Issue
Block a user