mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
159 lines
7.0 KiB
Java
159 lines
7.0 KiB
Java
|
|
import java.security.*;
|
|
import java.util.Arrays;
|
|
import javax.crypto.Cipher;
|
|
import javax.crypto.Mac;
|
|
import javax.crypto.SecretKey;
|
|
import javax.crypto.spec.IvParameterSpec;
|
|
import javax.crypto.spec.SecretKeySpec;
|
|
|
|
class BadMacUse {
|
|
|
|
private byte[] generateSalt(int length) {
|
|
byte[] salt = new byte[length];
|
|
new SecureRandom().nextBytes(salt);
|
|
return salt;
|
|
}
|
|
|
|
public void CipherThenMac(byte[] encryptionKeyBytes, byte[] macKeyBytes) throws Exception {
|
|
// Create keys directly from provided byte arrays
|
|
SecretKey encryptionKey = new SecretKeySpec(encryptionKeyBytes, "AES");
|
|
SecretKey macKey = new SecretKeySpec(macKeyBytes, "HmacSHA256");
|
|
|
|
// Encrypt some sample data using the encryption key
|
|
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
|
|
cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, new SecureRandom());
|
|
byte[] plaintext = "Further Use Test Data".getBytes();
|
|
byte[] ciphertext = cipher.doFinal(plaintext);
|
|
|
|
// Compute HMAC over the ciphertext using the MAC key
|
|
Mac mac = Mac.getInstance("HmacSHA256");
|
|
mac.init(macKey);
|
|
byte[] computedMac = mac.doFinal(ciphertext);
|
|
|
|
// Concatenate ciphertext and MAC
|
|
byte[] output = new byte[ciphertext.length + computedMac.length];
|
|
System.arraycopy(ciphertext, 0, output, 0, ciphertext.length);
|
|
System.arraycopy(computedMac, 0, output, ciphertext.length, computedMac.length);
|
|
}
|
|
|
|
public void BadDecryptThenMacOnPlaintextVerify(byte[] encryptionKeyBytes, byte[] macKeyBytes, byte[] input) throws Exception {
|
|
// Split input into ciphertext and MAC
|
|
int macLength = 32; // HMAC-SHA256 output length
|
|
byte[] ciphertext = Arrays.copyOfRange(input, 0, input.length - macLength);
|
|
byte[] receivedMac = Arrays.copyOfRange(input, input.length - macLength, input.length);
|
|
|
|
// Decrypt first (unsafe)
|
|
SecretKey encryptionKey = new SecretKeySpec(encryptionKeyBytes, "AES");
|
|
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
|
|
cipher.init(Cipher.DECRYPT_MODE, encryptionKey, new SecureRandom());
|
|
byte[] plaintext = cipher.doFinal(ciphertext); // $Source
|
|
|
|
// Now verify MAC (too late)
|
|
SecretKey macKey = new SecretKeySpec(macKeyBytes, "HmacSHA256");
|
|
Mac mac = Mac.getInstance("HmacSHA256");
|
|
mac.init(macKey);
|
|
byte[] computedMac = mac.doFinal(plaintext); // $Alert[java/quantum/examples/bad-mac-order-decrypt-to-mac]
|
|
|
|
if (!MessageDigest.isEqual(receivedMac, computedMac)) {
|
|
throw new SecurityException("MAC verification failed");
|
|
}
|
|
}
|
|
|
|
public void BadMacOnPlaintext(byte[] encryptionKeyBytes, byte[] macKeyBytes, byte[] plaintext) throws Exception {// $Source
|
|
// Create keys directly from provided byte arrays
|
|
SecretKey encryptionKey = new SecretKeySpec(encryptionKeyBytes, "AES");
|
|
SecretKey macKey = new SecretKeySpec(macKeyBytes, "HmacSHA256");
|
|
|
|
// BAD Compute MAC over plaintext (not ciphertext)
|
|
Mac mac = Mac.getInstance("HmacSHA256");
|
|
mac.init(macKey);
|
|
byte[] computedMac = mac.doFinal(plaintext); // Integrity not tied to encrypted data
|
|
|
|
// Encrypt the plaintext
|
|
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
|
|
cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, new SecureRandom());
|
|
byte[] ciphertext = cipher.doFinal(plaintext); // $Alert[java/quantum/examples/bad-mac-order-encrypt-plaintext-also-in-mac]
|
|
|
|
// Concatenate ciphertext and MAC
|
|
byte[] output = new byte[ciphertext.length + computedMac.length];
|
|
System.arraycopy(ciphertext, 0, output, 0, ciphertext.length);
|
|
System.arraycopy(computedMac, 0, output, ciphertext.length, computedMac.length);
|
|
}
|
|
|
|
public byte[] cipherOperationWrapper(byte[] bytes, byte[] encryptionKeyBytes, byte[] iv, int mode)
|
|
throws Exception {
|
|
|
|
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
|
SecretKeySpec secretKeySpec = new SecretKeySpec(encryptionKeyBytes, "AES");
|
|
|
|
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
|
|
cipher.init(mode, secretKeySpec, ivParameterSpec);
|
|
return cipher.doFinal(bytes);
|
|
}
|
|
|
|
/**
|
|
* A use of the cipher operation wrapper for decryption to throw off the
|
|
* analysis
|
|
*/
|
|
public byte[] decryptUsingWrapper(byte[] ciphertext, byte[] encryptionKeyBytes, byte[] iv) throws Exception {
|
|
return cipherOperationWrapper(ciphertext, encryptionKeyBytes, iv, Cipher.DECRYPT_MODE);
|
|
}
|
|
|
|
/**
|
|
* A use of the cipher operation wrapper for encryption to throw off the
|
|
* analysis
|
|
*/
|
|
public byte[] encryptUsingWrapper(byte[] plaintext, byte[] encryptionKeyBytes, byte[] iv) throws Exception {
|
|
return cipherOperationWrapper(plaintext, encryptionKeyBytes, iv, Cipher.ENCRYPT_MODE);
|
|
}
|
|
|
|
/**
|
|
* Encrypt then mac using the wrapper function
|
|
*/
|
|
public byte[] falsePositiveDecryptToMac(byte[] encryptionKeyBytes, byte[] macKeyBytes, byte[] plaintext) throws Exception {
|
|
// Encrypt the plaintext
|
|
byte[] iv = new byte[16];
|
|
new SecureRandom().nextBytes(iv);
|
|
byte[] ciphertext = cipherOperationWrapper(plaintext, encryptionKeyBytes, iv, Cipher.ENCRYPT_MODE);
|
|
|
|
// Compute HMAC over the ciphertext using the MAC key
|
|
SecretKey macKey = new SecretKeySpec(macKeyBytes, "HmacSHA256");
|
|
Mac mac = Mac.getInstance("HmacSHA256");
|
|
mac.init(macKey);
|
|
byte[] computedMac = mac.doFinal(ciphertext); // False Positive
|
|
|
|
// Concatenate ciphertext and MAC
|
|
byte[] output = new byte[ciphertext.length + computedMac.length];
|
|
System.arraycopy(ciphertext, 0, output, 0, ciphertext.length);
|
|
System.arraycopy(computedMac, 0, output, ciphertext.length, computedMac.length);
|
|
return output;
|
|
}
|
|
|
|
|
|
/**
|
|
* Correct inputs to a decrypt and MAC operation, but the ordering is unsafe.
|
|
* The function decrypts THEN computes the MAC on the plaintext.
|
|
* It should have the MAC computed on the ciphertext first.
|
|
*/
|
|
public void decryptThenMac(byte[] encryptionKeyBytes, byte[] macKeyBytes, byte[] input) throws Exception {
|
|
// Split input into ciphertext and MAC
|
|
int macLength = 32; // HMAC-SHA256 output length
|
|
byte[] ciphertext = Arrays.copyOfRange(input, 0, input.length - macLength);
|
|
byte[] receivedMac = Arrays.copyOfRange(input, input.length - macLength, input.length);
|
|
|
|
// Decrypt first (unsafe)
|
|
byte[] plaintext = decryptUsingWrapper(ciphertext, encryptionKeyBytes, new byte[16]); // $Source
|
|
|
|
// Now verify MAC (too late)
|
|
SecretKey macKey = new SecretKeySpec(macKeyBytes, "HmacSHA256");
|
|
Mac mac = Mac.getInstance("HmacSHA256");
|
|
mac.init(macKey);
|
|
byte[] computedMac = mac.doFinal(ciphertext); // $Alert[java/quantum/examples/bad-mac-order-decrypt-then-mac], False positive for Plaintext reuse
|
|
|
|
if (!MessageDigest.isEqual(receivedMac, computedMac)) {
|
|
throw new SecurityException("MAC verification failed");
|
|
}
|
|
}
|
|
}
|