Skip to content

Commit 5f14dc4

Browse files
committed
Release 1.12.2
Fixes: - `com.upokecenter:cbor` dependency bumped to minimum version 4.5.1 due to a known vulnerability, see: GHSA-fj2w-wfgv-mwq6 - Fixed crash in `AuthenticatorData` deserialization with `com.upokecenter:cbor` versions later than 4.0.1
2 parents 8c29385 + c07016c commit 5f14dc4

File tree

9 files changed

+414
-423
lines changed

9 files changed

+414
-423
lines changed

NEWS

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
== Version 1.12.2 ==
2+
3+
Fixes:
4+
5+
* `com.upokecenter:cbor` dependency bumped to minimum version 4.5.1 due to a
6+
known vulnerability, see: https://github.com/advisories/GHSA-fj2w-wfgv-mwq6
7+
* Fixed crash in `AuthenticatorData` deserialization with `com.upokecenter:cbor`
8+
versions later than 4.0.1
9+
10+
111
== Version 1.12.1 ==
212

313
Fixes:

README

Lines changed: 61 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@ Maven:
2525
<dependency>
2626
<groupId>com.yubico</groupId>
2727
<artifactId>webauthn-server-core</artifactId>
28-
<version>1.12.1</version>
28+
<version>1.12.2</version>
2929
<scope>compile</scope>
3030
</dependency>
3131
----------
3232

3333
Gradle:
3434

3535
----------
36-
compile 'com.yubico:webauthn-server-core:1.12.1'
36+
compile 'com.yubico:webauthn-server-core:1.12.2'
3737
----------
3838

3939
=== Semantic versioning
@@ -110,10 +110,7 @@ The client side involves:
110110
passing the result from `RelyingParty.startRegistration(...)` or `.startAssertion(...)` as the argument.
111111
2. Encode the result of the successfully resolved promise and return it to the server.
112112
For this you need some way to encode `Uint8Array` values;
113-
this guide will assume use of link:https://github.com/beatgammit/base64-js[base64-js].
114-
However the built-in parser methods for
115-
link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core-minimal/latest/com/yubico/webauthn/data/PublicKeyCredential.html[`PublicKeyCredential`]
116-
expect URL-safe Base64 encoding, so the below examples will include this as an additional step.
113+
this guide will use GitHub's link:https://github.com/github/webauthn-json[webauthn-json] library.
117114

118115
Example code is given below.
119116
For more detailed example usage, see
@@ -163,6 +160,8 @@ A registration ceremony consists of 5 main steps:
163160
4. Validate the response using `RelyingParty.finishRegistration(...)`.
164161
5. Update your database using the `finishRegistration` output.
165162

163+
This example uses GitHub's link:https://github.com/github/webauthn-json[webauthn-json] library to do both (2) and (3) in one function call.
164+
166165
First, generate registration parameters and send them to the client:
167166

168167
[source,java]
@@ -196,62 +195,23 @@ Now call the WebAuthn API on the client side:
196195

197196
[source,javascript]
198197
----------
199-
function base64urlToUint8array(base64Bytes) {
200-
const padding = '===='.substring(0, (4 - (base64Bytes.length % 4)) % 4);
201-
return base64js.toByteArray((base64Bytes + padding).replace(/\//g, "_").replace(/\+/g, "-"));
202-
}
203-
function uint8arrayToBase64url(bytes) {
204-
if (bytes instanceof Uint8Array) {
205-
return base64js.fromByteArray(bytes).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
206-
} else {
207-
return uint8arrayToBase64url(new Uint8Array(bytes));
208-
}
209-
}
210-
211-
fetch(/* ... */) // Make the call that returns the credentialCreateJson above
212-
.then(credentialCreateJson => ({ // Decode byte arrays from base64url
213-
publicKey: {
214-
...credentialCreateJson.publicKey,
215-
216-
challenge: base64urlToUint8array(credentialCreateJson.publicKey.challenge),
217-
user: {
218-
...credentialCreateJson.publicKey.user,
219-
id: base64urlToUint8array(credentialCreateJson.publicKey.user.id),
220-
},
221-
excludeCredentials: credentialCreateJson.publicKey.excludeCredentials.map(credential => ({
222-
...credential,
223-
id: base64urlToUint8array(credential.id),
224-
})),
225-
226-
// Warning: Extension inputs could also contain binary data that needs encoding
227-
extensions: credentialCreateJson.publicKey.extensions,
228-
},
229-
}))
230-
.then(credentialCreateOptions => // Call WebAuthn ceremony
231-
navigator.credentials.create(credentialCreateOptions))
232-
.then(publicKeyCredential => ({ // Encode PublicKeyCredential for transport to server (example)
233-
type: publicKeyCredential.type,
234-
id: publicKeyCredential.id,
235-
response: {
236-
attestationObject: uint8arrayToBase64url(publicKeyCredential.response.attestationObject),
237-
clientDataJSON: uint8arrayToBase64url(publicKeyCredential.response.clientDataJSON),
238-
239-
// Attempt to read transports, but recover gracefully if not supported by the browser
240-
transports: publicKeyCredential.response.getTransports && publicKeyCredential.response.getTransports() || [],
241-
},
242-
243-
// Warning: Client extension results could also contain binary data that needs encoding
244-
clientExtensionResults: publicKeyCredential.getClientExtensionResults(),
245-
}))
246-
.then(encodedResult =>
247-
fetch(/* ... */)); // Return encoded PublicKeyCredential to server
198+
import * as webauthnJson from "@github/webauthn-json";
199+
200+
// Make the call that returns the credentialCreateJson above
201+
const credentialCreateOptions = await fetch(/* ... */).then(resp => resp.json());
202+
203+
// Call WebAuthn ceremony using webauthn-json wrapper
204+
const publicKeyCredential = await webauthnJson.create(credentialCreateOptions);
205+
206+
// Return encoded PublicKeyCredential to server
207+
fetch(/* ... */, { body: JSON.stringify(publicKeyCredential) });
248208
----------
249209

250210
Validate the response on the server side:
251211

252212
[source,java]
253213
----------
254-
String publicKeyCredentialJson = /* ... */; // encodedResult from client
214+
String publicKeyCredentialJson = /* ... */; // publicKeyCredential from client
255215
PublicKeyCredential<AuthenticatorAttestationResponse, ClientRegistrationExtensionOutputs> pkc =
256216
PublicKeyCredential.parseRegistrationResponseJson(publicKeyCredentialJson);
257217

@@ -295,6 +255,8 @@ Like registration ceremonies, an authentication ceremony consists of 5 main step
295255
4. Validate the response using `RelyingParty.finishAssertion(...)`.
296256
5. Update your database using the `finishAssertion` output, and act upon the result (for example, grant login access).
297257

258+
This example uses GitHub's link:https://github.com/github/webauthn-json[webauthn-json] library to do both (2) and (3) in one function call.
259+
298260
First, generate authentication parameters and send them to the client:
299261

300262
[source,java]
@@ -313,58 +275,23 @@ Now call the WebAuthn API on the client side:
313275

314276
[source,javascript]
315277
----------
316-
function base64urlToUint8array(base64Bytes) {
317-
const padding = '===='.substring(0, (4 - (base64Bytes.length % 4)) % 4);
318-
return base64js.toByteArray((base64Bytes + padding).replace(/\//g, "_").replace(/\+/g, "-"));
319-
}
320-
function uint8arrayToBase64url(bytes) {
321-
if (bytes instanceof Uint8Array) {
322-
return base64js.fromByteArray(bytes).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
323-
} else {
324-
return uint8arrayToBase64url(new Uint8Array(bytes));
325-
}
326-
}
327-
328-
fetch(/* ... */) // Make the call that returns the credentialGetJson above
329-
.then(credentialGetJson => ({ // Decode byte arrays from base64url
330-
publicKey: {
331-
...credentialGetJson.publicKey,
332-
allowCredentials: credentialGetJson.publicKey.allowCredentials
333-
&& credentialGetJson.publicKey.allowCredentials.map(credential => ({
334-
...credential,
335-
id: base64urlToUint8array(credential.id),
336-
})),
337-
338-
challenge: base64urlToUint8array(credentialGetJson.publicKey.challenge),
339-
340-
// Warning: Extension inputs could also contain binary data that needs encoding
341-
extensions: credentialGetJson.publicKey.extensions,
342-
},
343-
}))
344-
.then(credentialGetOptions => // Call WebAuthn ceremony
345-
navigator.credentials.get(credentialGetOptions))
346-
.then(publicKeyCredential => ({ // Encode PublicKeyCredential for transport to server (example)
347-
type: publicKeyCredential.type,
348-
id: publicKeyCredential.id,
349-
response: {
350-
authenticatorData: uint8arrayToBase64url(publicKeyCredential.response.authenticatorData),
351-
clientDataJSON: uint8arrayToBase64url(publicKeyCredential.response.clientDataJSON),
352-
signature: uint8arrayToBase64url(publicKeyCredential.response.signature),
353-
userHandle: publicKeyCredential.response.userHandle && uint8arrayToBase64url(publicKeyCredential.response.userHandle),
354-
},
355-
356-
// Warning: Client extension results could also contain binary data that needs encoding
357-
clientExtensionResults: publicKeyCredential.getClientExtensionResults(),
358-
}))
359-
.then(encodedResult =>
360-
fetch(/* ... */)); // Return encoded PublicKeyCredential to server
278+
import * as webauthnJson from "@github/webauthn-json";
279+
280+
// Make the call that returns the credentialGetJson above
281+
const credentialGetOptions = await fetch(/* ... */).then(resp => resp.json());
282+
283+
// Call WebAuthn ceremony using webauthn-json wrapper
284+
const publicKeyCredential = await webauthnJson.get(credentialGetOptions);
285+
286+
// Return encoded PublicKeyCredential to server
287+
fetch(/* ... */, { body: JSON.stringify(publicKeyCredential) });
361288
----------
362289

363290
Validate the response on the server side:
364291

365292
[source,java]
366293
----------
367-
String publicKeyCredentialJson = /* ... */; // encodedResult from client
294+
String publicKeyCredentialJson = /* ... */; // publicKeyCredential from client
368295
PublicKeyCredential<AuthenticatorAssertionResponse, ClientAssertionExtensionOutputs> pkc =
369296
PublicKeyCredential.parseAssertionResponseJson(publicKeyCredentialJson);
370297

@@ -473,13 +400,13 @@ and credentials registered via the U2F API will continue to work with the WebAut
473400

474401
To migrate to using the WebAuthn API, you need to do the following:
475402

476-
* Follow the link:#getting-started[Getting started] guide above to set up WebAuthn support in general.
403+
1. Follow the link:#getting-started[Getting started] guide above to set up WebAuthn support in general.
477404
+
478405
Note that unlike a U2F AppID, the WebAuthn link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core-minimal/latest/com/yubico/webauthn/data/RelyingPartyIdentity.RelyingPartyIdentityBuilder.html#id(java.lang.String)[RP ID]
479406
consists of only the domain name of the AppID.
480407
WebAuthn does not support link:https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-appid-and-facets-v1.2-ps-20170411.html[U2F Trusted Facet Lists].
481408

482-
* Set the
409+
2. Set the
483410
link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core-minimal/latest/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#appId(com.yubico.webauthn.extension.appid.AppId)[`appId()`]
484411
setting on your `RelyingParty` instance.
485412
The argument to the `appid()` setting should be the same as you used for the `appId` argument to the
@@ -489,36 +416,36 @@ This will enable the link:https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sc
489416
and link:https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-exclude-extension[`appidExclude`]
490417
extensions and configure the `RelyingParty` to accept the given AppId when verifying authenticator signatures.
491418

492-
* Generate a link:https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#user-handle[user handle] for each existing user
493-
and store it in their account,
494-
or decide on a method for deriving one deterministically from existing user attributes.
495-
For example, if your user records are assigned UUIDs, you can use that UUID as the user handle.
496-
You SHOULD NOT use a plain username or e-mail address, or hash of either, as the user handle -
497-
for more on this, see the link:https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-user-handle-privacy[User Handle Contents]
498-
privacy consideration.
499-
500-
* When your `CredentialRepository` creates a `RegisteredCredential` for a U2F credential,
501-
use the U2F key handle as the
502-
link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core-minimal/latest/com/yubico/webauthn/RegisteredCredential.RegisteredCredentialBuilder.html#credentialId(com.yubico.webauthn.data.ByteArray)[credential ID].
503-
If you store key handles base64 encoded, you should decode them using
504-
link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core-minimal/latest/com/yubico/webauthn/data/ByteArray.html#fromBase64(java.lang.String)[`ByteArray.fromBase64`]
505-
or
506-
link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core-minimal/latest/com/yubico/webauthn/data/ByteArray.html#fromBase64Url(java.lang.String)[`ByteArray.fromBase64Url`]
507-
as appropriate before passing them to the `RegisteredCredential`.
508-
509-
* When your `CredentialRepository` creates a `RegisteredCredential` for a U2F credential,
510-
use the
511-
link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core-minimal/latest/com/yubico/webauthn/RegisteredCredential.RegisteredCredentialBuilder.html#publicKeyEs256Raw(com.yubico.webauthn.data.ByteArray)[`publicKeyEs256Raw()`]
512-
method instead of link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core-minimal/latest/com/yubico/webauthn/RegisteredCredential.RegisteredCredentialBuilder.html#publicKeyCose(com.yubico.webauthn.data.ByteArray)[`publicKeyCose()`]
513-
to set the credential public key.
514-
515-
* Replace calls to the U2F
516-
link:https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-javascript-api-v1.2-ps-20170411.html#high-level-javascript-api[`register`]
517-
method with calls to `navigator.credentials.create()` as described in link:#getting-started[Getting started].
518-
519-
* Replace calls to the U2F
520-
link:https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-javascript-api-v1.2-ps-20170411.html#high-level-javascript-api[`sign`]
521-
method with calls to `navigator.credentials.get()` as described in link:#getting-started[Getting started].
419+
3. Generate a link:https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#user-handle[user handle]
420+
for each existing user and store it in their account,
421+
or decide on a method for deriving one deterministically from existing user attributes.
422+
For example, if your user records are assigned UUIDs, you can use that UUID as the user handle.
423+
You SHOULD NOT use a plain username or e-mail address, or hash of either, as the user handle -
424+
for more on this, see the link:https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-user-handle-privacy[User Handle Contents]
425+
privacy consideration.
426+
427+
4. When your `CredentialRepository` creates a `RegisteredCredential` for a U2F credential,
428+
use the U2F key handle as the
429+
link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core-minimal/latest/com/yubico/webauthn/RegisteredCredential.RegisteredCredentialBuilder.html#credentialId(com.yubico.webauthn.data.ByteArray)[credential ID].
430+
If you store key handles base64 encoded, you should decode them using
431+
link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core-minimal/latest/com/yubico/webauthn/data/ByteArray.html#fromBase64(java.lang.String)[`ByteArray.fromBase64`]
432+
or
433+
link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core-minimal/latest/com/yubico/webauthn/data/ByteArray.html#fromBase64Url(java.lang.String)[`ByteArray.fromBase64Url`]
434+
as appropriate before passing them to the `RegisteredCredential`.
435+
436+
5. When your `CredentialRepository` creates a `RegisteredCredential` for a U2F credential,
437+
use the
438+
link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core-minimal/latest/com/yubico/webauthn/RegisteredCredential.RegisteredCredentialBuilder.html#publicKeyEs256Raw(com.yubico.webauthn.data.ByteArray)[`publicKeyEs256Raw()`]
439+
method instead of link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core-minimal/latest/com/yubico/webauthn/RegisteredCredential.RegisteredCredentialBuilder.html#publicKeyCose(com.yubico.webauthn.data.ByteArray)[`publicKeyCose()`]
440+
to set the credential public key.
441+
442+
6. Replace calls to the U2F
443+
link:https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-javascript-api-v1.2-ps-20170411.html#high-level-javascript-api[`register`]
444+
method with calls to `navigator.credentials.create()` as described in link:#getting-started[Getting started].
445+
446+
7. Replace calls to the U2F
447+
link:https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-javascript-api-v1.2-ps-20170411.html#high-level-javascript-api[`sign`]
448+
method with calls to `navigator.credentials.get()` as described in link:#getting-started[Getting started].
522449

523450
Existing U2F credentials should now work with the WebAuthn API.
524451

build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ buildscript {
44
}
55
dependencies {
66
classpath 'com.cinnober.gradle:semver-git:2.5.0'
7-
classpath 'com.diffplug.spotless:spotless-plugin-gradle:6.0.4'
7+
classpath 'com.diffplug.spotless:spotless-plugin-gradle:6.2.0'
88
classpath 'io.github.cosmicsilence:gradle-scalafix:0.1.8'
99
}
1010
}
@@ -51,7 +51,7 @@ dependencies {
5151
api('com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:[2.11.0,3)')
5252
api('com.fasterxml.jackson.datatype:jackson-datatype-jdk8:[2.11.0,3)')
5353
api('com.google.guava:guava:[24.1.1,31)')
54-
api('com.upokecenter:cbor:[4.0.1,5)')
54+
api('com.upokecenter:cbor:[4.5.1,5)')
5555
api('javax.ws.rs:javax.ws.rs-api:[2.1,3)')
5656
api('javax.xml.bind:jaxb-api:[2.3.0,3)')
5757
api('junit:junit:[4.12,5)')

webauthn-server-core/src/main/java/com/yubico/webauthn/data/AuthenticatorData.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
package com.yubico.webauthn.data;
2626

2727
import com.fasterxml.jackson.annotation.JsonCreator;
28+
import com.fasterxml.jackson.annotation.JsonIgnore;
2829
import com.fasterxml.jackson.annotation.JsonProperty;
2930
import com.fasterxml.jackson.core.JsonGenerator;
3031
import com.fasterxml.jackson.databind.SerializerProvider;
@@ -81,9 +82,9 @@ public class AuthenticatorData {
8182
*
8283
* @see #flags
8384
*/
84-
private final transient AttestedCredentialData attestedCredentialData;
85+
@JsonIgnore private final transient AttestedCredentialData attestedCredentialData;
8586

86-
private final transient CBORObject extensions;
87+
@JsonIgnore private final transient CBORObject extensions;
8788

8889
private static final int RP_ID_HASH_INDEX = 0;
8990
private static final int RP_ID_HASH_END = RP_ID_HASH_INDEX + 32;

0 commit comments

Comments
 (0)