Skip to content

Commit 984109e

Browse files
committed
Merge branch 'main' into release-2.6.0
2 parents 7c8fd3b + 345762b commit 984109e

File tree

10 files changed

+176
-133
lines changed

10 files changed

+176
-133
lines changed

.github/workflows/release-verify-signatures.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939

4040
strategy:
4141
matrix:
42-
java: [17]
42+
java: ["17.0.7"]
4343
distribution: [temurin, zulu, microsoft]
4444

4545
steps:

NEWS

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
https://github.com/Yubico/java-webauthn-server/pull/299
77

88

9-
== Version 2.5.0 (unreleased) ==
9+
== Version 2.5.0 ==
1010

1111
`webauthn-server-core`:
1212

@@ -32,6 +32,9 @@ Fixes:
3232
properties are emitted by the `PublicKeyCredential.toJSON()` method added in
3333
WebAuthn Level 3.
3434
* Relaxed Guava dependency version constraint to include major version 32.
35+
* `RelyingParty.finishAssertion` now behaves the same if
36+
`StartAssertionOptions.allowCredentials` is explicitly set to a present, empty
37+
list as when absent.
3538

3639

3740
`webauthn-server-attestation`:

README

Lines changed: 53 additions & 53 deletions
Large diffs are not rendered by default.

webauthn-server-attestation/README.adoc

Lines changed: 47 additions & 47 deletions
Large diffs are not rendered by default.

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ public void validate() {
111111
request
112112
.getPublicKeyCredentialRequestOptions()
113113
.getAllowCredentials()
114+
.filter(allowCredentials -> !allowCredentials.isEmpty())
114115
.ifPresent(
115116
allowed -> {
116117
assertTrue(

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

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,13 @@
3131
import com.yubico.webauthn.data.ByteArray;
3232
import com.yubico.webauthn.data.COSEAlgorithmIdentifier;
3333
import java.io.IOException;
34+
import java.math.BigInteger;
3435
import java.security.KeyFactory;
3536
import java.security.NoSuchAlgorithmException;
3637
import java.security.PublicKey;
3738
import java.security.interfaces.ECPublicKey;
3839
import java.security.spec.InvalidKeySpecException;
40+
import java.security.spec.RSAPublicKeySpec;
3941
import java.security.spec.X509EncodedKeySpec;
4042
import java.util.Arrays;
4143
import java.util.HashMap;
@@ -126,14 +128,29 @@ static PublicKey importCosePublicKey(ByteArray key)
126128
// COSE-JAVA is hardcoded to ed25519-java provider ("EdDSA") which would require an
127129
// additional dependency to parse EdDSA keys via the OneKey constructor
128130
return importCoseEdDsaPublicKey(cose);
129-
case 2: // Fall through
131+
case 2:
132+
return importCoseP256PublicKey(cose);
130133
case 3:
131-
return new OneKey(cose).AsPublicKey();
134+
// COSE-JAVA supports RSA in v1.1.0 but not in v1.0.0
135+
return importCoseRsaPublicKey(cose);
132136
default:
133137
throw new IllegalArgumentException("Unsupported key type: " + kty);
134138
}
135139
}
136140

141+
private static PublicKey importCoseRsaPublicKey(CBORObject cose)
142+
throws NoSuchAlgorithmException, InvalidKeySpecException {
143+
RSAPublicKeySpec spec =
144+
new RSAPublicKeySpec(
145+
new BigInteger(1, cose.get(CBORObject.FromObject(-1)).GetByteString()),
146+
new BigInteger(1, cose.get(CBORObject.FromObject(-2)).GetByteString()));
147+
return KeyFactory.getInstance("RSA").generatePublic(spec);
148+
}
149+
150+
private static ECPublicKey importCoseP256PublicKey(CBORObject cose) throws CoseException {
151+
return (ECPublicKey) new OneKey(cose).AsPublicKey();
152+
}
153+
137154
private static PublicKey importCoseEdDsaPublicKey(CBORObject cose)
138155
throws InvalidKeySpecException, NoSuchAlgorithmException {
139156
final int curveId = cose.get(CBORObject.FromObject(-1)).AsInt32();

webauthn-server-core/src/test/scala/com/yubico/webauthn/RelyingPartyAssertionSpec.scala

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -594,14 +594,21 @@ class RelyingPartyAssertionSpec
594594
}
595595

596596
it("Succeeds if no credential IDs were requested.") {
597-
val steps = finishAssertion(
598-
allowCredentials = None,
599-
credentialId = new ByteArray(Array(0, 1, 2, 3)),
600-
)
601-
val step: FinishAssertionSteps#Step5 = steps.begin
597+
for {
598+
allowCredentials <- List(
599+
None,
600+
Some(List.empty[PublicKeyCredentialDescriptor].asJava),
601+
)
602+
} {
603+
val steps = finishAssertion(
604+
allowCredentials = allowCredentials,
605+
credentialId = new ByteArray(Array(0, 1, 2, 3)),
606+
)
607+
val step: FinishAssertionSteps#Step5 = steps.begin
602608

603-
step.validations shouldBe a[Success[_]]
604-
step.tryNext shouldBe a[Success[_]]
609+
step.validations shouldBe a[Success[_]]
610+
step.tryNext shouldBe a[Success[_]]
611+
}
605612
}
606613
}
607614

webauthn-server-demo/README

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ layer.
4444
This layer manages the general architecture of the system, and is where most
4545
business logic and integration code would go. The demo server implements the
4646
"persistent" storage of users and credential registrations - the
47-
link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`]
47+
link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`]
4848
integration point - as the
4949
link:src/main/java/demo/webauthn/InMemoryRegistrationStorage.java[`InMemoryRegistrationStorage`]
5050
class, which simply keeps them stored in memory for a limited time. The
@@ -58,7 +58,7 @@ would be specific to a particular Relying Party (RP) would go in this layer.
5858
- The server layer in turn calls the *library layer*, which is where the
5959
link:../webauthn-server-core/[`webauthn-server-core`]
6060
library gets involved. The entry point into the library is the
61-
link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/RelyingParty.html[`RelyingParty`]
61+
link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.html[`RelyingParty`]
6262
class.
6363
+
6464
This layer implements the Web Authentication
@@ -69,11 +69,11 @@ and exposes integration points for storage of challenges and credentials. Some
6969
notable integration points are:
7070
+
7171
** The library user must provide an implementation of the
72-
link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`]
72+
link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`]
7373
interface to use for looking up stored public keys, user handles and signature
7474
counters.
7575
** The library user can optionally provide an instance of the
76-
link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/attestation/AttestationTrustSource.html[`AttestationTrustSource`]
76+
link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/attestation/AttestationTrustSource.html[`AttestationTrustSource`]
7777
interface to enable identification and validation of authenticator models. This
7878
instance is then used to look up trusted attestation root certificates. The
7979
link:../webauthn-server-attestation/[`webauthn-server-attestation`]
@@ -158,7 +158,7 @@ correct environment.
158158
Authentication demo'`
159159

160160
- `YUBICO_WEBAUTHN_USE_FIDO_MDS`: If set to `true` (case-insensitive), use
161-
https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`]
161+
https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`]
162162
from the link:../webauthn-server-attestation[`webauthn-server-attestation`]
163163
module as a source of attestation data in addition to the static JSON file
164164
bundled with the demo. This will write cache files to the

webauthn-server-demo/src/main/java/demo/webauthn/WebAuthnServer.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,17 @@ public void serialize(
647647
throw new RuntimeException(e);
648648
}
649649
});
650-
gen.writeObjectField("extensions", value.getExtensions());
650+
value
651+
.getExtensions()
652+
.ifPresent(
653+
extensions -> {
654+
try {
655+
gen.writeObjectField(
656+
"extensions", JacksonCodecs.cbor().readTree(extensions.EncodeToBytes()));
657+
} catch (IOException e) {
658+
throw new RuntimeException(e);
659+
}
660+
});
651661
gen.writeEndObject();
652662
}
653663
}

webauthn-server-demo/src/test/scala/demo/webauthn/WebAuthnServerSpec.scala

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import com.yubico.webauthn.data.Generators.arbitraryAuthenticatorTransport
3838
import com.yubico.webauthn.data.PublicKeyCredentialRequestOptions
3939
import com.yubico.webauthn.data.RelyingPartyIdentity
4040
import com.yubico.webauthn.data.ResidentKeyRequirement
41+
import com.yubico.webauthn.test.RealExamples
4142
import demo.webauthn.data.AssertionRequestWrapper
4243
import demo.webauthn.data.CredentialRegistration
4344
import demo.webauthn.data.RegistrationRequest
@@ -91,23 +92,27 @@ class WebAuthnServerSpec
9192
}
9293

9394
it("has a finish method which accepts and outputs JSON.") {
94-
val requestId = ByteArray.fromBase64Url("request1")
95-
96-
val server = newServerWithRegistrationRequest(
97-
RegistrationTestData.FidoU2f.BasicAttestation
98-
)
95+
for {
96+
testData <- List(
97+
RegistrationTestData.FidoU2f.BasicAttestation, // This test case for no particular reason
98+
RealExamples.LargeBlobWrite.asRegistrationTestData, // This test case because it has authenticator extensions
99+
)
100+
} {
101+
val requestId = ByteArray.fromBase64Url("request1")
102+
val server = newServerWithRegistrationRequest(testData)
99103

100-
val authenticationAttestationResponseJson =
101-
"""{"attestationObject":"v2hhdXRoRGF0YVikSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NBAAAFOQABAgMEBQYHCAkKCwwNDg8AIIjjhj6nH3qL2QF3tkUogilFykuaXjJTw35O4m-0NSX0pSJYIA5Nt8eYkLco-NQfKPXaA6dD9UfX_SHaYo-L-YQb78HsAyYBAiFYIOuzRl1o1Hem2jVRYhjkbSeIydhqLln9iltAgsDYjXRTIAFjZm10aGZpZG8tdTJmZ2F0dFN0bXS_Y3g1Y59ZAekwggHlMIIBjKADAgECAgIFOTAKBggqhkjOPQQDAjBqMSYwJAYDVQQDDB1ZdWJpY28gV2ViQXV0aG4gdW5pdCB0ZXN0cyBDQTEPMA0GA1UECgwGWXViaWNvMSIwIAYDVQQLDBlBdXRoZW50aWNhdG9yIEF0dGVzdGF0aW9uMQswCQYDVQQGEwJTRTAeFw0xODA5MDYxNzQyMDBaFw0xODA5MDYxNzQyMDBaMGcxIzAhBgNVBAMMGll1YmljbyBXZWJBdXRobiB1bml0IHRlc3RzMQ8wDQYDVQQKDAZZdWJpY28xIjAgBgNVBAsMGUF1dGhlbnRpY2F0b3IgQXR0ZXN0YXRpb24xCzAJBgNVBAYTAlNFMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJ-8bFED9TnFhaArujgB0foNaV4gQIulP1mC5DO1wvSByw4eOyXujpPHkTw9y5e5J2J3N9coSReZJgBRpvFzYD6MlMCMwIQYLKwYBBAGC5RwBAQQEEgQQAAECAwQFBgcICQoLDA0ODzAKBggqhkjOPQQDAgNHADBEAiB4bL25EH06vPBOVnReObXrS910ARVOLJPPnKNoZbe64gIgX1Rg5oydH45zEMEVDjNPStwv6Z3nE_isMeY-szlQhv3_Y3NpZ1hHMEUCIQDBs1nbSuuKQ6yoHMQoRp8eCT_HZvR45F_aVP6qFX_wKgIgMCL58bv-crkLwTwiEL9ibCV4nDYM-DZuW5_BFCJbcxn__w","clientDataJSON":"eyJjaGFsbGVuZ2UiOiJBQUVCQWdNRkNBMFZJamRaRUdsNVlscyIsIm9yaWdpbiI6ImxvY2FsaG9zdCIsInR5cGUiOiJ3ZWJhdXRobi5jcmVhdGUiLCJ0b2tlbkJpbmRpbmciOnsic3RhdHVzIjoic3VwcG9ydGVkIn19"}"""
102-
val publicKeyCredentialJson =
103-
s"""{"id":"iOOGPqcfeovZAXe2RSiCKUXKS5peMlPDfk7ib7Q1JfQ","response":${authenticationAttestationResponseJson},"clientExtensionResults":{},"type":"public-key"}"""
104-
val responseJson =
105-
s"""{"requestId":"${requestId.getBase64Url}","credential":${publicKeyCredentialJson}}"""
104+
val authenticationAttestationResponseJson =
105+
s"""{"attestationObject":"${testData.attestationObject.getBase64Url}","clientDataJSON":"${testData.clientDataJsonBytes.getBase64Url}"}"""
106+
val publicKeyCredentialJson =
107+
s"""{"id":"${testData.response.getId.getBase64Url}","response":${authenticationAttestationResponseJson},"clientExtensionResults":{},"type":"public-key"}"""
108+
val responseJson =
109+
s"""{"requestId":"${requestId.getBase64Url}","credential":${publicKeyCredentialJson}}"""
106110

107-
val response = server.finishRegistration(responseJson)
108-
val json = jsonMapper.writeValueAsString(response.right.get)
111+
val response = server.finishRegistration(responseJson)
112+
val json = jsonMapper.writeValueAsString(response.right.get)
109113

110-
json should not be null
114+
json should not be null
115+
}
111116
}
112117

113118
}
@@ -393,8 +398,8 @@ class WebAuthnServerSpec
393398
new InMemoryRegistrationStorage,
394399
registrationRequests,
395400
newCache(),
396-
rpId,
397-
origins,
401+
testData.rpId,
402+
Set(testData.response.getResponse.getClientData.getOrigin).asJava,
398403
)
399404
}
400405

0 commit comments

Comments
 (0)