Skip to content

Commit 365aba2

Browse files
authored
Merge pull request #419 from Yubico/SupportedCtapOptions-absent
MDS: Adjust SupportedCtapOptions parsing logic
2 parents c8a5523 + a94d148 commit 365aba2

File tree

4 files changed

+187
-48
lines changed

4 files changed

+187
-48
lines changed

NEWS

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
== Version 2.8.0 (unreleased) ==
22

3+
`webauthn-server-core`:
4+
35
New features:
46

57
* Added JavaDoc to `COSEAlgorithmIdentifier` constants.
@@ -33,6 +35,13 @@ New features:
3335
** NOTE: Experimental features may receive breaking changes without a major
3436
version increase.
3537

38+
`webauthn-server-attestation`:
39+
40+
Fixes:
41+
42+
* Fixed parsing logic of tri-valued Boolean SupportedCtapOptions properties.
43+
See: https://github.com/Yubico/java-webauthn-server/issues/382
44+
3645
== Version 2.7.0 ==
3746

3847
New features:
Lines changed: 114 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,160 +1,226 @@
11
package com.yubico.fido.metadata;
22

33
import com.fasterxml.jackson.annotation.JsonAlias;
4-
import lombok.AccessLevel;
5-
import lombok.AllArgsConstructor;
4+
import com.fasterxml.jackson.annotation.JsonCreator;
5+
import com.fasterxml.jackson.annotation.JsonInclude;
6+
import com.fasterxml.jackson.annotation.JsonProperty;
67
import lombok.Builder;
78
import lombok.Value;
8-
import lombok.extern.jackson.Jacksonized;
99

1010
/**
1111
* A fixed-keys map of CTAP2 option names to Boolean values representing whether an authenticator
1212
* supports the respective option.
1313
*
1414
* @see <a
15-
* href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client
15+
* href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client
1616
* to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a>
1717
*/
1818
@Value
1919
@Builder
20-
@Jacksonized
21-
@AllArgsConstructor(access = AccessLevel.PRIVATE)
2220
public class SupportedCtapOptions {
2321

2422
/**
2523
* @see <a
26-
* href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client
24+
* href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client
2725
* to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a>
2826
*/
29-
@Builder.Default boolean plat = false;
27+
boolean plat;
3028

3129
/**
3230
* @see <a
33-
* href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client
31+
* href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client
3432
* to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a>
3533
*/
36-
@Builder.Default boolean rk = false;
34+
boolean rk;
3735

3836
/**
37+
* If set to <code>true</code> the device is capable of accepting PIN.
38+
*
3939
* @see <a
40-
* href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client
40+
* href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client
4141
* to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a>
4242
*/
43-
@Builder.Default boolean clientPin = false;
43+
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
44+
boolean clientPin;
4445

4546
/**
4647
* @see <a
47-
* href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client
48+
* href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client
4849
* to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a>
4950
*/
50-
@Builder.Default boolean up = false;
51+
boolean up;
5152

5253
/**
54+
* If set to <code>true</code> the device is capable of built-in user verification.
55+
*
5356
* @see <a
54-
* href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client
57+
* href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client
5558
* to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a>
5659
*/
57-
@Builder.Default boolean uv = false;
60+
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
61+
boolean uv;
5862

5963
/**
6064
* @see <a
61-
* href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client
65+
* href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client
6266
* to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a>
6367
*/
64-
@JsonAlias("uvToken")
65-
@Builder.Default
66-
boolean pinUvAuthToken = false;
68+
boolean pinUvAuthToken;
6769

6870
/**
6971
* @see <a
70-
* href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client
72+
* href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client
7173
* to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a>
7274
*/
73-
@Builder.Default boolean noMcGaPermissionsWithClientPin = false;
75+
boolean noMcGaPermissionsWithClientPin;
7476

7577
/**
7678
* @see <a
77-
* href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client
79+
* href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client
7880
* to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a>
7981
*/
80-
@Builder.Default boolean largeBlobs = false;
82+
boolean largeBlobs;
8183

8284
/**
85+
* If set to <code>true</code> the authenticator is enterprise attestation capable.
86+
*
8387
* @see <a
84-
* href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client
88+
* href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client
8589
* to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a>
8690
*/
87-
@Builder.Default boolean ep = false;
91+
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
92+
boolean ep;
8893

8994
/**
95+
* If set to <code>true</code> the authenticator supports the authenticatorBioEnrollment commands.
96+
*
9097
* @see <a
91-
* href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client
98+
* href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client
9299
* to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a>
93100
*/
94-
@Builder.Default boolean bioEnroll = false;
101+
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
102+
boolean bioEnroll;
95103

96104
/**
105+
* If set to <code>true</code> the authenticator supports the Prototype authenticatorBioEnrollment
106+
* (0x40) commands.
107+
*
97108
* @see <a
98-
* href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client
109+
* href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client
99110
* to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a>
100111
*/
101-
@Builder.Default boolean userVerificationMgmtPreview = false;
112+
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
113+
boolean userVerificationMgmtPreview;
102114

103115
/**
104116
* @see <a
105-
* href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client
117+
* href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client
106118
* to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a>
107119
*/
108-
@Builder.Default boolean uvBioEnroll = false;
120+
boolean uvBioEnroll;
109121

110122
/**
111123
* @see <a
112-
* href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client
124+
* href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client
113125
* to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a>
114126
*/
115-
@JsonAlias("config")
116-
@Builder.Default
117-
boolean authnrCfg = false;
127+
boolean authnrCfg;
118128

119129
/**
120130
* @see <a
121-
* href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client
131+
* href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client
122132
* to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a>
123133
*/
124-
@Builder.Default boolean uvAcfg = false;
134+
boolean uvAcfg;
125135

126136
/**
127137
* @see <a
128-
* href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client
138+
* href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client
129139
* to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a>
130140
*/
131-
@Builder.Default boolean credMgmt = false;
141+
boolean credMgmt;
132142

133143
/**
134144
* @see <a
135-
* href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client
145+
* href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client
136146
* to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a>
137147
*/
138-
@Builder.Default boolean credentialMgmtPreview = false;
148+
boolean perCredMgmtRO;
139149

140150
/**
141151
* @see <a
142-
* href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client
152+
* href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client
143153
* to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a>
144154
*/
145-
@Builder.Default boolean setMinPINLength = false;
155+
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
156+
boolean credentialMgmtPreview;
146157

147158
/**
148159
* @see <a
149-
* href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client
160+
* href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client
150161
* to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a>
151162
*/
152-
@Builder.Default boolean makeCredUvNotRqd = false;
163+
boolean setMinPINLength;
153164

154165
/**
155166
* @see <a
156-
* href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client
167+
* href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client
157168
* to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a>
158169
*/
159-
@Builder.Default boolean alwaysUv = false;
170+
boolean makeCredUvNotRqd;
171+
172+
/**
173+
* If set to <code>true</code> the authenticator supports the Always Require User Verification
174+
* feature.
175+
*
176+
* @see <a
177+
* href="https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#authenticatorGetInfo">Client
178+
* to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a>
179+
*/
180+
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
181+
boolean alwaysUv;
182+
183+
@JsonCreator
184+
private SupportedCtapOptions(
185+
@JsonProperty("plat") Boolean plat,
186+
@JsonProperty("rk") Boolean rk,
187+
@JsonProperty("clientPin") Boolean clientPin,
188+
@JsonProperty("up") Boolean up,
189+
@JsonProperty("uv") Boolean uv,
190+
@JsonAlias("uvToken") @JsonProperty("pinUvAuthToken") Boolean pinUvAuthToken,
191+
@JsonProperty("noMcGaPermissionsWithClientPin") Boolean noMcGaPermissionsWithClientPin,
192+
@JsonProperty("largeBlobs") Boolean largeBlobs,
193+
@JsonProperty("ep") Boolean ep,
194+
@JsonProperty("bioEnroll") Boolean bioEnroll,
195+
@JsonProperty("userVerificationMgmtPreview") Boolean userVerificationMgmtPreview,
196+
@JsonProperty("uvBioEnroll") Boolean uvBioEnroll,
197+
@JsonAlias("config") @JsonProperty("authnrCfg") Boolean authnrCfg,
198+
@JsonProperty("uvAcfg") Boolean uvAcfg,
199+
@JsonProperty("credMgmt") Boolean credMgmt,
200+
@JsonProperty("perCredMgmtRO") Boolean perCredMgmtRO,
201+
@JsonProperty("credentialMgmtPreview") Boolean credentialMgmtPreview,
202+
@JsonProperty("setMinPINLength") Boolean setMinPINLength,
203+
@JsonProperty("makeCredUvNotRqd") Boolean makeCredUvNotRqd,
204+
@JsonProperty("alwaysUv") Boolean alwaysUv) {
205+
this.plat = Boolean.TRUE.equals(plat);
206+
this.rk = Boolean.TRUE.equals(rk);
207+
this.clientPin = clientPin != null;
208+
this.up = Boolean.TRUE.equals(up);
209+
this.uv = uv != null;
210+
this.pinUvAuthToken = Boolean.TRUE.equals(pinUvAuthToken);
211+
this.noMcGaPermissionsWithClientPin = Boolean.TRUE.equals(noMcGaPermissionsWithClientPin);
212+
this.largeBlobs = Boolean.TRUE.equals(largeBlobs);
213+
this.ep = ep != null;
214+
this.bioEnroll = bioEnroll != null;
215+
this.userVerificationMgmtPreview = userVerificationMgmtPreview != null;
216+
this.uvBioEnroll = Boolean.TRUE.equals(uvBioEnroll);
217+
this.authnrCfg = Boolean.TRUE.equals(authnrCfg);
218+
this.uvAcfg = Boolean.TRUE.equals(uvAcfg);
219+
this.credMgmt = Boolean.TRUE.equals(credMgmt);
220+
this.perCredMgmtRO = Boolean.TRUE.equals(perCredMgmtRO);
221+
this.credentialMgmtPreview = Boolean.TRUE.equals(credentialMgmtPreview);
222+
this.setMinPINLength = Boolean.TRUE.equals(setMinPINLength);
223+
this.makeCredUvNotRqd = Boolean.TRUE.equals(makeCredUvNotRqd);
224+
this.alwaysUv = alwaysUv != null;
225+
}
160226
}

webauthn-server-attestation/src/test/scala/com/yubico/fido/metadata/Generators.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,7 @@ object Generators {
432432
authnrCfg <- arbitrary[Boolean]
433433
uvAcfg <- arbitrary[Boolean]
434434
credMgmt <- arbitrary[Boolean]
435+
perCredMgmtRO <- arbitrary[Boolean]
435436
credentialMgmtPreview <- arbitrary[Boolean]
436437
setMinPINLength <- arbitrary[Boolean]
437438
makeCredUvNotRqd <- arbitrary[Boolean]
@@ -453,6 +454,7 @@ object Generators {
453454
.authnrCfg(authnrCfg)
454455
.uvAcfg(uvAcfg)
455456
.credMgmt(credMgmt)
457+
.perCredMgmtRO(perCredMgmtRO)
456458
.credentialMgmtPreview(credentialMgmtPreview)
457459
.setMinPINLength(setMinPINLength)
458460
.makeCredUvNotRqd(makeCredUvNotRqd)

webauthn-server-attestation/src/test/scala/com/yubico/fido/metadata/MetadataBlobSpec.scala

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
package com.yubico.fido.metadata
22

3+
import com.yubico.fido.metadata.Generators.arbitrarySupportedCtapOptions
34
import com.yubico.internal.util.JacksonCodecs
45
import com.yubico.webauthn.data.ByteArray
6+
import org.scalacheck.Arbitrary
7+
import org.scalacheck.Gen
58
import org.scalatest.funspec.AnyFunSpec
69
import org.scalatest.matchers.should.Matchers
710
import org.scalatestplus.scalacheck.ScalaCheckDrivenPropertyChecks
811

12+
import scala.jdk.CollectionConverters.SetHasAsScala
13+
import scala.jdk.OptionConverters.RichOptional
14+
915
class MetadataBlobSpec
1016
extends AnyFunSpec
1117
with Matchers
@@ -50,4 +56,60 @@ class MetadataBlobSpec
5056
}
5157
}
5258

59+
describe("SupportedCtapOptions") {
60+
it("can be parsed from an empty JSON object.") {
61+
val options = JacksonCodecs
62+
.json()
63+
.readValue("{}", classOf[SupportedCtapOptions])
64+
options should not be null
65+
options.isPlat should be(false)
66+
options.isRk should be(false)
67+
options.isUp should be(false)
68+
options.isUv should be(false)
69+
options.isPinUvAuthToken should be(false)
70+
options.isNoMcGaPermissionsWithClientPin should be(false)
71+
options.isLargeBlobs should be(false)
72+
options.isEp should be(false)
73+
options.isBioEnroll should be(false)
74+
options.isUserVerificationMgmtPreview should be(false)
75+
options.isUvBioEnroll should be(false)
76+
options.isAuthnrCfg should be(false)
77+
options.isUvAcfg should be(false)
78+
options.isCredMgmt should be(false)
79+
options.isPerCredMgmtRO should be(false)
80+
options.isCredentialMgmtPreview should be(false)
81+
options.isSetMinPINLength should be(false)
82+
options.isMakeCredUvNotRqd should be(false)
83+
options.isAlwaysUv should be(false)
84+
}
85+
86+
it(
87+
"are structurally identical after multiple (de)serialization round-trips."
88+
) {
89+
val json = JacksonCodecs.json()
90+
val blob = json
91+
.readValue(
92+
ByteArray
93+
.fromBase64Url(FidoMds3Examples.BlobPayloadBase64url)
94+
.getBytes,
95+
classOf[MetadataBLOBPayload],
96+
)
97+
val blobOptions = blob.getEntries.asScala
98+
.flatMap(entry => entry.getMetadataStatement.toScala)
99+
.flatMap(statement => statement.getAuthenticatorGetInfo.toScala)
100+
.flatMap(info => info.getOptions.toScala)
101+
forAll(Gen.oneOf(Arbitrary.arbitrary, Gen.oneOf(blobOptions))) {
102+
(options1: SupportedCtapOptions) =>
103+
val encoded1 = json.writeValueAsBytes(options1)
104+
val options2 = json.readValue(encoded1, classOf[SupportedCtapOptions])
105+
val encoded2 = json.writeValueAsBytes(options2)
106+
val options3 = json.readValue(encoded2, classOf[SupportedCtapOptions])
107+
108+
options2 should not be null
109+
options2 should equal(options1)
110+
options3 should not be null
111+
options3 should equal(options1)
112+
}
113+
}
114+
}
53115
}

0 commit comments

Comments
 (0)