Skip to content

Commit 24988c1

Browse files
committed
Merge branch 'main' into release-2.5.0
2 parents 1c8a8ad + 1576b3d commit 24988c1

File tree

6 files changed

+69
-26
lines changed

6 files changed

+69
-26
lines changed

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ Fixes:
2424
properties are emitted by the `PublicKeyCredential.toJSON()` method added in
2525
WebAuthn Level 3.
2626
* Relaxed Guava dependency version constraint to include major version 32.
27+
* `RelyingParty.finishAssertion` now behaves the same if
28+
`StartAssertionOptions.allowCredentials` is explicitly set to a present, empty
29+
list as when absent.
2730

2831

2932
`webauthn-server-attestation`:

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/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)