From faca7a059a70aae418fa83aa8dc6309b2a6d0b12 Mon Sep 17 00:00:00 2001 From: Alfie Fresta Date: Sun, 25 May 2025 16:56:56 +0200 Subject: [PATCH 1/4] Adjust CTAP transport casing --- .../ctap2/model/credential_protection.rs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 libwebauthn/src/proto/ctap2/model/credential_protection.rs diff --git a/libwebauthn/src/proto/ctap2/model/credential_protection.rs b/libwebauthn/src/proto/ctap2/model/credential_protection.rs new file mode 100644 index 00000000..d51f1326 --- /dev/null +++ b/libwebauthn/src/proto/ctap2/model/credential_protection.rs @@ -0,0 +1,22 @@ +use minicbor::{Decode, Encode}; + +/// Credential protection policy as defined in the CTAP2 spec. +/// +/// This replaces the ctap_types::ctap2::credential_management::CredentialProtectionPolicy +/// to avoid external dependency. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)] +#[cbor(index_only)] +pub enum CredentialProtectionPolicy { + #[n(1)] + UserVerificationOptional = 0x01, + #[n(2)] + UserVerificationOptionalWithCredentialIdList = 0x02, + #[n(3)] + UserVerificationRequired = 0x03, +} + +impl Default for CredentialProtectionPolicy { + fn default() -> Self { + Self::UserVerificationOptional + } +} From f5770ccc60c6bc48dd7dce4051d2b7504cca91ce Mon Sep 17 00:00:00 2001 From: Alfie Fresta Date: Mon, 26 May 2025 16:54:35 +0200 Subject: [PATCH 2/4] Do not fail on unknown algorithms --- libwebauthn/src/proto/ctap2/model.rs | 52 +++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/libwebauthn/src/proto/ctap2/model.rs b/libwebauthn/src/proto/ctap2/model.rs index a6bfa768..b29ab95f 100644 --- a/libwebauthn/src/proto/ctap2/model.rs +++ b/libwebauthn/src/proto/ctap2/model.rs @@ -116,6 +116,9 @@ impl Ctap2PublicKeyCredentialUserEntity { pub enum Ctap2PublicKeyCredentialType { #[serde(rename = "public-key")] PublicKey, + + #[serde(other)] + Unknown, } #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] @@ -154,9 +157,11 @@ pub enum Ctap2COSEAlgorithmIdentifier { ES256 = -7, EDDSA = -8, TOPT = -9, + #[serde(other)] + Unknown = -999, } -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)] pub struct Ctap2CredentialType { #[serde(rename = "alg")] pub algorithm: Ctap2COSEAlgorithmIdentifier, @@ -184,6 +189,11 @@ impl Ctap2CredentialType { algorithm, } } + + pub fn is_known(&self) -> bool { + self.algorithm != Ctap2COSEAlgorithmIdentifier::Unknown + && self.public_key_type != Ctap2PublicKeyCredentialType::Unknown + } } pub trait Ctap2UserVerifiableRequest { @@ -243,4 +253,44 @@ mod tests { let expected = hex::decode("a2626964414264747970656a7075626c69632d6b6579").unwrap(); assert_eq!(serialized, expected); } + + #[test] + pub fn deserialize_known_credential_type() { + // python $ cbor2.dumps({"alg":-7,"type":"public-key"}).hex() + let serialized: Vec = + hex::decode("a263616c672664747970656a7075626c69632d6b6579").unwrap(); + let credential_type: Ctap2CredentialType = serde_cbor::from_slice(&serialized).unwrap(); + assert_eq!( + credential_type, + Ctap2CredentialType { + algorithm: Ctap2COSEAlgorithmIdentifier::ES256, + public_key_type: Ctap2PublicKeyCredentialType::PublicKey, + } + ); + assert!(credential_type.is_known()); + } + + #[test] + pub fn deserialize_unknown_credential_type_algorithm() { + // python $ cbor2.dumps({"alg":-42,"type":"public-key"}).hex() + let serialized: Vec = + hex::decode("a263616c67382964747970656a7075626c69632d6b6579").unwrap(); + let credential_type: Ctap2CredentialType = serde_cbor::from_slice(&serialized).unwrap(); + assert_eq!( + credential_type, + Ctap2CredentialType { + algorithm: Ctap2COSEAlgorithmIdentifier::Unknown, + public_key_type: Ctap2PublicKeyCredentialType::PublicKey, + } + ); + assert!(!credential_type.is_known()); + } + + #[test] + pub fn deerialize_unknown_credential_type() { + // python $ cbor2.dumps({"alg":-7,"type":"unknown"}).hex() + let serialized: Vec = hex::decode("a263616c6726647479706567756e6b6e6f776e").unwrap(); + let credential_type: Ctap2CredentialType = serde_cbor::from_slice(&serialized).unwrap(); + assert!(!credential_type.is_known()); + } } From 255e60c8dd63c06a9f5642e043341794262c45e7 Mon Sep 17 00:00:00 2001 From: Alfie Fresta Date: Wed, 28 May 2025 23:26:47 +0200 Subject: [PATCH 3/4] Remove accidental file --- .../ctap2/model/credential_protection.rs | 22 ------------------- 1 file changed, 22 deletions(-) delete mode 100644 libwebauthn/src/proto/ctap2/model/credential_protection.rs diff --git a/libwebauthn/src/proto/ctap2/model/credential_protection.rs b/libwebauthn/src/proto/ctap2/model/credential_protection.rs deleted file mode 100644 index d51f1326..00000000 --- a/libwebauthn/src/proto/ctap2/model/credential_protection.rs +++ /dev/null @@ -1,22 +0,0 @@ -use minicbor::{Decode, Encode}; - -/// Credential protection policy as defined in the CTAP2 spec. -/// -/// This replaces the ctap_types::ctap2::credential_management::CredentialProtectionPolicy -/// to avoid external dependency. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)] -#[cbor(index_only)] -pub enum CredentialProtectionPolicy { - #[n(1)] - UserVerificationOptional = 0x01, - #[n(2)] - UserVerificationOptionalWithCredentialIdList = 0x02, - #[n(3)] - UserVerificationRequired = 0x03, -} - -impl Default for CredentialProtectionPolicy { - fn default() -> Self { - Self::UserVerificationOptional - } -} From 0c8f856bd754995f132f483db5dc73e7efc16e68 Mon Sep 17 00:00:00 2001 From: Alfie Fresta Date: Wed, 28 May 2025 23:27:12 +0200 Subject: [PATCH 4/4] Fix typo in test name --- libwebauthn/src/proto/ctap2/model.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libwebauthn/src/proto/ctap2/model.rs b/libwebauthn/src/proto/ctap2/model.rs index b29ab95f..27151821 100644 --- a/libwebauthn/src/proto/ctap2/model.rs +++ b/libwebauthn/src/proto/ctap2/model.rs @@ -287,7 +287,7 @@ mod tests { } #[test] - pub fn deerialize_unknown_credential_type() { + pub fn deserialize_unknown_credential_type() { // python $ cbor2.dumps({"alg":-7,"type":"unknown"}).hex() let serialized: Vec = hex::decode("a263616c6726647479706567756e6b6e6f776e").unwrap(); let credential_type: Ctap2CredentialType = serde_cbor::from_slice(&serialized).unwrap();