fix(ctap2): store credential public keys as opaque COSE bytes#229
Merged
Conversation
msirringhaus
approved these changes
May 27, 2026
Collaborator
msirringhaus
left a comment
There was a problem hiding this comment.
lgtm. One comment update would be nice, though.
| ) -> Result<Self::IdlModel, ResponseSerializationError> { | ||
| // Get credential ID from attested credential data | ||
| let credential_id_bytes = self | ||
| // The AT flag MUST be set on makeCredential responses per CTAP §6.1. |
Collaborator
There was a problem hiding this comment.
Either provide a link or specify which CTAP-version is meant here, because §6 is something different for each release. A direct quote would probably also be nice, because I couldn't find it right away.
cosey::PublicKey is a closed enum of P-256, Ed25519 and ECDH-ES+HKDF-256. Any credential using RSA, P-384, P-521, secp256k1 or a post-quantum algorithm cannot be deserialised at all. The platform does not crypto-validate credential public keys, so it now stores them as raw COSE_Key bytes and forwards them to the relying party verbatim. The authData deserialiser captures the COSE_Key span through the CBOR cursor so the attestation signature over those bytes stays valid for RP verification. A new internal cose::read_alg helper reads the mandatory `alg` parameter per WebAuthn L3 §6.5.2, which is how the make_credential response now populates getPublicKeyAlgorithm(). The previous ES256 fallback for missing attested credential data is removed, since the AT flag MUST be set per CTAP §6.1. cosey stays in use for the ECDH-ES P-256 key agreement during PIN/UV protocols, where the COSE shape is fixed by spec.
0ac08f3 to
1c17ed4
Compare
AlfioEmanueleFresta
added a commit
that referenced
this pull request
May 28, 2026
WebAuthn L3 §5.2.1.1 says `AuthenticatorAttestationResponse.getPublicKey()` returns DER-encoded SubjectPublicKeyInfo for credentials using ES256, EdDSA Ed25519 and RS256, and null for any algorithm the user agent does not implement. libwebauthn was emitting raw COSE bytes there instead, which relying parties could not feed into `SubtleCrypto` or standard X.509 parsers. A new converter produces SPKI for the WebAuthn L3 floor plus ESP256, which is equivalent to ES256 per RFC 9864. Algorithms outside the supported set return null per spec. Malformed keys for understood algorithms surface as an error. The conversion uses the `spki` and `der` crates from RustCrypto, both already in the transitive dependency tree via `p256`, so no new external runtime crates land. Adding new algorithm support later is a small per-algorithm addition. Stacked on top of #229.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The credential public key returned by an authenticator was being stored in a closed Rust enum that only handled P-256 and Ed25519. Any credential using RSA, P-384, secp256k1 or a post-quantum algorithm failed to deserialise, so the whole registration response was rejected.
Credential public keys are now stored as raw COSE bytes. The platform doesn't crypto-validate them, it forwards them to the relying party, so the closed enum was over-modelling. The authenticator data parser captures the COSE_Key bytes verbatim, preserving the attestation signature's bit-identity for relying-party verification.
A small helper reads the mandatory
algparameter per WebAuthn L3 §6.5.2, which is what populatespublicKeyAlgorithmin the JSON response. The previous ES256 fallback when attested credential data was missing is removed because the AT flag MUST be set per CTAP §6.1, and a silent fallback was hiding real protocol violations.The closed enum is still used for the ECDH-ES P-256 key agreement during PIN/UV protocols, where the COSE shape is fixed by spec.
Stacked on top of #228.