From f4981f2cd5b3e8ecb3437147c5e793a78d314b4e Mon Sep 17 00:00:00 2001 From: Dennis Fokin Date: Mon, 2 Jun 2025 12:05:36 +0200 Subject: [PATCH 1/8] Adjust parsing logic --- .../fido/metadata/SupportedCtapOptions.java | 47 +++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/SupportedCtapOptions.java b/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/SupportedCtapOptions.java index cda8898e9..e5d6eed5a 100644 --- a/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/SupportedCtapOptions.java +++ b/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/SupportedCtapOptions.java @@ -1,8 +1,8 @@ package com.yubico.fido.metadata; import com.fasterxml.jackson.annotation.JsonAlias; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Builder; import lombok.Value; import lombok.extern.jackson.Jacksonized; @@ -18,7 +18,6 @@ @Value @Builder @Jacksonized -@AllArgsConstructor(access = AccessLevel.PRIVATE) public class SupportedCtapOptions { /** @@ -157,4 +156,46 @@ public class SupportedCtapOptions { * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ @Builder.Default boolean alwaysUv = false; + + @JsonCreator + private SupportedCtapOptions( + @JsonProperty("plat") Boolean plat, + @JsonProperty("rk") Boolean rk, + @JsonProperty("clientPin") Boolean clientPin, + @JsonProperty("up") Boolean up, + @JsonProperty("uv") Boolean uv, + @JsonProperty("pinUvAuthToken") Boolean pinUvAuthToken, + @JsonProperty("noMcGaPermissionsWithClientPin") Boolean noMcGaPermissionsWithClientPin, + @JsonProperty("largeBlobs") Boolean largeBlobs, + @JsonProperty("ep") Boolean ep, + @JsonProperty("bioEnroll") Boolean bioEnroll, + @JsonProperty("userVerificationMgmtPreview") Boolean userVerificationMgmtPreview, + @JsonProperty("uvBioEnroll") Boolean uvBioEnroll, + @JsonProperty("authnrCfg") Boolean authnrCfg, + @JsonProperty("uvAcfg") Boolean uvAcfg, + @JsonProperty("credMgmt") Boolean credMgmt, + @JsonProperty("credentialMgmtPreview") Boolean credentialMgmtPreview, + @JsonProperty("setMinPINLength") Boolean setMinPINLength, + @JsonProperty("makeCredUvNotRqd") Boolean makeCredUvNotRqd, + @JsonProperty("alwaysUv") Boolean alwaysUv) { + this.plat = plat; + this.rk = rk; + this.clientPin = clientPin != null; + this.up = up; + this.uv = uv != null; + this.pinUvAuthToken = Boolean.TRUE.equals(pinUvAuthToken); + this.noMcGaPermissionsWithClientPin = Boolean.TRUE.equals(noMcGaPermissionsWithClientPin); + this.largeBlobs = Boolean.TRUE.equals(largeBlobs); + this.ep = ep != null; + this.bioEnroll = bioEnroll != null; + this.userVerificationMgmtPreview = userVerificationMgmtPreview != null; + this.uvBioEnroll = Boolean.TRUE.equals(uvBioEnroll); + this.authnrCfg = Boolean.TRUE.equals(authnrCfg); + this.uvAcfg = Boolean.TRUE.equals(uvAcfg); + this.credMgmt = Boolean.TRUE.equals(credMgmt); + this.credentialMgmtPreview = Boolean.TRUE.equals(credentialMgmtPreview); + this.setMinPINLength = Boolean.TRUE.equals(setMinPINLength); + this.makeCredUvNotRqd = Boolean.TRUE.equals(makeCredUvNotRqd); + this.alwaysUv = alwaysUv != null; + } } From 77a8af8a6e045eee67a798eaa7a4bc12c1c2a210 Mon Sep 17 00:00:00 2001 From: Emil Lundberg Date: Tue, 3 Jun 2025 16:46:25 +0200 Subject: [PATCH 2/8] Add test of SupportedCtapOptions serialization stability This test will be needed later for a more focused view on why the sibling test of `MetadataBLOBPayload` serialization stability begins to fail. --- .../fido/metadata/MetadataBlobSpec.scala | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/webauthn-server-attestation/src/test/scala/com/yubico/fido/metadata/MetadataBlobSpec.scala b/webauthn-server-attestation/src/test/scala/com/yubico/fido/metadata/MetadataBlobSpec.scala index ed0d126a9..ea389a4e2 100644 --- a/webauthn-server-attestation/src/test/scala/com/yubico/fido/metadata/MetadataBlobSpec.scala +++ b/webauthn-server-attestation/src/test/scala/com/yubico/fido/metadata/MetadataBlobSpec.scala @@ -1,11 +1,17 @@ package com.yubico.fido.metadata +import com.yubico.fido.metadata.Generators.arbitrarySupportedCtapOptions import com.yubico.internal.util.JacksonCodecs import com.yubico.webauthn.data.ByteArray +import org.scalacheck.Arbitrary +import org.scalacheck.Gen import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers import org.scalatestplus.scalacheck.ScalaCheckDrivenPropertyChecks +import scala.jdk.CollectionConverters.SetHasAsScala +import scala.jdk.OptionConverters.RichOptional + class MetadataBlobSpec extends AnyFunSpec with Matchers @@ -50,4 +56,34 @@ class MetadataBlobSpec } } + describe("SupportedCtapOptions") { + it( + "are structurally identical after multiple (de)serialization round-trips." + ) { + val json = JacksonCodecs.json() + val blob = json + .readValue( + ByteArray + .fromBase64Url(FidoMds3Examples.BlobPayloadBase64url) + .getBytes, + classOf[MetadataBLOBPayload], + ) + val blobOptions = blob.getEntries.asScala + .flatMap(entry => entry.getMetadataStatement.toScala) + .flatMap(statement => statement.getAuthenticatorGetInfo.toScala) + .flatMap(info => info.getOptions.toScala) + forAll(Gen.oneOf(Arbitrary.arbitrary, Gen.oneOf(blobOptions))) { + (options1: SupportedCtapOptions) => + val encoded1 = json.writeValueAsBytes(options1) + val options2 = json.readValue(encoded1, classOf[SupportedCtapOptions]) + val encoded2 = json.writeValueAsBytes(options2) + val options3 = json.readValue(encoded2, classOf[SupportedCtapOptions]) + + options2 should not be null + options2 should equal(options1) + options3 should not be null + options3 should equal(options1) + } + } + } } From 5bceb93bbcb68ae776aad3c7eafbc9ee2ed54dde Mon Sep 17 00:00:00 2001 From: Emil Lundberg Date: Tue, 3 Jun 2025 16:45:20 +0200 Subject: [PATCH 3/8] Fix parsing SupportedCtapOptions from empty JSON object This commit was constructed through a series of steps: Step 1: Add the new test "SupportedCtapOptions can be parsed from an empty JSON object". It initially fails with: ``` true was not false ScalaTestFailureLocation: com.yubico.fido.metadata.MetadataBlobSpec at (MetadataBlobSpec.scala:68) org.scalatest.exceptions.TestFailedException: true was not false at org.scalatest.matchers.MatchersHelper$.indicateFailure(MatchersHelper.scala:392) at org.scalatest.matchers.should.Matchers$ShouldMethodHelperClass.shouldMatcher(Matchers.scala:7304) at org.scalatest.matchers.should.Matchers$AnyShouldWrapper.should(Matchers.scala:7347) at com.yubico.fido.metadata.MetadataBlobSpec.$anonfun$new$5(MetadataBlobSpec.scala:68) ``` At the line: ``` options.isUv should be(false) ``` This is because `SupportedCtapOptions` is still annotated with `@Jacksonized`, so Jackson uses the builder to construct the object, and the class has ``` @Builder.Default boolean uv = false; ``` so the builder initializes the builder field to the primitive `false` value, which causes the constructor argument to be `Boolean.FALSE`, which is not null, and therefore the constructor sets `this.uv` to `true`. Step 2: Remove `@Jacksonized` from `SupportedCtapOptions`. This causes Jackson to invoke the constructor directly instead of going through the builder, which bypasses the builder field defaults. The test "SupportedCtapOptions can be parsed from an empty JSON object" now fails with a different cause: ``` Cannot construct instance of `com.yubico.fido.metadata.SupportedCtapOptions`, problem: `java.lang.NullPointerException` at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 2] com.fasterxml.jackson.databind.exc.ValueInstantiationException: Cannot construct instance of `com.yubico.fido.metadata.SupportedCtapOptions`, problem: `java.lang.NullPointerException` at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 2] at com.fasterxml.jackson.databind.exc.ValueInstantiationException.from(ValueInstantiationException.java:46) [...] Caused by: java.lang.NullPointerException at com.yubico.fido.metadata.SupportedCtapOptions.(SupportedCtapOptions.java:180) [...] ``` which is the line: ``` this.plat = plat; ``` The test "FIDO Metadata Service 3 blob payloads are structurally identical after multiple (de)serialization round-trips." also begins failing now. Step 3: Use `Boolean.TRUE.equals(...)` to fix `NullPointerException`s while setting `plat`, `rk` and `up` in `SupportedCtapOptions` constructor. The test "SupportedCtapOptions can be parsed from an empty JSON object" now passes. Instead the test "FIDO Metadata Service 3 blob payloads are structurally identical after multiple (de)serialization round-trips" now fails. This is why we added the test in the previous commit, to get a more focused view of this failure. This failure is: ``` TestFailedException was thrown during property evaluation. Message: SupportedCtapOptions(plat=false, rk=true, clientPin=true, up=true, uv=true, pinUvAuthToken=false, noMcGaPermissionsWithClientPin=false, largeBlobs=false, ep=true, bioEnroll=true, userVerificationMgmtPreview=true, uvBioEnroll=false, authnrCfg=false, uvAcfg=false, credMgmt=false, credentialMgmtPreview=false, setMinPINLength=false, makeCredUvNotRqd=false, alwaysUv=true) did not equal SupportedCtapOptions(plat=false, rk=true, clientPin=true, up=true, uv=true, pinUvAuthToken=false, noMcGaPermissionsWithClientPin=false, largeBlobs=false, ep=false, bioEnroll=false, userVerificationMgmtPreview=false, uvBioEnroll=false, authnrCfg=false, uvAcfg=false, credMgmt=false, credentialMgmtPreview=false, setMinPINLength=false, makeCredUvNotRqd=false, alwaysUv=false) Location: (MetadataBlobSpec.scala:108) Occurred when passed generated values ( arg0 = SupportedCtapOptions(plat=false, rk=true, clientPin=true, up=true, uv=true, pinUvAuthToken=false, noMcGaPermissionsWithClientPin=false, largeBlobs=false, ep=false, bioEnroll=false, userVerificationMgmtPreview=false, uvBioEnroll=false, authnrCfg=false, uvAcfg=false, credMgmt=false, credentialMgmtPreview=false, setMinPINLength=false, makeCredUvNotRqd=false, alwaysUv=false) ) ``` This is because the constructor sets some of the the tri-state inputs to `true` when non-null, including when `false`. This means that when the input is absent (null), it is set to `false` and then emitted as `false` when serialized back to JSON, which is then interpreted as non-null and converted to `true` when deserializing again. Step 4: Add `@JsonInclude` directives to omit the tri-state fields that are true-when-non-null when their value is false. All tests now pass! --- .../fido/metadata/SupportedCtapOptions.java | 37 +++++++++++++------ .../fido/metadata/MetadataBlobSpec.scala | 25 +++++++++++++ 2 files changed, 50 insertions(+), 12 deletions(-) diff --git a/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/SupportedCtapOptions.java b/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/SupportedCtapOptions.java index e5d6eed5a..7906c152b 100644 --- a/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/SupportedCtapOptions.java +++ b/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/SupportedCtapOptions.java @@ -2,10 +2,10 @@ import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Builder; import lombok.Value; -import lombok.extern.jackson.Jacksonized; /** * A fixed-keys map of CTAP2 option names to Boolean values representing whether an authenticator @@ -17,7 +17,6 @@ */ @Value @Builder -@Jacksonized public class SupportedCtapOptions { /** @@ -39,7 +38,9 @@ public class SupportedCtapOptions { * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ - @Builder.Default boolean clientPin = false; + @JsonInclude(JsonInclude.Include.NON_DEFAULT) + @Builder.Default + boolean clientPin = false; /** * @see Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ - @Builder.Default boolean uv = false; + @JsonInclude(JsonInclude.Include.NON_DEFAULT) + @Builder.Default + boolean uv = false; /** * @see Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ - @Builder.Default boolean ep = false; + @JsonInclude(JsonInclude.Include.NON_DEFAULT) + @Builder.Default + boolean ep = false; /** * @see Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ - @Builder.Default boolean bioEnroll = false; + @JsonInclude(JsonInclude.Include.NON_DEFAULT) + @Builder.Default + boolean bioEnroll = false; /** * @see Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ - @Builder.Default boolean userVerificationMgmtPreview = false; + @JsonInclude(JsonInclude.Include.NON_DEFAULT) + @Builder.Default + boolean userVerificationMgmtPreview = false; /** * @see Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ - @Builder.Default boolean credentialMgmtPreview = false; + @JsonInclude(JsonInclude.Include.NON_DEFAULT) + @Builder.Default + boolean credentialMgmtPreview = false; /** * @see Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ - @Builder.Default boolean alwaysUv = false; + @JsonInclude(JsonInclude.Include.NON_DEFAULT) + @Builder.Default + boolean alwaysUv = false; @JsonCreator private SupportedCtapOptions( @@ -178,10 +191,10 @@ private SupportedCtapOptions( @JsonProperty("setMinPINLength") Boolean setMinPINLength, @JsonProperty("makeCredUvNotRqd") Boolean makeCredUvNotRqd, @JsonProperty("alwaysUv") Boolean alwaysUv) { - this.plat = plat; - this.rk = rk; + this.plat = Boolean.TRUE.equals(plat); + this.rk = Boolean.TRUE.equals(rk); this.clientPin = clientPin != null; - this.up = up; + this.up = Boolean.TRUE.equals(up); this.uv = uv != null; this.pinUvAuthToken = Boolean.TRUE.equals(pinUvAuthToken); this.noMcGaPermissionsWithClientPin = Boolean.TRUE.equals(noMcGaPermissionsWithClientPin); diff --git a/webauthn-server-attestation/src/test/scala/com/yubico/fido/metadata/MetadataBlobSpec.scala b/webauthn-server-attestation/src/test/scala/com/yubico/fido/metadata/MetadataBlobSpec.scala index ea389a4e2..ecae4ff50 100644 --- a/webauthn-server-attestation/src/test/scala/com/yubico/fido/metadata/MetadataBlobSpec.scala +++ b/webauthn-server-attestation/src/test/scala/com/yubico/fido/metadata/MetadataBlobSpec.scala @@ -57,6 +57,31 @@ class MetadataBlobSpec } describe("SupportedCtapOptions") { + it("can be parsed from an empty JSON object.") { + val options = JacksonCodecs + .json() + .readValue("{}", classOf[SupportedCtapOptions]) + options should not be null + options.isPlat should be(false) + options.isRk should be(false) + options.isUp should be(false) + options.isUv should be(false) + options.isPinUvAuthToken should be(false) + options.isNoMcGaPermissionsWithClientPin should be(false) + options.isLargeBlobs should be(false) + options.isEp should be(false) + options.isBioEnroll should be(false) + options.isUserVerificationMgmtPreview should be(false) + options.isUvBioEnroll should be(false) + options.isAuthnrCfg should be(false) + options.isUvAcfg should be(false) + options.isCredMgmt should be(false) + options.isCredentialMgmtPreview should be(false) + options.isSetMinPINLength should be(false) + options.isMakeCredUvNotRqd should be(false) + options.isAlwaysUv should be(false) + } + it( "are structurally identical after multiple (de)serialization round-trips." ) { From ad36a8bcf859e9baeeea08a6ab1112214b8c289c Mon Sep 17 00:00:00 2001 From: Emil Lundberg Date: Tue, 3 Jun 2025 17:27:42 +0200 Subject: [PATCH 4/8] Remove redundant builder defaults --- .../fido/metadata/SupportedCtapOptions.java | 47 ++++++++----------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/SupportedCtapOptions.java b/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/SupportedCtapOptions.java index 7906c152b..5c96f7688 100644 --- a/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/SupportedCtapOptions.java +++ b/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/SupportedCtapOptions.java @@ -24,14 +24,14 @@ public class SupportedCtapOptions { * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ - @Builder.Default boolean plat = false; + boolean plat; /** * @see Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ - @Builder.Default boolean rk = false; + boolean rk; /** * @see */ @JsonInclude(JsonInclude.Include.NON_DEFAULT) - @Builder.Default - boolean clientPin = false; + boolean clientPin; /** * @see Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ - @Builder.Default boolean up = false; + boolean up; /** * @see */ @JsonInclude(JsonInclude.Include.NON_DEFAULT) - @Builder.Default - boolean uv = false; + boolean uv; /** * @see */ @JsonAlias("uvToken") - @Builder.Default - boolean pinUvAuthToken = false; + boolean pinUvAuthToken; /** * @see Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ - @Builder.Default boolean noMcGaPermissionsWithClientPin = false; + boolean noMcGaPermissionsWithClientPin; /** * @see Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ - @Builder.Default boolean largeBlobs = false; + boolean largeBlobs; /** * @see */ @JsonInclude(JsonInclude.Include.NON_DEFAULT) - @Builder.Default - boolean ep = false; + boolean ep; /** * @see */ @JsonInclude(JsonInclude.Include.NON_DEFAULT) - @Builder.Default - boolean bioEnroll = false; + boolean bioEnroll; /** * @see */ @JsonInclude(JsonInclude.Include.NON_DEFAULT) - @Builder.Default - boolean userVerificationMgmtPreview = false; + boolean userVerificationMgmtPreview; /** * @see Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ - @Builder.Default boolean uvBioEnroll = false; + boolean uvBioEnroll; /** * @see */ @JsonAlias("config") - @Builder.Default - boolean authnrCfg = false; + boolean authnrCfg; /** * @see Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ - @Builder.Default boolean uvAcfg = false; + boolean uvAcfg; /** * @see Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ - @Builder.Default boolean credMgmt = false; + boolean credMgmt; /** * @see */ @JsonInclude(JsonInclude.Include.NON_DEFAULT) - @Builder.Default - boolean credentialMgmtPreview = false; + boolean credentialMgmtPreview; /** * @see Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ - @Builder.Default boolean setMinPINLength = false; + boolean setMinPINLength; /** * @see Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ - @Builder.Default boolean makeCredUvNotRqd = false; + boolean makeCredUvNotRqd; /** * @see */ @JsonInclude(JsonInclude.Include.NON_DEFAULT) - @Builder.Default - boolean alwaysUv = false; + boolean alwaysUv; @JsonCreator private SupportedCtapOptions( From 0452830e43e21b5a43acef894d14049c1020735b Mon Sep 17 00:00:00 2001 From: Emil Lundberg Date: Tue, 3 Jun 2025 17:24:03 +0200 Subject: [PATCH 5/8] Move @JsonAlias alongside @JsonProperty in SupportedCtapOptions --- .../java/com/yubico/fido/metadata/SupportedCtapOptions.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/SupportedCtapOptions.java b/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/SupportedCtapOptions.java index 5c96f7688..5b7ff7a6d 100644 --- a/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/SupportedCtapOptions.java +++ b/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/SupportedCtapOptions.java @@ -61,7 +61,6 @@ public class SupportedCtapOptions { * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ - @JsonAlias("uvToken") boolean pinUvAuthToken; /** @@ -114,7 +113,6 @@ public class SupportedCtapOptions { * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ - @JsonAlias("config") boolean authnrCfg; /** @@ -168,14 +166,14 @@ private SupportedCtapOptions( @JsonProperty("clientPin") Boolean clientPin, @JsonProperty("up") Boolean up, @JsonProperty("uv") Boolean uv, - @JsonProperty("pinUvAuthToken") Boolean pinUvAuthToken, + @JsonAlias("uvToken") @JsonProperty("pinUvAuthToken") Boolean pinUvAuthToken, @JsonProperty("noMcGaPermissionsWithClientPin") Boolean noMcGaPermissionsWithClientPin, @JsonProperty("largeBlobs") Boolean largeBlobs, @JsonProperty("ep") Boolean ep, @JsonProperty("bioEnroll") Boolean bioEnroll, @JsonProperty("userVerificationMgmtPreview") Boolean userVerificationMgmtPreview, @JsonProperty("uvBioEnroll") Boolean uvBioEnroll, - @JsonProperty("authnrCfg") Boolean authnrCfg, + @JsonAlias("config") @JsonProperty("authnrCfg") Boolean authnrCfg, @JsonProperty("uvAcfg") Boolean uvAcfg, @JsonProperty("credMgmt") Boolean credMgmt, @JsonProperty("credentialMgmtPreview") Boolean credentialMgmtPreview, From f51c289bad9e0a87ef8aecd57b6b9553e5861a9e Mon Sep 17 00:00:00 2001 From: Dennis Fokin Date: Tue, 10 Jun 2025 13:33:43 +0200 Subject: [PATCH 6/8] Add perCredMgmtRO and fix url to CTAP --- .../fido/metadata/SupportedCtapOptions.java | 49 +++++++++++-------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/SupportedCtapOptions.java b/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/SupportedCtapOptions.java index 5b7ff7a6d..a18ca5585 100644 --- a/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/SupportedCtapOptions.java +++ b/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/SupportedCtapOptions.java @@ -12,7 +12,7 @@ * supports the respective option. * * @see Client + * href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ @Value @@ -21,21 +21,21 @@ public class SupportedCtapOptions { /** * @see Client + * href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ boolean plat; /** * @see Client + * href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ boolean rk; /** * @see Client + * href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ @JsonInclude(JsonInclude.Include.NON_DEFAULT) @@ -43,14 +43,14 @@ public class SupportedCtapOptions { /** * @see Client + * href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ boolean up; /** * @see Client + * href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ @JsonInclude(JsonInclude.Include.NON_DEFAULT) @@ -58,28 +58,28 @@ public class SupportedCtapOptions { /** * @see Client + * href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ boolean pinUvAuthToken; /** * @see Client + * href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ boolean noMcGaPermissionsWithClientPin; /** * @see Client + * href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ boolean largeBlobs; /** * @see Client + * href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ @JsonInclude(JsonInclude.Include.NON_DEFAULT) @@ -87,7 +87,7 @@ public class SupportedCtapOptions { /** * @see Client + * href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ @JsonInclude(JsonInclude.Include.NON_DEFAULT) @@ -95,7 +95,7 @@ public class SupportedCtapOptions { /** * @see Client + * href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ @JsonInclude(JsonInclude.Include.NON_DEFAULT) @@ -103,35 +103,42 @@ public class SupportedCtapOptions { /** * @see Client + * href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ boolean uvBioEnroll; /** * @see Client + * href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ boolean authnrCfg; /** * @see Client + * href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ boolean uvAcfg; /** * @see Client + * href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ boolean credMgmt; /** * @see Client + * href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) + */ + boolean perCredMgmtRO; + + /** + * @see Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ @JsonInclude(JsonInclude.Include.NON_DEFAULT) @@ -139,21 +146,21 @@ public class SupportedCtapOptions { /** * @see Client + * href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ boolean setMinPINLength; /** * @see Client + * href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ boolean makeCredUvNotRqd; /** * @see Client + * href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) */ @JsonInclude(JsonInclude.Include.NON_DEFAULT) @@ -176,6 +183,7 @@ private SupportedCtapOptions( @JsonAlias("config") @JsonProperty("authnrCfg") Boolean authnrCfg, @JsonProperty("uvAcfg") Boolean uvAcfg, @JsonProperty("credMgmt") Boolean credMgmt, + @JsonProperty("perCredMgmtRO") Boolean perCredMgmtRO, @JsonProperty("credentialMgmtPreview") Boolean credentialMgmtPreview, @JsonProperty("setMinPINLength") Boolean setMinPINLength, @JsonProperty("makeCredUvNotRqd") Boolean makeCredUvNotRqd, @@ -195,6 +203,7 @@ private SupportedCtapOptions( this.authnrCfg = Boolean.TRUE.equals(authnrCfg); this.uvAcfg = Boolean.TRUE.equals(uvAcfg); this.credMgmt = Boolean.TRUE.equals(credMgmt); + this.perCredMgmtRO = Boolean.TRUE.equals(perCredMgmtRO); this.credentialMgmtPreview = Boolean.TRUE.equals(credentialMgmtPreview); this.setMinPINLength = Boolean.TRUE.equals(setMinPINLength); this.makeCredUvNotRqd = Boolean.TRUE.equals(makeCredUvNotRqd); From 8a972045726b8e9166396dbb82aa1ce8ddd9830b Mon Sep 17 00:00:00 2001 From: Dennis Fokin Date: Tue, 10 Jun 2025 13:36:50 +0200 Subject: [PATCH 7/8] Update JavaDoc --- .../yubico/fido/metadata/SupportedCtapOptions.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/SupportedCtapOptions.java b/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/SupportedCtapOptions.java index a18ca5585..88fa473b0 100644 --- a/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/SupportedCtapOptions.java +++ b/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/SupportedCtapOptions.java @@ -34,6 +34,8 @@ public class SupportedCtapOptions { boolean rk; /** + * If set to true the device is capable of accepting PIN. + * * @see Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) @@ -49,6 +51,8 @@ public class SupportedCtapOptions { boolean up; /** + * If set to true the device is capable of built-in user verification. + * * @see Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) @@ -78,6 +82,8 @@ public class SupportedCtapOptions { boolean largeBlobs; /** + * If set to true the authenticator is enterprise attestation capable. + * * @see Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) @@ -86,6 +92,8 @@ public class SupportedCtapOptions { boolean ep; /** + * If set to true the authenticator supports the authenticatorBioEnrollment commands. + * * @see Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) @@ -94,6 +102,9 @@ public class SupportedCtapOptions { boolean bioEnroll; /** + * If set to true the authenticator supports the Prototype authenticatorBioEnrollment + * (0x40) commands. + * * @see Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) @@ -159,6 +170,9 @@ public class SupportedCtapOptions { boolean makeCredUvNotRqd; /** + * If set to true the authenticator supports the Always Require User Verification + * feature. + * * @see Client * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04) From 6801734298499e8beb1f9ec5d3da93360ce9c595 Mon Sep 17 00:00:00 2001 From: Dennis Fokin Date: Fri, 13 Jun 2025 08:46:28 +0200 Subject: [PATCH 8/8] Add perCredMgmtRO to test --- .../test/scala/com/yubico/fido/metadata/MetadataBlobSpec.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/webauthn-server-attestation/src/test/scala/com/yubico/fido/metadata/MetadataBlobSpec.scala b/webauthn-server-attestation/src/test/scala/com/yubico/fido/metadata/MetadataBlobSpec.scala index ecae4ff50..3de744451 100644 --- a/webauthn-server-attestation/src/test/scala/com/yubico/fido/metadata/MetadataBlobSpec.scala +++ b/webauthn-server-attestation/src/test/scala/com/yubico/fido/metadata/MetadataBlobSpec.scala @@ -76,6 +76,7 @@ class MetadataBlobSpec options.isAuthnrCfg should be(false) options.isUvAcfg should be(false) options.isCredMgmt should be(false) + options.isPerCredMgmtRO should be(false) options.isCredentialMgmtPreview should be(false) options.isSetMinPINLength should be(false) options.isMakeCredUvNotRqd should be(false)