diff --git a/src/api/customization.rs b/src/api/customization.rs index 3531002..dd45018 100644 --- a/src/api/customization.rs +++ b/src/api/customization.rs @@ -26,6 +26,12 @@ pub trait Customization { // Constants for adjusting privacy and protection levels. // ########################################################################### + /// Removes support for PIN protocol v1. + /// + /// We support PIN protocol v2, "intended to aid FIPS certification". + /// To certify, you might want to remove support for v1 using this customization. + fn allows_pin_protocol_v1(&self) -> bool; + /// Changes the default level for the credProtect extension. /// /// You can change this value to one of the following for more privacy: @@ -245,6 +251,7 @@ pub trait Customization { #[derive(Clone)] pub struct CustomizationImpl { + pub allows_pin_protocol_v1: bool, pub default_cred_protect: Option, pub default_min_pin_length: u8, pub default_min_pin_length_rp_ids: &'static [&'static str], @@ -263,6 +270,7 @@ pub struct CustomizationImpl { } pub const DEFAULT_CUSTOMIZATION: CustomizationImpl = CustomizationImpl { + allows_pin_protocol_v1: true, default_cred_protect: None, default_min_pin_length: 4, default_min_pin_length_rp_ids: &[], @@ -281,6 +289,10 @@ pub const DEFAULT_CUSTOMIZATION: CustomizationImpl = CustomizationImpl { }; impl Customization for CustomizationImpl { + fn allows_pin_protocol_v1(&self) -> bool { + self.allows_pin_protocol_v1 + } + fn default_cred_protect(&self) -> Option { self.default_cred_protect } diff --git a/src/ctap/client_pin.rs b/src/ctap/client_pin.rs index e7b6361..12d5b64 100644 --- a/src/ctap/client_pin.rs +++ b/src/ctap/client_pin.rs @@ -21,6 +21,7 @@ use super::pin_protocol::{verify_pin_uv_auth_token, PinProtocol, SharedSecret}; use super::response::{AuthenticatorClientPinResponse, ResponseData}; use super::status_code::Ctap2StatusCode; use super::token_state::PinUvAuthTokenState; +use crate::api::customization::Customization; use crate::ctap::storage; use crate::env::Env; use alloc::boxed::Box; @@ -390,6 +391,11 @@ impl ClientPin { client_pin_params: AuthenticatorClientPinParameters, now: CtapInstant, ) -> Result { + if !env.customization().allows_pin_protocol_v1() + && client_pin_params.pin_uv_auth_protocol == PinUvAuthProtocol::V1 + { + return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER); + } let response = match client_pin_params.sub_command { ClientPinSubCommand::GetPinRetries => Some(self.process_get_pin_retries(env)?), ClientPinSubCommand::GetKeyAgreement => { @@ -872,6 +878,20 @@ mod test { test_helper_process_get_key_agreement(PinUvAuthProtocol::V2); } + #[test] + fn test_process_get_key_agreement_v1_not_allowed() { + let (mut client_pin, params) = create_client_pin_and_parameters( + PinUvAuthProtocol::V1, + ClientPinSubCommand::GetKeyAgreement, + ); + let mut env = TestEnv::new(); + env.customization_mut().set_allows_pin_protocol_v1(false); + assert_eq!( + client_pin.process_command(&mut env, params, CtapInstant::new(0)), + Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER) + ); + } + fn test_helper_process_set_pin(pin_uv_auth_protocol: PinUvAuthProtocol) { let (mut client_pin, params) = create_client_pin_and_parameters(pin_uv_auth_protocol, ClientPinSubCommand::SetPin); diff --git a/src/ctap/mod.rs b/src/ctap/mod.rs index 1f3bdeb..156ac12 100644 --- a/src/ctap/mod.rs +++ b/src/ctap/mod.rs @@ -1264,6 +1264,10 @@ impl CtapState { (String::from("setMinPINLength"), true), (String::from("makeCredUvNotRqd"), !has_always_uv), ]); + let mut pin_protocols = vec![PinUvAuthProtocol::V2 as u64]; + if env.customization().allows_pin_protocol_v1() { + pin_protocols.push(PinUvAuthProtocol::V1 as u64); + } Ok(ResponseData::AuthenticatorGetInfo( AuthenticatorGetInfoResponse { @@ -1279,10 +1283,7 @@ impl CtapState { options: Some(options), max_msg_size: Some(env.customization().max_msg_size() as u64), // The order implies preference. We favor the new V2. - pin_protocols: Some(vec![ - PinUvAuthProtocol::V2 as u64, - PinUvAuthProtocol::V1 as u64, - ]), + pin_protocols: Some(pin_protocols), max_credential_count_in_list: env .customization() .max_credential_count_in_list() @@ -1581,6 +1582,23 @@ mod test { assert_eq!(info_reponse, response_cbor); } + #[test] + fn test_get_info_no_pin_protocol_v1() { + let mut env = TestEnv::new(); + env.customization_mut().set_allows_pin_protocol_v1(false); + let ctap_state = CtapState::new(&mut env, CtapInstant::new(0)); + let info_response = ctap_state.process_get_info(&mut env).unwrap(); + match info_response { + ResponseData::AuthenticatorGetInfo(response) => { + assert_eq!( + response.pin_protocols, + Some(vec![PinUvAuthProtocol::V2 as u64]) + ); + } + _ => panic!("Invalid response type"), + } + } + fn create_minimal_make_credential_parameters() -> AuthenticatorMakeCredentialParameters { let client_data_hash = vec![0xCD]; let rp = PublicKeyCredentialRpEntity { diff --git a/src/env/test/customization.rs b/src/env/test/customization.rs index e6fe45e..642d729 100644 --- a/src/env/test/customization.rs +++ b/src/env/test/customization.rs @@ -18,6 +18,7 @@ use alloc::string::String; use alloc::vec::Vec; pub struct TestCustomization { + allows_pin_protocol_v1: bool, default_cred_protect: Option, default_min_pin_length: u8, default_min_pin_length_rp_ids: Vec, @@ -36,6 +37,10 @@ pub struct TestCustomization { } impl TestCustomization { + pub fn set_allows_pin_protocol_v1(&mut self, is_allowed: bool) { + self.allows_pin_protocol_v1 = is_allowed; + } + pub fn setup_enterprise_attestation( &mut self, mode: Option, @@ -49,6 +54,10 @@ impl TestCustomization { } impl Customization for TestCustomization { + fn allows_pin_protocol_v1(&self) -> bool { + self.allows_pin_protocol_v1 + } + fn default_cred_protect(&self) -> Option { self.default_cred_protect } @@ -117,6 +126,7 @@ impl Customization for TestCustomization { impl From for TestCustomization { fn from(c: CustomizationImpl) -> Self { let CustomizationImpl { + allows_pin_protocol_v1, default_cred_protect, default_min_pin_length, default_min_pin_length_rp_ids, @@ -145,6 +155,7 @@ impl From for TestCustomization { .collect::>(); Self { + allows_pin_protocol_v1, default_cred_protect, default_min_pin_length, default_min_pin_length_rp_ids,