Files
codeql/java/ql/test/experimental/library-tests/quantum/jca/MACOperation.java
2025-10-06 11:53:39 -04:00

250 lines
9.7 KiB
Java

package com.example.crypto.algorithms;
// import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.*;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
/**
* MACOperation demonstrates various Message Authentication Code (MAC)
* operations and further use of MAC outputs as inputs into higher-level
* cryptosystems.
*
* Flows include:
*
* 1. Secure HMAC-SHA2 (HMAC-SHA256) - a widely accepted MAC. 2. Secure
* HMAC-SHA3 (HMAC-SHA3-256) - an alternative using the SHA-3 family. 3. Secure
* Poly1305 MAC - using BouncyCastle's implementation. 4. Secure GMAC - using
* AES-GCM's authentication tag in a dedicated MAC mode. 5. Secure KMAC - using
* KMAC128 (from the SHA-3 family).
*
* Insecure examples include:
*
* 6. Insecure HMAC-SHA1 - which is deprecated.
*
* Further flows:
*
* A. processMACOutput: Uses the MAC output directly as key material for AES
* encryption. (Note: This is acceptable only if the MAC is produced by a secure
* function.)
*
* B. alternativeMACFlow: Uses the MAC output as an identifier that is then
* encrypted.
*
* C. furtherUseMACForKeyDerivation: Uses PBKDF2 to split a MAC output into two
* keys, one for encryption and one for MACing ciphertext.
*
* SAST/CBOM Notes: - Secure MAC algorithms (HMAC-SHA256, HMAC-SHA3-256,
* Poly1305, GMAC, KMAC128) are acceptable if used correctly. - HMAC-SHA1 is
* flagged as insecure. - Using a raw MAC output directly as key material is
* ambiguous unless the MAC is produced by a secure KDF.
*/
public class MACOperation {
// static {
// Security.addProvider(new BouncyCastleProvider());
// }
// ---------- MAC Operations ----------
/**
* Secure MAC using HMAC-SHA256. SAST: HMAC-SHA256 is widely considered
* secure.
*/
public byte[] secureHMACSHA256(String message, byte[] key) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256", "BC");
SecretKey secretKey = new SecretKeySpec(key, "HmacSHA256");
mac.init(secretKey);
return mac.doFinal(message.getBytes());
}
/**
* Secure MAC using HMAC-SHA3-256. SAST: HMAC-SHA3 is a modern alternative
* from the SHA-3 family.
*/
public byte[] secureHMACSHA3(String message, byte[] key) throws Exception {
Mac mac = Mac.getInstance("HmacSHA3-256", "BC");
SecretKey secretKey = new SecretKeySpec(key, "HmacSHA3-256");
mac.init(secretKey);
return mac.doFinal(message.getBytes());
}
/**
* Secure MAC using Poly1305. SAST: Poly1305 is secure when used with a
* one-time key from a cipher (e.g. ChaCha20).
*/
public byte[] securePoly1305(String message, byte[] key) throws Exception {
Mac mac = Mac.getInstance("Poly1305", "BC");
SecretKey secretKey = new SecretKeySpec(key, "Poly1305");
mac.init(secretKey);
return mac.doFinal(message.getBytes());
}
/**
* Secure MAC using GMAC. SAST: GMAC (the MAC part of AES-GCM) is secure
* when used correctly.
*/
public byte[] secureGMAC(String message, byte[] key) throws Exception {
// For GMAC, we use the GMac algorithm as provided by BC.
Mac mac = Mac.getInstance("GMac", "BC");
// Initialize the key for GMAC; key should be appropriate for the underlying
// block cipher.
SecretKey secretKey = new SecretKeySpec(key, "AES");
mac.init(secretKey);
return mac.doFinal(message.getBytes());
}
/**
* Secure MAC using KMAC128. SAST: KMAC128 is part of the SHA-3 family and
* is secure when used properly.
*/
public byte[] secureKMAC(String message, byte[] key) throws Exception {
Mac mac = Mac.getInstance("KMAC128", "BC");
SecretKey secretKey = new SecretKeySpec(key, "KMAC128");
mac.init(secretKey);
return mac.doFinal(message.getBytes());
}
/**
* Insecure MAC using HMAC-SHA1. SAST: HMAC-SHA1 is considered deprecated
* and weak.
*/
public byte[] insecureHMACSHA1(String message, byte[] key) throws Exception {
Mac mac = Mac.getInstance("HmacSHA1", "BC");
SecretKey secretKey = new SecretKeySpec(key, "HmacSHA1");
mac.init(secretKey);
return mac.doFinal(message.getBytes());
}
// ---------- Further Use of MAC Outputs ----------
/**
* Processes the MAC output by using it as key material for AES encryption.
* SAST: Using a raw MAC output as key material is acceptable only if the
* MAC was produced by a secure function; otherwise, this is ambiguous.
*
* @param macOutput The computed MAC output.
* @throws Exception if encryption fails.
*/
public void processMACOutput(byte[] macOutput) throws Exception {
// Derive a 128-bit AES key from the MAC output.
SecretKey key = new SecretKeySpec(macOutput, 0, 16, "AES");
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key, new SecureRandom());
byte[] encryptedData = cipher.doFinal("Sensitive Data".getBytes());
storeEncryptedMAC(encryptedData);
}
/**
* Alternative flow: Uses the MAC output as an identifier and then encrypts
* it. SAST: Using a MAC as an identifier is common; subsequent encryption
* must be secure.
*
* @param macOutput The computed MAC output.
* @throws Exception if encryption fails.
*/
public void alternativeMACFlow(byte[] macOutput) throws Exception {
byte[] identifier = Base64.getEncoder().encode(macOutput);
encryptAndSend(identifier);
}
/**
* Further use: Derives two separate keys from the MAC output using PBKDF2,
* then uses one key for encryption and one for computing an additional MAC
* over the ciphertext.
*
* SAST: This key-splitting technique is acceptable if PBKDF2 is used
* securely.
*
* @param macOutput The MAC output to derive keys from.
* @throws Exception if key derivation or encryption fails.
*/
public void furtherUseMACForKeyDerivation(byte[] macOutput) throws Exception {
// Use the Base64 representation of the MAC as the password input to PBKDF2.
String macAsPassword = Base64.getEncoder().encodeToString(macOutput);
byte[] salt = generateSalt(16);
PBEKeySpec spec = new PBEKeySpec(macAsPassword.toCharArray(), salt, 10000, 256);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] keyMaterial = factory.generateSecret(spec).getEncoded();
// Split into two 128-bit keys.
byte[] encryptionKeyBytes = Arrays.copyOfRange(keyMaterial, 0, 16);
byte[] macKeyBytes = Arrays.copyOfRange(keyMaterial, 16, 32);
SecretKey encryptionKey = new SecretKeySpec(encryptionKeyBytes, "AES");
SecretKey derivedMacKey = new SecretKeySpec(macKeyBytes, "HmacSHA256");
// Encrypt some sample data using the derived encryption key.
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, new SecureRandom());
byte[] ciphertext = cipher.doFinal("Further Use Test Data".getBytes());
// Compute HMAC over the ciphertext using the derived MAC key.
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(derivedMacKey);
byte[] computedMac = mac.doFinal(ciphertext);
// Concatenate the ciphertext and MAC for further use.
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);
storeEncryptedMAC(output);
}
// ---------- Output/Storage Methods ----------
/**
* Simulates secure storage or transmission of an encrypted MAC output.
* SAST: In production, storage and transmission must be protected.
*
* @param encryptedMAC The encrypted MAC output.
*/
public void storeEncryptedMAC(byte[] encryptedMAC) {
String stored = Base64.getEncoder().encodeToString(encryptedMAC);
// In production, this string would be securely stored or transmitted.
}
/**
* Encrypts data using AES-GCM and simulates secure transmission. SAST: Uses
* a securely generated AES key.
*
* @param data The data to encrypt.
* @throws Exception if encryption fails.
*/
public void encryptAndSend(byte[] data) throws Exception {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKey key = generateAESKey();
cipher.init(Cipher.ENCRYPT_MODE, key, new SecureRandom());
byte[] encryptedData = cipher.doFinal(data);
storeEncryptedMAC(encryptedData);
}
// ---------- Helper Methods ----------
/**
* Generates a secure 256-bit AES key. SAST: Uses a strong RNG for key
* generation.
*
* @return A SecretKey for AES.
* @throws NoSuchAlgorithmException if AES is unsupported.
*/
private SecretKey generateAESKey() throws NoSuchAlgorithmException {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
return keyGen.generateKey();
}
/**
* Generates a random salt of the specified length using SecureRandom. SAST:
* Salting is essential for secure key derivation.
*
* @param length The salt length.
* @return A byte array representing the salt.
*/
private byte[] generateSalt(int length) {
byte[] salt = new byte[length];
new SecureRandom().nextBytes(salt);
return salt;
}
}