From 8d737b3c80a7ce421709ac08b193e02d70a1e1eb Mon Sep 17 00:00:00 2001 From: Fabian Kaczmarczyck Date: Wed, 13 May 2020 18:49:35 +0200 Subject: [PATCH] introduces a default level for credProtect --- README.md | 3 +++ src/ctap/data_formats.rs | 2 +- src/ctap/mod.rs | 35 ++++++++++++++++++++++++----------- src/ctap/response.rs | 10 +++++++++- 4 files changed, 37 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 28093fc..db91f82 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,9 @@ a few things you can personalize: 4. Depending on your available flash storage, choose an appropriate maximum number of supported residential keys and number of pages in `ctap/storage.rs`. +5. Change the default level for the credProtect extension. Resident credentials + become undiscoverable without user verification. This helps privacy, but + can make usage less comfortable for less important credentials. ### 3D printed enclosure diff --git a/src/ctap/data_formats.rs b/src/ctap/data_formats.rs index 538090d..5586252 100644 --- a/src/ctap/data_formats.rs +++ b/src/ctap/data_formats.rs @@ -440,7 +440,7 @@ impl TryFrom<&cbor::Value> for SignatureAlgorithm { } } -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, PartialOrd)] #[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug))] pub enum CredentialProtectionPolicy { UserVerificationOptional = 0x01, diff --git a/src/ctap/mod.rs b/src/ctap/mod.rs index b61c650..aaf6f5d 100644 --- a/src/ctap/mod.rs +++ b/src/ctap/mod.rs @@ -32,9 +32,10 @@ use self::command::{ #[cfg(feature = "with_ctap2_1")] use self::data_formats::AuthenticatorTransport; use self::data_formats::{ - ClientPinSubCommand, CoseKey, GetAssertionHmacSecretInput, PackedAttestationStatement, - PublicKeyCredentialDescriptor, PublicKeyCredentialParameter, PublicKeyCredentialSource, - PublicKeyCredentialType, PublicKeyCredentialUserEntity, SignatureAlgorithm, + ClientPinSubCommand, CoseKey, CredentialProtectionPolicy, GetAssertionHmacSecretInput, + PackedAttestationStatement, PublicKeyCredentialDescriptor, PublicKeyCredentialParameter, + PublicKeyCredentialSource, PublicKeyCredentialType, PublicKeyCredentialUserEntity, + SignatureAlgorithm, }; use self::hid::ChannelID; use self::key_material::{AAGUID, ATTESTATION_CERTIFICATE, ATTESTATION_PRIVATE_KEY}; @@ -109,6 +110,10 @@ pub const ES256_CRED_PARAM: PublicKeyCredentialParameter = PublicKeyCredentialPa cred_type: PublicKeyCredentialType::PublicKey, alg: SignatureAlgorithm::ES256, }; +// You can change this value to one of the following for more privacy. +// - Some(CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList) +// - Some(CredentialProtectionPolicy::UserVerificationRequired) +const DEFAULT_CRED_PROTECT: Option = None; fn check_pin_auth(hmac_key: &[u8], hmac_contents: &[u8], pin_auth: &[u8]) -> bool { if pin_auth.len() != PIN_AUTH_LENGTH { @@ -430,12 +435,19 @@ where } let (use_hmac_extension, cred_protect_policy) = if let Some(extensions) = extensions { - ( - extensions.has_make_credential_hmac_secret()?, - extensions - .make_credential_cred_protect_policy() - .transpose()?, - ) + let mut cred_protect = extensions + .make_credential_cred_protect_policy() + .transpose()?; + if cred_protect + .as_ref() + .unwrap_or(&CredentialProtectionPolicy::UserVerificationOptional) + > DEFAULT_CRED_PROTECT + .as_ref() + .unwrap_or(&CredentialProtectionPolicy::UserVerificationOptional) + { + cred_protect = DEFAULT_CRED_PROTECT; + } + (extensions.has_make_credential_hmac_secret()?, cred_protect) } else { (false, None) }; @@ -812,6 +824,7 @@ where transports: Some(vec![AuthenticatorTransport::Usb]), #[cfg(feature = "with_ctap2_1")] algorithms: Some(vec![ES256_CRED_PARAM]), + default_cred_protect: DEFAULT_CRED_PROTECT, #[cfg(feature = "with_ctap2_1")] firmware_version: None, }, @@ -1116,8 +1129,8 @@ where #[cfg(test)] mod test { use super::data_formats::{ - CredentialProtectionPolicy, Extensions, GetAssertionOptions, MakeCredentialOptions, - PublicKeyCredentialRpEntity, PublicKeyCredentialUserEntity, + Extensions, GetAssertionOptions, MakeCredentialOptions, PublicKeyCredentialRpEntity, + PublicKeyCredentialUserEntity, }; use super::*; use crypto::rng256::ThreadRng256; diff --git a/src/ctap/response.rs b/src/ctap/response.rs index 389b82d..2a33a6d 100644 --- a/src/ctap/response.rs +++ b/src/ctap/response.rs @@ -15,7 +15,7 @@ #[cfg(feature = "with_ctap2_1")] use super::data_formats::{AuthenticatorTransport, PublicKeyCredentialParameter}; use super::data_formats::{ - CoseKey, PackedAttestationStatement, PublicKeyCredentialDescriptor, + CoseKey, CredentialProtectionPolicy, PackedAttestationStatement, PublicKeyCredentialDescriptor, PublicKeyCredentialUserEntity, }; use alloc::collections::BTreeMap; @@ -119,6 +119,7 @@ pub struct AuthenticatorGetInfoResponse { pub transports: Option>, #[cfg(feature = "with_ctap2_1")] pub algorithms: Option>, + pub default_cred_protect: Option, #[cfg(feature = "with_ctap2_1")] pub firmware_version: Option, } @@ -137,6 +138,7 @@ impl From for cbor::Value { max_credential_id_length, transports, algorithms, + default_cred_protect, firmware_version, } = get_info_response; @@ -159,6 +161,7 @@ impl From for cbor::Value { 0x08 => max_credential_id_length, 0x09 => transports.map(|vec| cbor_array_vec!(vec)), 0x0A => algorithms.map(|vec| cbor_array_vec!(vec)), + 0x0C => default_cred_protect.map(|p| p as u64), 0x0E => firmware_version, } } @@ -172,6 +175,7 @@ impl From for cbor::Value { options, max_msg_size, pin_protocols, + default_cred_protect, } = get_info_response; let options_cbor: Option = options.map(|options| { @@ -189,6 +193,7 @@ impl From for cbor::Value { 0x04 => options_cbor, 0x05 => max_msg_size, 0x06 => pin_protocols.map(|vec| cbor_array_vec!(vec)), + 0x0C => default_cred_protect.map(|p| p as u64), } } } @@ -290,6 +295,7 @@ mod test { transports: None, #[cfg(feature = "with_ctap2_1")] algorithms: None, + default_cred_protect: None, #[cfg(feature = "with_ctap2_1")] firmware_version: None, }; @@ -318,6 +324,7 @@ mod test { max_credential_id_length: Some(256), transports: Some(vec![AuthenticatorTransport::Usb]), algorithms: Some(vec![ES256_CRED_PARAM]), + default_cred_protect: Some(CredentialProtectionPolicy::UserVerificationRequired), firmware_version: Some(0), }; let response_cbor: Option = @@ -333,6 +340,7 @@ mod test { 0x08 => 256, 0x09 => cbor_array_vec![vec!["usb"]], 0x0A => cbor_array_vec![vec![ES256_CRED_PARAM]], + 0x0C => CredentialProtectionPolicy::UserVerificationRequired as u64, 0x0E => 0, }; assert_eq!(response_cbor, Some(expected_cbor));