diff --git a/CHANGELOG.md b/CHANGELOG.md index e826f7e..7ece47c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,12 +4,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased +- Add config option for setting a maximum number of resident credentials. + ## [0.1.1] - 2022-08-22 - Fix bug that treated U2F payloads as APDU over APDU in NFC transport @conorpp - Add config option to skip UP when device was just booted, as insertion is a kind of UP check @robin-nitrokey -## [Unreleased] +## [0.1.0] - 2022-03-17 - use 2021 edition - use @szszszsz's credential ID shortening diff --git a/src/constants.rs b/src/constants.rs index 43e88da..dbab710 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -7,3 +7,5 @@ pub const U2F_UP_TIMEOUT: u32 = 250; pub const ATTESTATION_CERT_ID: CertId = CertId::from_special(0); pub const ATTESTATION_KEY_ID: KeyId = KeyId::from_special(0); + +pub const MAX_RESIDENT_CREDENTIALS_GUESSTIMATE: u32 = 100; diff --git a/src/ctap2.rs b/src/ctap2.rs index 789ca04..ea1ad40 100644 --- a/src/ctap2.rs +++ b/src/ctap2.rs @@ -372,6 +372,16 @@ impl Authenticator for crate::Authenti self.delete_resident_key_by_user_id(&rp_id_hash, &credential.user.id) .ok(); + // then check the maximum number of RK credentials + if let Some(max_count) = self.config.max_resident_credential_count { + let mut cm = credential_management::CredentialManagement::new(self); + let metadata = cm.get_creds_metadata()?; + let count = metadata.existing_resident_credentials_count.unwrap_or(max_count); + if count >= max_count { + return Err(Error::KeyStoreFull); + } + } + // then store key, making it resident let credential_id_hash = self.hash(credential_id.0.as_ref()); try_syscall!(self.trussed.write_file( diff --git a/src/ctap2/credential_management.rs b/src/ctap2/credential_management.rs index ed6f75e..39eaaf8 100644 --- a/src/ctap2/credential_management.rs +++ b/src/ctap2/credential_management.rs @@ -16,6 +16,7 @@ use ctap_types::{ }; use crate::{ + constants::MAX_RESIDENT_CREDENTIALS_GUESSTIMATE, credential::Credential, state::{CredentialManagementEnumerateCredentials, CredentialManagementEnumerateRps}, Authenticator, Result, TrussedRequirements, UserPresence, @@ -65,9 +66,12 @@ where info!("get metadata"); let mut response: Response = Default::default(); - let guesstimate = self.state.persistent.max_resident_credentials_guesstimate(); + let max_resident_credentials = self + .config + .max_resident_credential_count + .unwrap_or(MAX_RESIDENT_CREDENTIALS_GUESSTIMATE); response.existing_resident_credentials_count = Some(0); - response.max_possible_remaining_residential_credentials_count = Some(guesstimate); + response.max_possible_remaining_residential_credentials_count = Some(max_resident_credentials); let dir = PathBuf::from(b"rk"); let maybe_first_rp = @@ -96,11 +100,7 @@ where None => { response.existing_resident_credentials_count = Some(num_rks); response.max_possible_remaining_residential_credentials_count = - Some(if num_rks >= guesstimate { - 0 - } else { - guesstimate - num_rks - }); + Some(max_resident_credentials.saturating_sub(num_rks)); return Ok(response); } Some(rp) => { diff --git a/src/lib.rs b/src/lib.rs index e1f776b..5cd444a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,6 +76,8 @@ pub struct Config { /// If set, the first Get Assertion or Authenticate request within the specified time after /// boot is accepted without additional user presence verification. pub skip_up_timeout: Option, + /// The maximum number of resident credentials. + pub max_resident_credential_count: Option, } // impl Default for Config { diff --git a/src/state.rs b/src/state.rs index 20eaeef..c7a0f7a 100644 --- a/src/state.rs +++ b/src/state.rs @@ -272,11 +272,6 @@ pub struct PersistentState { impl PersistentState { const RESET_RETRIES: u8 = 8; const FILENAME: &'static [u8] = b"persistent-state.cbor"; - const MAX_RESIDENT_CREDENTIALS_GUESSTIMATE: u32 = 100; - - pub fn max_resident_credentials_guesstimate(&self) -> u32 { - Self::MAX_RESIDENT_CREDENTIALS_GUESSTIMATE - } pub fn load(trussed: &mut T) -> Result { // TODO: add "exists_file" method instead?