Skip to content

Commit 303ecaa

Browse files
Make maximum resident credential count configurable
Previously, we estimated that we can handle 100 resident keys when returning the number of remaining resident keys in the credential management command. This patch introduces a config option to set a maximum count of resident keys that is used to report the number of remaining resident keys and that is enforced when trying to create a new resident key.
1 parent d3e1753 commit 303ecaa

6 files changed

Lines changed: 25 additions & 13 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## Unreleased
8+
- Add config option for setting a maximum number of resident credentials.
9+
710
## [0.1.1] - 2022-08-22
811
- Fix bug that treated U2F payloads as APDU over APDU in NFC transport @conorpp
912
- Add config option to skip UP when device was just booted,
1013
as insertion is a kind of UP check @robin-nitrokey
1114

12-
## [Unreleased]
15+
## [0.1.0] - 2022-03-17
1316

1417
- use 2021 edition
1518
- use @szszszsz's credential ID shortening

src/constants.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@ pub const U2F_UP_TIMEOUT: u32 = 250;
77

88
pub const ATTESTATION_CERT_ID: CertId = CertId::from_special(0);
99
pub const ATTESTATION_KEY_ID: KeyId = KeyId::from_special(0);
10+
11+
pub const MAX_RESIDENT_CREDENTIALS_GUESSTIMATE: u32 = 100;

src/ctap2.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,16 @@ impl<UP: UserPresence, T: TrussedRequirements> Authenticator for crate::Authenti
375375
self.delete_resident_key_by_user_id(&rp_id_hash, &credential.user.id)
376376
.ok();
377377

378+
// then check the maximum number of RK credentials
379+
if let Some(max_count) = self.config.max_resident_credential_count {
380+
let mut cm = credential_management::CredentialManagement::new(self);
381+
let metadata = cm.get_creds_metadata()?;
382+
let count = metadata.existing_resident_credentials_count.unwrap_or(max_count);
383+
if count >= max_count {
384+
return Err(Error::KeyStoreFull);
385+
}
386+
}
387+
378388
// then store key, making it resident
379389
let credential_id_hash = self.hash(credential_id.0.as_ref());
380390
try_syscall!(self.trussed.write_file(

src/ctap2/credential_management.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use ctap_types::{
1818
use littlefs2::path::{Path, PathBuf};
1919

2020
use crate::{
21+
constants::MAX_RESIDENT_CREDENTIALS_GUESSTIMATE,
2122
credential::Credential,
2223
state::{CredentialManagementEnumerateCredentials, CredentialManagementEnumerateRps},
2324
Authenticator, Result, TrussedRequirements, UserPresence,
@@ -67,9 +68,12 @@ where
6768
info!("get metadata");
6869
let mut response: Response = Default::default();
6970

70-
let guesstimate = self.state.persistent.max_resident_credentials_guesstimate();
71+
let max_resident_credentials = self
72+
.config
73+
.max_resident_credential_count
74+
.unwrap_or(MAX_RESIDENT_CREDENTIALS_GUESSTIMATE);
7175
response.existing_resident_credentials_count = Some(0);
72-
response.max_possible_remaining_residential_credentials_count = Some(guesstimate);
76+
response.max_possible_remaining_residential_credentials_count = Some(max_resident_credentials);
7377

7478
let dir = PathBuf::from(b"rk");
7579
let maybe_first_rp =
@@ -98,11 +102,7 @@ where
98102
None => {
99103
response.existing_resident_credentials_count = Some(num_rks);
100104
response.max_possible_remaining_residential_credentials_count =
101-
Some(if num_rks >= guesstimate {
102-
0
103-
} else {
104-
guesstimate - num_rks
105-
});
105+
Some(max_resident_credentials.saturating_sub(num_rks));
106106
return Ok(response);
107107
}
108108
Some(rp) => {

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ pub struct Config {
7676
/// If set, the first Get Assertion or Authenticate request within the specified time after
7777
/// boot is accepted without additional user presence verification.
7878
pub skip_up_timeout: Option<Duration>,
79+
/// The maximum number of resident credentials.
80+
pub max_resident_credential_count: Option<u32>,
7981
}
8082

8183
// impl Default for Config {

src/state.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -273,11 +273,6 @@ pub struct PersistentState {
273273
impl PersistentState {
274274
const RESET_RETRIES: u8 = 8;
275275
const FILENAME: &'static [u8] = b"persistent-state.cbor";
276-
const MAX_RESIDENT_CREDENTIALS_GUESSTIMATE: u32 = 100;
277-
278-
pub fn max_resident_credentials_guesstimate(&self) -> u32 {
279-
Self::MAX_RESIDENT_CREDENTIALS_GUESSTIMATE
280-
}
281276

282277
pub fn load<T: client::Client + client::Chacha8Poly1305>(trussed: &mut T) -> Result<Self> {
283278
// TODO: add "exists_file" method instead?

0 commit comments

Comments
 (0)