Skip to content

Commit

Permalink
Merge branch 'main' into release-2.5.0
Browse files Browse the repository at this point in the history
  • Loading branch information
emlun committed Jul 4, 2023
2 parents 1c8a8ad + 1576b3d commit 24988c1
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 26 deletions.
3 changes: 3 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ Fixes:
properties are emitted by the `PublicKeyCredential.toJSON()` method added in
WebAuthn Level 3.
* Relaxed Guava dependency version constraint to include major version 32.
* `RelyingParty.finishAssertion` now behaves the same if
`StartAssertionOptions.allowCredentials` is explicitly set to a present, empty
list as when absent.


`webauthn-server-attestation`:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ public void validate() {
request
.getPublicKeyCredentialRequestOptions()
.getAllowCredentials()
.filter(allowCredentials -> !allowCredentials.isEmpty())
.ifPresent(
allowed -> {
assertTrue(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@
import com.yubico.webauthn.data.ByteArray;
import com.yubico.webauthn.data.COSEAlgorithmIdentifier;
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.HashMap;
Expand Down Expand Up @@ -126,14 +128,29 @@ static PublicKey importCosePublicKey(ByteArray key)
// COSE-JAVA is hardcoded to ed25519-java provider ("EdDSA") which would require an
// additional dependency to parse EdDSA keys via the OneKey constructor
return importCoseEdDsaPublicKey(cose);
case 2: // Fall through
case 2:
return importCoseP256PublicKey(cose);
case 3:
return new OneKey(cose).AsPublicKey();
// COSE-JAVA supports RSA in v1.1.0 but not in v1.0.0
return importCoseRsaPublicKey(cose);
default:
throw new IllegalArgumentException("Unsupported key type: " + kty);
}
}

private static PublicKey importCoseRsaPublicKey(CBORObject cose)
throws NoSuchAlgorithmException, InvalidKeySpecException {
RSAPublicKeySpec spec =
new RSAPublicKeySpec(
new BigInteger(1, cose.get(CBORObject.FromObject(-1)).GetByteString()),
new BigInteger(1, cose.get(CBORObject.FromObject(-2)).GetByteString()));
return KeyFactory.getInstance("RSA").generatePublic(spec);
}

private static ECPublicKey importCoseP256PublicKey(CBORObject cose) throws CoseException {
return (ECPublicKey) new OneKey(cose).AsPublicKey();
}

private static PublicKey importCoseEdDsaPublicKey(CBORObject cose)
throws InvalidKeySpecException, NoSuchAlgorithmException {
final int curveId = cose.get(CBORObject.FromObject(-1)).AsInt32();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -594,14 +594,21 @@ class RelyingPartyAssertionSpec
}

it("Succeeds if no credential IDs were requested.") {
val steps = finishAssertion(
allowCredentials = None,
credentialId = new ByteArray(Array(0, 1, 2, 3)),
)
val step: FinishAssertionSteps#Step5 = steps.begin
for {
allowCredentials <- List(
None,
Some(List.empty[PublicKeyCredentialDescriptor].asJava),
)
} {
val steps = finishAssertion(
allowCredentials = allowCredentials,
credentialId = new ByteArray(Array(0, 1, 2, 3)),
)
val step: FinishAssertionSteps#Step5 = steps.begin

step.validations shouldBe a[Success[_]]
step.tryNext shouldBe a[Success[_]]
step.validations shouldBe a[Success[_]]
step.tryNext shouldBe a[Success[_]]
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,17 @@ public void serialize(
throw new RuntimeException(e);
}
});
gen.writeObjectField("extensions", value.getExtensions());
value
.getExtensions()
.ifPresent(
extensions -> {
try {
gen.writeObjectField(
"extensions", JacksonCodecs.cbor().readTree(extensions.EncodeToBytes()));
} catch (IOException e) {
throw new RuntimeException(e);
}
});
gen.writeEndObject();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import com.yubico.webauthn.data.Generators.arbitraryAuthenticatorTransport
import com.yubico.webauthn.data.PublicKeyCredentialRequestOptions
import com.yubico.webauthn.data.RelyingPartyIdentity
import com.yubico.webauthn.data.ResidentKeyRequirement
import com.yubico.webauthn.test.RealExamples
import demo.webauthn.data.AssertionRequestWrapper
import demo.webauthn.data.CredentialRegistration
import demo.webauthn.data.RegistrationRequest
Expand Down Expand Up @@ -91,23 +92,27 @@ class WebAuthnServerSpec
}

it("has a finish method which accepts and outputs JSON.") {
val requestId = ByteArray.fromBase64Url("request1")

val server = newServerWithRegistrationRequest(
RegistrationTestData.FidoU2f.BasicAttestation
)
for {
testData <- List(
RegistrationTestData.FidoU2f.BasicAttestation, // This test case for no particular reason
RealExamples.LargeBlobWrite.asRegistrationTestData, // This test case because it has authenticator extensions
)
} {
val requestId = ByteArray.fromBase64Url("request1")
val server = newServerWithRegistrationRequest(testData)

val authenticationAttestationResponseJson =
"""{"attestationObject":"v2hhdXRoRGF0YVikSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NBAAAFOQABAgMEBQYHCAkKCwwNDg8AIIjjhj6nH3qL2QF3tkUogilFykuaXjJTw35O4m-0NSX0pSJYIA5Nt8eYkLco-NQfKPXaA6dD9UfX_SHaYo-L-YQb78HsAyYBAiFYIOuzRl1o1Hem2jVRYhjkbSeIydhqLln9iltAgsDYjXRTIAFjZm10aGZpZG8tdTJmZ2F0dFN0bXS_Y3g1Y59ZAekwggHlMIIBjKADAgECAgIFOTAKBggqhkjOPQQDAjBqMSYwJAYDVQQDDB1ZdWJpY28gV2ViQXV0aG4gdW5pdCB0ZXN0cyBDQTEPMA0GA1UECgwGWXViaWNvMSIwIAYDVQQLDBlBdXRoZW50aWNhdG9yIEF0dGVzdGF0aW9uMQswCQYDVQQGEwJTRTAeFw0xODA5MDYxNzQyMDBaFw0xODA5MDYxNzQyMDBaMGcxIzAhBgNVBAMMGll1YmljbyBXZWJBdXRobiB1bml0IHRlc3RzMQ8wDQYDVQQKDAZZdWJpY28xIjAgBgNVBAsMGUF1dGhlbnRpY2F0b3IgQXR0ZXN0YXRpb24xCzAJBgNVBAYTAlNFMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJ-8bFED9TnFhaArujgB0foNaV4gQIulP1mC5DO1wvSByw4eOyXujpPHkTw9y5e5J2J3N9coSReZJgBRpvFzYD6MlMCMwIQYLKwYBBAGC5RwBAQQEEgQQAAECAwQFBgcICQoLDA0ODzAKBggqhkjOPQQDAgNHADBEAiB4bL25EH06vPBOVnReObXrS910ARVOLJPPnKNoZbe64gIgX1Rg5oydH45zEMEVDjNPStwv6Z3nE_isMeY-szlQhv3_Y3NpZ1hHMEUCIQDBs1nbSuuKQ6yoHMQoRp8eCT_HZvR45F_aVP6qFX_wKgIgMCL58bv-crkLwTwiEL9ibCV4nDYM-DZuW5_BFCJbcxn__w","clientDataJSON":"eyJjaGFsbGVuZ2UiOiJBQUVCQWdNRkNBMFZJamRaRUdsNVlscyIsIm9yaWdpbiI6ImxvY2FsaG9zdCIsInR5cGUiOiJ3ZWJhdXRobi5jcmVhdGUiLCJ0b2tlbkJpbmRpbmciOnsic3RhdHVzIjoic3VwcG9ydGVkIn19"}"""
val publicKeyCredentialJson =
s"""{"id":"iOOGPqcfeovZAXe2RSiCKUXKS5peMlPDfk7ib7Q1JfQ","response":${authenticationAttestationResponseJson},"clientExtensionResults":{},"type":"public-key"}"""
val responseJson =
s"""{"requestId":"${requestId.getBase64Url}","credential":${publicKeyCredentialJson}}"""
val authenticationAttestationResponseJson =
s"""{"attestationObject":"${testData.attestationObject.getBase64Url}","clientDataJSON":"${testData.clientDataJsonBytes.getBase64Url}"}"""
val publicKeyCredentialJson =
s"""{"id":"${testData.response.getId.getBase64Url}","response":${authenticationAttestationResponseJson},"clientExtensionResults":{},"type":"public-key"}"""
val responseJson =
s"""{"requestId":"${requestId.getBase64Url}","credential":${publicKeyCredentialJson}}"""

val response = server.finishRegistration(responseJson)
val json = jsonMapper.writeValueAsString(response.right.get)
val response = server.finishRegistration(responseJson)
val json = jsonMapper.writeValueAsString(response.right.get)

json should not be null
json should not be null
}
}

}
Expand Down Expand Up @@ -393,8 +398,8 @@ class WebAuthnServerSpec
new InMemoryRegistrationStorage,
registrationRequests,
newCache(),
rpId,
origins,
testData.rpId,
Set(testData.response.getResponse.getClientData.getOrigin).asJava,
)
}

Expand Down

0 comments on commit 24988c1

Please sign in to comment.