Skip to content

Commit 33c2041

Browse files
committed
Release 0.7.0
=== `webauthn-server-core` === Breaking changes: - Deleted parameter `RelyingParty.verifyTypeAttribute`. This was added as a workaround while browser implementations were incomplete, and should never be used in production. - Replaced field `RegisteredCredential.publicKey: PublicKey` with `publicKeyCose: ByteArray`. This means the library user no longer needs to parse the public key before passing it back into the library. - `RelyingParty.finishAssertion` now throws `InvalidSignatureCountException` instead of its supertype `AssertionFailedException` when signature count validation is enabled and the received signature count is invalid. New features: - New parameter `StartAssertionOptions.userVerification` which is forwarded into `PublicKeyCredentialRequestOptions` by `RelyingParty.startAssertion` === `webauthn-server-attestation` === - Added attestation metadata for Security Key NFC by Yubico
2 parents 2c9b6b2 + dc59f36 commit 33c2041

20 files changed

+341
-183
lines changed

NEWS

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,33 @@
1-
== Version 0.6.0 (unreleased) ==
1+
== Version 0.7.0 ==
2+
3+
=== `webauthn-server-attestation` ===
4+
5+
* Added attestation metadata for Security Key NFC by Yubico
26

37
=== `webauthn-server-core` ===
48

59
Breaking changes:
610

11+
* Deleted parameter `RelyingParty.verifyTypeAttribute`. This was added as a
12+
workaround while browser implementations were incomplete, and should never be
13+
used in production.
14+
* Replaced field `RegisteredCredential.publicKey: PublicKey` with
15+
`publicKeyCose: ByteArray`. This means the library user no longer needs to
16+
parse the public key before passing it back into the library.
17+
* `RelyingParty.finishAssertion` now throws `InvalidSignatureCountException`
18+
instead of its supertype `AssertionFailedException` when signature count
19+
validation is enabled and the received signature count is invalid.
20+
21+
New features:
22+
23+
* New parameter `StartAssertionOptions.userVerification` which is forwarded into
24+
`PublicKeyCredentialRequestOptions` by `RelyingParty.startAssertion`
25+
26+
27+
== Version 0.6.0 ==
28+
29+
Breaking changes:
30+
731
* Classes moved from package `com.yubico.webauthn.data` to `com.yubico.webauthn`:
832
** `AssertionRequest`
933
** `AssertionResult`

README

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,7 @@ authenticators and authenticating registered authenticators.
1616

1717
=== Planned breaking changes
1818

19-
* Update spec version from Candidate Recommendation 2018-03-20 to Proposed
20-
Recommendation 2018-12-??. This will include at least:
21-
** Renaming class `AttestationData` to `AttestedCredentialData`
22-
** Deleting enum value `TokenBindingStatus.NOT_SUPPORTED` and constructor
23-
`TokenBindingInfo.notSupported()`
19+
None.
2420

2521

2622
=== Example Usage

webauthn-server-attestation/src/main/resources/metadata.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,26 @@
1010
"-----BEGIN CERTIFICATE-----\nMIIDHjCCAgagAwIBAgIEG1BT9zANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNZ\ndWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAw\nMDBaGA8yMDUwMDkwNDAwMDAwMFowLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290\nIENBIFNlcmlhbCA0NTcyMDA2MzEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\nAoIBAQC/jwYuhBVlqaiYWEMsrWFisgJ+PtM91eSrpI4TK7U53mwCIawSDHy8vUmk\n5N2KAj9abvT9NP5SMS1hQi3usxoYGonXQgfO6ZXyUA9a+KAkqdFnBnlyugSeCOep\n8EdZFfsaRFtMjkwz5Gcz2Py4vIYvCdMHPtwaz0bVuzneueIEz6TnQjE63Rdt2zbw\nnebwTG5ZybeWSwbzy+BJ34ZHcUhPAY89yJQXuE0IzMZFcEBbPNRbWECRKgjq//qT\n9nmDOFVlSRCt2wiqPSzluwn+v+suQEBsUjTGMEd25tKXXTkNW21wIWbxeSyUoTXw\nLvGS6xlwQSgNpk2qXYwf8iXg7VWZAgMBAAGjQjBAMB0GA1UdDgQWBBQgIvz0bNGJ\nhjgpToksyKpP9xv9oDAPBgNVHRMECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAN\nBgkqhkiG9w0BAQsFAAOCAQEAjvjuOMDSa+JXFCLyBKsycXtBVZsJ4Ue3LbaEsPY4\nMYN/hIQ5ZM5p7EjfcnMG4CtYkNsfNHc0AhBLdq45rnT87q/6O3vUEtNMafbhU6kt\nhX7Y+9XFN9NpmYxr+ekVY5xOxi8h9JDIgoMP4VB1uS0aunL1IGqrNooL9mmFnL2k\nLVVee6/VR6C5+KSTCMCWppMuJIZII2v9o4dkoZ8Y7QRjQlLfYzd3qGtKbw7xaF1U\nsG/5xUb/Btwb2X2g4InpiB/yt/3CpQXpiWX/K4mBvUKiGn05ZsqeY1gx4g0xLBqc\nU9psmyPzK+Vsgw2jeRQ5JlKDyqE0hebfC1tvFu0CCrJFcw==\n-----END CERTIFICATE-----"
1111
],
1212
"devices": [
13+
{
14+
"deviceId": "1.3.6.1.4.1.41482.1.1",
15+
"displayName": "Security Key NFC by Yubico",
16+
"transports": 12,
17+
"deviceUrl": "https://www.yubico.com/product/security-key-nfc-by-yubico/",
18+
"imageUrl": "https://developers.yubico.com/U2F/Images/SKY-NFC.png",
19+
"selectors": [
20+
{
21+
"type": "x509Extension",
22+
"parameters": {
23+
"key": "1.3.6.1.4.1.45724.1.1.4",
24+
"value": {
25+
"type": "hex",
26+
"value": "6d44ba9bf6ec2e49b9300c8fe920cb73"
27+
}
28+
}
29+
}
30+
]
31+
},
32+
1333
{
1434
"deviceId": "1.3.6.1.4.1.41482.1.1",
1535
"displayName": "Security Key by Yubico",

webauthn-server-core/src/main/java/com/yubico/webauthn/FinishAssertionSteps.java

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,20 @@
2525
package com.yubico.webauthn;
2626

2727

28+
import COSE.CoseException;
29+
import com.fasterxml.jackson.core.JsonProcessingException;
2830
import com.yubico.internal.util.CollectionUtil;
31+
import com.yubico.internal.util.WebAuthnCodecs;
2932
import com.yubico.webauthn.data.AuthenticatorAssertionResponse;
3033
import com.yubico.webauthn.data.ByteArray;
3134
import com.yubico.webauthn.data.ClientAssertionExtensionOutputs;
3235
import com.yubico.webauthn.data.CollectedClientData;
3336
import com.yubico.webauthn.data.PublicKeyCredential;
3437
import com.yubico.webauthn.data.UserVerificationRequirement;
38+
import com.yubico.webauthn.exception.InvalidSignatureCountException;
3539
import com.yubico.webauthn.extension.appid.AppId;
40+
import java.io.IOException;
41+
import java.security.PublicKey;
3642
import java.util.ArrayList;
3743
import java.util.Collections;
3844
import java.util.LinkedList;
@@ -60,8 +66,6 @@ final class FinishAssertionSteps {
6066
private final String rpId;
6167
private final CredentialRepository credentialRepository;
6268

63-
@Builder.Default
64-
private final boolean validateTypeAttribute = true;
6569
@Builder.Default
6670
private final boolean validateSignatureCounter = true;
6771
@Builder.Default
@@ -71,14 +75,14 @@ public Step0 begin() {
7175
return new Step0();
7276
}
7377

74-
public AssertionResult run() {
78+
public AssertionResult run() throws InvalidSignatureCountException {
7579
return begin().run();
7680
}
7781

7882
private interface Step<A extends Step<?, ?>, B extends Step<?, ?>> {
7983
B nextStep();
8084

81-
void validate();
85+
void validate() throws InvalidSignatureCountException;
8286

8387
List<String> getPrevWarnings();
8488

@@ -101,12 +105,12 @@ default List<String> allWarnings() {
101105
return CollectionUtil.immutableList(result);
102106
}
103107

104-
default B next() {
108+
default B next() throws InvalidSignatureCountException {
105109
validate();
106110
return nextStep();
107111
}
108112

109-
default AssertionResult run() {
113+
default AssertionResult run() throws InvalidSignatureCountException {
110114
if (isFinished()) {
111115
return result().get();
112116
} else {
@@ -330,18 +334,10 @@ public List<String> getWarnings() {
330334

331335
@Override
332336
public void validate() {
333-
if (!
334-
CLIENT_DATA_TYPE.equals(clientData.getType())
335-
) {
336-
final String message = String.format(
337-
"The \"type\" in the client data must be exactly \"%s\", was: %s", CLIENT_DATA_TYPE, clientData.getType()
338-
);
339-
if (validateTypeAttribute) {
340-
throw new IllegalArgumentException(message);
341-
} else {
342-
warnings.add(message);
343-
}
344-
}
337+
assure(CLIENT_DATA_TYPE.equals(clientData.getType()),
338+
"The \"type\" in the client data must be exactly \"%s\", was: %s",
339+
CLIENT_DATA_TYPE, clientData.getType()
340+
);
345341
}
346342

347343
@Override
@@ -553,9 +549,29 @@ public class Step16 implements Step<Step15, Step17> {
553549

554550
@Override
555551
public void validate() {
552+
final ByteArray cose = credential.getPublicKeyCose();
553+
final PublicKey key;
554+
555+
try {
556+
key = WebAuthnCodecs.importCoseP256PublicKey(cose);
557+
} catch (CoseException | IOException e) {
558+
String coseString;
559+
try {
560+
coseString = WebAuthnCodecs.json().writeValueAsString(cose.getBytes());
561+
} catch (JsonProcessingException e2) {
562+
coseString = "(Failed to write as string)";
563+
}
564+
565+
throw new IllegalArgumentException(String.format(
566+
"Failed to decode public key: Credential ID: {} COSE: {}",
567+
credential.getCredentialId().getBase64Url(),
568+
coseString
569+
));
570+
}
571+
556572
if (!
557573
crypto.verifySignature(
558-
credential.getPublicKey(),
574+
key,
559575
signedBytes(),
560576
response.getResponse().getSignature()
561577
)
@@ -581,13 +597,15 @@ public class Step17 implements Step<Step16, Finished> {
581597
private final List<String> prevWarnings;
582598

583599
@Override
584-
public void validate() {
600+
public void validate() throws InvalidSignatureCountException {
585601
if (validateSignatureCounter) {
586-
assure(
587-
signatureCounterValid(),
588-
"Signature counter must increase. Stored value: %s, received value: %s",
589-
storedSignatureCountBefore(), assertionSignatureCount()
590-
);
602+
if (!signatureCounterValid()) {
603+
throw new InvalidSignatureCountException(
604+
response.getId(),
605+
storedSignatureCountBefore() + 1,
606+
assertionSignatureCount()
607+
);
608+
}
591609
}
592610
}
593611

webauthn-server-core/src/main/java/com/yubico/webauthn/FinishRegistrationSteps.java

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,12 @@ final class FinishRegistrationSteps {
6868
private final Optional<ByteArray> callerTokenBindingId;
6969
private final Set<String> origins;
7070
private final String rpId;
71-
private final Boolean allowUntrustedAttestation;
71+
private final boolean allowUntrustedAttestation;
7272
private final Optional<MetadataService> metadataService;
7373
private final CredentialRepository credentialRepository;
7474

7575
@Builder.Default
76-
private final Boolean allowUnrequestedExtensions = false;
77-
@Builder.Default
78-
private final Boolean validateTypeAttribute = true;
76+
private final boolean allowUnrequestedExtensions = false;
7977

8078

8179
public Step1 begin() {
@@ -172,20 +170,10 @@ public class Step3 implements Step<Step4> {
172170

173171
@Override
174172
public void validate() {
175-
final String type = clientData.getType();
176-
177-
if (!CLIENT_DATA_TYPE.equals(type)) {
178-
final String message = String.format(
179-
"The \"type\" in the client data must be exactly \"%s\", was: %s",
180-
CLIENT_DATA_TYPE, clientData.getType()
181-
);
182-
183-
if (validateTypeAttribute) {
184-
throw new IllegalArgumentException(message);
185-
} else {
186-
warnings.add(message);
187-
}
188-
}
173+
assure(CLIENT_DATA_TYPE.equals(clientData.getType()),
174+
"The \"type\" in the client data must be exactly \"%s\", was: %s",
175+
CLIENT_DATA_TYPE, clientData.getType()
176+
);
189177
}
190178

191179
@Override

webauthn-server-core/src/main/java/com/yubico/webauthn/RegisteredCredential.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@
2424

2525
package com.yubico.webauthn;
2626

27+
import com.yubico.webauthn.data.AttestedCredentialData;
2728
import com.yubico.webauthn.data.AuthenticatorAssertionResponse;
2829
import com.yubico.webauthn.data.AuthenticatorData;
2930
import com.yubico.webauthn.data.ByteArray;
3031
import com.yubico.webauthn.data.PublicKeyCredentialDescriptor;
3132
import com.yubico.webauthn.data.UserIdentity;
32-
import java.security.PublicKey;
3333
import lombok.AccessLevel;
3434
import lombok.AllArgsConstructor;
3535
import lombok.Builder;
@@ -71,17 +71,19 @@ public class RegisteredCredential {
7171
private final ByteArray userHandle;
7272

7373
/**
74-
* The public key of the credential.
74+
* The credential public key encoded in COSE_Key format, as defined in Section 7 of <a
75+
* href="https://tools.ietf.org/html/rfc8152">RFC 8152</a>.
7576
*
7677
* <p>
7778
* This is used to verify the {@link AuthenticatorAssertionResponse#getSignature() signature} in authentication
7879
* assertions.
7980
* </p>
8081
*
82+
* @see AttestedCredentialData#getCredentialPublicKey()
8183
* @see RegistrationResult#getPublicKeyCose()
8284
*/
8385
@NonNull
84-
private final PublicKey publicKey;
86+
private final ByteArray publicKeyCose;
8587

8688
/**
8789
* The stored <a href="https://w3c.github.io/webauthn/#signcount">signature count</a> of the credential.
@@ -116,8 +118,8 @@ public Step3 userHandle(ByteArray userHandle) {
116118
}
117119
}
118120
public class Step3 {
119-
public RegisteredCredentialBuilder publicKey(PublicKey publicKey) {
120-
return builder.publicKey(publicKey);
121+
public RegisteredCredentialBuilder publicKeyCose(ByteArray publicKeyCose) {
122+
return builder.publicKeyCose(publicKeyCose);
121123
}
122124
}
123125
}

webauthn-server-core/src/main/java/com/yubico/webauthn/RegistrationResult.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public class RegistrationResult {
8787
* signatures.
8888
* </p>
8989
*
90-
* @see RegisteredCredential#getPublicKey()
90+
* @see RegisteredCredential#getPublicKeyCose()
9191
*/
9292
@NonNull
9393
private final ByteArray publicKeyCose;

0 commit comments

Comments
 (0)