From f058bb353c96bb08fb2e60f669107b9b366225b8 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Fri, 22 May 2026 15:00:19 +0200 Subject: [PATCH] get_assertion: Load full credential if allowList is passed Normally, we load discoverable credentials from the filesystem so we have access to all metadata. But if an allowList is passed, we currently just use the metadata from the credential ID. To be able to access the full metadata like the credBlob value, we have to load the full credential from the filessytem. --- CHANGELOG.md | 1 + src/ctap2.rs | 25 ++++++++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab15e6f..037dd42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Update to `ctap-types` v0.6.0-rc.1. - Set `algorithms`, `firmware_version` and `remaining_discoverable_credentials` in `get_info` and add `firmware_version` to `Config`. - Implement the `credBlob` extension. +- Load full credential from filesstem for getAssertion if an allow list is used with a discoverable credential. ## [v0.3.0](https://github.com/trussed-dev/fido-authenticator/releases/tag/v0.3.0) (2026-03-25) diff --git a/src/ctap2.rs b/src/ctap2.rs index bbc74af..183d9bd 100644 --- a/src/ctap2.rs +++ b/src/ctap2.rs @@ -1160,7 +1160,8 @@ impl crate::Authenticator { // they probably meant to send None. if !allow_list.is_empty() { for credential_id in allow_list { - let credential = match Credential::try_from(self, rp_id_hash, credential_id) { + let mut credential = match Credential::try_from(self, rp_id_hash, credential_id) + { Ok(credential) => credential, _ => continue, }; @@ -1169,6 +1170,28 @@ impl crate::Authenticator { continue; } + // If this is an RK, we still need to load it from the filesystem to have access + // to all metadata + if let Credential::Stripped(stripped) = &credential { + if matches!(stripped.key, Key::ResidentKey(_)) { + let credential_id_hash = self.hash(credential_id.id); + let rk_path = rk_path(rp_id_hash, &credential_id_hash); + let credential_data = match try_syscall!(self + .trussed + .read_file(Location::Internal, rk_path)) + { + Ok(reply) => reply.data, + Err(_) => continue, + }; + match FullCredential::deserialize(&credential_data) { + Ok(full_credential) => { + credential = Credential::Full(full_credential); + } + Err(_) => continue, + } + } + } + return Ok(Some((credential, 1))); }