diff --git a/build.savant b/build.savant
index e702bb5..a8060cc 100644
--- a/build.savant
+++ b/build.savant
@@ -17,7 +17,7 @@
savantVersion = "1.0.0"
jacksonVersion = "2.13.2"
-project(group: "io.fusionauth", name: "fusionauth-jwt", version: "5.1.1", licenses: ["ApacheV2_0"]) {
+project(group: "io.fusionauth", name: "fusionauth-jwt", version: "5.1.2", licenses: ["ApacheV2_0"]) {
workflow {
fetch {
diff --git a/pom.xml b/pom.xml
index 1a8743d..3c83562 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
io.fusionauth
fusionauth-jwt
- 5.1.1
+ 5.1.2
jar
FusionAuth JWT
diff --git a/src/main/java/io/fusionauth/jwt/ec/ECVerifier.java b/src/main/java/io/fusionauth/jwt/ec/ECVerifier.java
index 0d91106..4fcff65 100644
--- a/src/main/java/io/fusionauth/jwt/ec/ECVerifier.java
+++ b/src/main/java/io/fusionauth/jwt/ec/ECVerifier.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2020, FusionAuth, All Rights Reserved
+ * Copyright (c) 2018-2022, FusionAuth, All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -178,11 +178,36 @@ public boolean canVerify(Algorithm algorithm) {
}
}
+ private void checkFor_CVE_2022_21449(byte[] signature) {
+ int half = signature.length / 2;
+
+ boolean rOk = false;
+ boolean sOk = false;
+ for (int i = 0; i < signature.length; i++) {
+ if (i < half) {
+ rOk = signature[i] != 0;
+ if (rOk) {
+ i = half - 1;
+ }
+ } else {
+ sOk = signature[i] != 0;
+ if (sOk) {
+ break;
+ }
+ }
+ }
+
+ if (!rOk || !sOk) {
+ throw new InvalidJWTSignatureException();
+ }
+ }
+
@Override
public void verify(Algorithm algorithm, byte[] message, byte[] signature) {
Objects.requireNonNull(algorithm);
Objects.requireNonNull(message);
Objects.requireNonNull(signature);
+ checkFor_CVE_2022_21449(signature);
try {
Signature verifier = cryptoProvider.getSignatureInstance(algorithm.getName());
diff --git a/src/test/java/io/fusionauth/jwt/VulnerabilityTest.java b/src/test/java/io/fusionauth/jwt/VulnerabilityTest.java
index 7a24967..b32bb06 100644
--- a/src/test/java/io/fusionauth/jwt/VulnerabilityTest.java
+++ b/src/test/java/io/fusionauth/jwt/VulnerabilityTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2019, FusionAuth, All Rights Reserved
+ * Copyright (c) 2018-2022, FusionAuth, All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,8 @@
package io.fusionauth.jwt;
import io.fusionauth.jwt.domain.JWT;
+import io.fusionauth.jwt.ec.ECSigner;
+import io.fusionauth.jwt.ec.ECVerifier;
import io.fusionauth.jwt.hmac.HMACSigner;
import io.fusionauth.jwt.hmac.HMACVerifier;
import io.fusionauth.jwt.rsa.RSAVerifier;
@@ -26,6 +28,8 @@
import java.nio.file.Paths;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
+import java.util.Arrays;
+import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
@@ -49,6 +53,29 @@ public void test_SignedWithoutSignature() {
expectException(MissingSignatureException.class, () -> JWT.getDecoder().decode(encodedJWTNoSignature));
}
+ @Test
+ public void test_ECDSA_CVE_2022_21449() {
+ // Note this test will always fail when run on Java 8, it will fail on Java 15, 16 and 17.
+ JWT inputJwt = new JWT()
+ .setSubject("123456789")
+ .setIssuedAt(ZonedDateTime.now(ZoneOffset.UTC))
+ .setExpiration(ZonedDateTime.now(ZoneOffset.UTC).plusHours(2));
+
+ // Sign it using ECDSA 256
+ for (String alg : Arrays.asList("256", "384", "521")) {
+ System.out.println("\n\n\n\n");
+ Signer signer = alg.equals("256")
+ ? ECSigner.newSHA256Signer(readFile("ec_private_key_p_" + alg + ".pem"))
+ : alg.equals("384")
+ ? ECSigner.newSHA384Signer(readFile("ec_private_key_p_" + alg + ".pem"))
+ : ECSigner.newSHA512Signer(readFile("ec_private_key_p_" + alg + ".pem"));
+
+ String encodedJWT = JWT.getEncoder().encode(inputJwt, signer);
+ String hackedEncodedJWT = encodedJWT.substring(0, encodedJWT.lastIndexOf('.') + 1) + Base64.getUrlEncoder().encodeToString(new byte[64]);
+ expectException(InvalidJWTSignatureException.class, () -> JWT.getDecoder().decode(hackedEncodedJWT, ECVerifier.newVerifier(readFile("ec_public_key_p_" + alg + ".pem"))));
+ }
+ }
+
@Test
public void test_encodedJwtWithSignatureRemoved() {
// Sign a JWT and then attempt to verify it using None.