CVE-2017-12973 (published 2017-08-20T16:29Z)

Nimbus JOSE+JWT before 4.39 proceeds improperly after detection of an invalid HMAC in authenticated AES-CBC decryption, which allows attackers to conduct a padding oracle attack.

Severity: MEDIUM
CVSS v2 base score: 4.3 (AV:N/AC:M/Au:N/C:P/I:N/A:N)
CVSS v3 base score: 3.1, LOW (CVSS:3.0/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:N/A:N)

Weakness information:
cwe_id: CWE-354
cwe_name: Improper Validation of Integrity Check Value
description: The product does not validate or incorrectly validates the integrity check values or "checksums" of a message. This may prevent it from detecting if the data has been modified or corrupted in transmission.
extended_description: Improper validation of checksums before use results in an unnecessary risk that can easily be mitigated. The protocol specification describes the algorithm used for calculating the checksum. It is then a simple matter of implementing the calculation and verifying that the calculated checksum and the received checksum match. Improper verification of the calculated checksum and the received checksum can lead to far greater consequences.
         

Fix information:
repo_url: https://bitbucket.org/connect2id/nimbus-jose-jwt
commit_details: repo_url: https://bitbucket.org/connect2id/nimbus-jose-jwt
author: Vladimir Dzhuvinov
author_date: 2017-06-01 10:40:56+03:00
msg: Fixes HMAC return on authenticated AESCBC decrypt (iss #223)
repository: repo_url: https://bitbucket.org/connect2id/nimbus-jose-jwt
repo_name: visit repo url
description: visit repo url
repo_language: visit repo url
owner: connect2id
file_changes: filename: AESCBC.java
change_type: ModificationType.MODIFY
diff: @@ -46,7 +46,7 @@ import net.jcip.annotations.ThreadSafe;
  *
  * @author Vladimir Dzhuvinov
  * @author Axel Nennker
- * @version 2015-05-26
+ * @version 2017-05-30
  */
 @ThreadSafe
 class AESCBC {
@@ -351,21 +351,11 @@ class AESCBC {
 
 		byte[] expectedAuthTag = Arrays.copyOf(hmac, compositeKey.getTruncatedMACByteLength());
 
-		boolean macCheckPassed = true;
-
 		if (! ConstantTimeUtils.areEqual(expectedAuthTag, authTag)) {
-			// Thwart timing attacks by delaying exception until after decryption
-			macCheckPassed = false;
-		}
-
-		byte[] plainText = decrypt(compositeKey.getAESKey(), iv, cipherText, ceProvider);
-
-		if (! macCheckPassed) {
-
 			throw new JOSEException("MAC check failed");
 		}
 
-		return plainText;
+		return decrypt(compositeKey.getAESKey(), iv, cipherText, ceProvider);
 	}
 
 
@@ -413,26 +403,23 @@ class AESCBC {
 
 			epv = new Base64URL((String)header.getCustomParam("epv")).decode();
 		}
-
-		SecretKey cekAlt = LegacyConcatKDF.generateCEK(secretKey, header.getEncryptionMethod(), epu, epv);
-
-		final byte[] plainText = AESCBC.decrypt(cekAlt, iv.decode(), cipherText.decode(), ceProvider);
-
+		
 		SecretKey cik = LegacyConcatKDF.generateCIK(secretKey, header.getEncryptionMethod(), epu, epv);
-
+		
 		String macInput = header.toBase64URL().toString() + "." +
 			encryptedKey.toString() + "." +
 			iv.toString() + "." +
 			cipherText.toString();
-
+		
 		byte[] mac = HMAC.compute(cik, macInput.getBytes(), macProvider);
-
+		
 		if (! ConstantTimeUtils.areEqual(authTag.decode(), mac)) {
-
-			throw new JOSEException("HMAC integrity check failed");
+			throw new JOSEException("MAC check failed");
 		}
 
-		return plainText;
+		SecretKey cekAlt = LegacyConcatKDF.generateCEK(secretKey, header.getEncryptionMethod(), epu, epv);
+
+		return AESCBC.decrypt(cekAlt, iv.decode(), cipherText.decode(), ceProvider);
 	}
 
 

programming_language: Java
filename: AESCBCTest.java
change_type: ModificationType.MODIFY
diff: @@ -21,6 +21,11 @@ package com.nimbusds.jose.crypto;
 import javax.crypto.SecretKey;
 import javax.crypto.spec.SecretKeySpec;
 
+import com.nimbusds.jose.EncryptionMethod;
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWEAlgorithm;
+import com.nimbusds.jose.JWEHeader;
+import com.nimbusds.jose.util.Base64URL;
 import junit.framework.TestCase;
 
 import org.junit.Assert;
@@ -31,7 +36,7 @@ import org.junit.Assert;
  * vectors from draft-ietf-jose-json-web-algorithms-10, appendix C.
  *
  * @author Vladimir Dzhuvinov
- * @version 2015-05-17
+ * @version 2017-05-30
  */
 public class AESCBCTest extends TestCase {
 
@@ -177,4 +182,65 @@ public class AESCBCTest extends TestCase {
 		Assert.assertArrayEquals("Cipher text", CIPHER_TEXT_512, act.getCipherText());
 		Assert.assertArrayEquals("Auth tag", AUTH_TAG_512, act.getAuthenticationTag());
 	}
+	
+	
+	public void testCBCPaddingOracleAttack()
+		throws Exception {
+		
+		SecretKey inputKey = new SecretKeySpec(INPUT_KEY_256, "AES");
+		
+		Assert.assertArrayEquals("Input key", INPUT_KEY_256, inputKey.getEncoded());
+		
+		AuthenticatedCipherText act = AESCBC.encryptAuthenticated(inputKey, IV, PLAIN_TEXT, AAD, null, null);
+		
+		byte[] cipherText = act.getCipherText();
+		
+		// Now change the cipher text to make CBC padding invalid.
+		cipherText[cipherText.length - 1] ^= 0x01;
+		
+		try {
+			AESCBC.decryptAuthenticated(inputKey, IV, cipherText, AAD, act.getAuthenticationTag(), null, null);
+		} catch (JOSEException e) {
+			assertEquals("MAC check failed", e.getMessage());
+		}
+	}
+	
+	
+	public void testCBCPaddingOracleAttackOldConcatKDF()
+		throws Exception {
+		
+		SecretKey inputKey = new SecretKeySpec(INPUT_KEY_256, "AES");
+		
+		Assert.assertArrayEquals("Input key", INPUT_KEY_256, inputKey.getEncoded());
+		
+		AuthenticatedCipherText act = AESCBC.encryptWithConcatKDF(
+			new JWEHeader(JWEAlgorithm.RSA1_5, EncryptionMethod.A128CBC_HS256_DEPRECATED),
+			new SecretKeySpec(INPUT_KEY_256, "AES"),
+			Base64URL.encode(INPUT_KEY_256), // mock
+			IV,
+			PLAIN_TEXT,
+			null,
+			null
+			);
+		
+		byte[] cipherText = act.getCipherText();
+		
+		// Now change the cipher text to make CBC padding invalid.
+		cipherText[cipherText.length - 1] ^= 0x01;
+		
+		try {
+			AESCBC.decryptWithConcatKDF(
+				new JWEHeader(JWEAlgorithm.RSA1_5, EncryptionMethod.A128CBC_HS256_DEPRECATED),
+				new SecretKeySpec(INPUT_KEY_256, "AES"),
+				Base64URL.encode(INPUT_KEY_256), // mock
+				Base64URL.encode(IV),
+				Base64URL.encode(cipherText),
+				Base64URL.encode(act.getAuthenticationTag()),
+				null,
+				null
+			);
+		} catch (JOSEException e) {
+			assertEquals("MAC check failed", e.getMessage());
+		}
+	}
 }
\ No newline at end of file

programming_language: Java