From df4925c8e59e349b346d163f6c2704730845ef79 Mon Sep 17 00:00:00 2001 From: arpansharma Date: Fri, 21 Aug 2026 12:55:18 -0500 Subject: [PATCH] Parameterize the negative encryption tests across all ML-KEM parameter sets Converts the wrong-recipient-key and truncated-encapsulation rejection tests on both the DOM and StAX paths from single hardcoded ML-KEM-768 cases to @ParameterizedTest/@CsvSource across ML-KEM-512/768/1024, matching the style of the existing encrypt-decrypt tests. The encryptToRecipient helpers take the key encapsulation algorithm as a parameter instead of hardcoding ML-KEM-768. --- .../encryption/XMLEncryptionMLKEMTest.java | 42 +++++++++++-------- .../encryption/StaxMLKEMEncryptionTest.java | 42 ++++++++++++------- 2 files changed, 52 insertions(+), 32 deletions(-) diff --git a/src/test/java/org/apache/xml/security/test/dom/encryption/XMLEncryptionMLKEMTest.java b/src/test/java/org/apache/xml/security/test/dom/encryption/XMLEncryptionMLKEMTest.java index 431bd29e4..73dbc2d6c 100644 --- a/src/test/java/org/apache/xml/security/test/dom/encryption/XMLEncryptionMLKEMTest.java +++ b/src/test/java/org/apache/xml/security/test/dom/encryption/XMLEncryptionMLKEMTest.java @@ -45,7 +45,6 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.w3c.dom.Document; @@ -220,15 +219,21 @@ void testMLKEMEncryptDecrypt(String keyEncapsulationUri, String jcaAlgorithm) th assertEquals("CardNumber:4019111111111111", decryptedRoot.getTextContent()); } - @Test - void testMLKEMWrongRecipientPrivateKeyFailsCleanly() throws Exception { + @ParameterizedTest + @CsvSource({ + EncryptionConstants.ALGO_ID_KEYTRANSPORT_MLKEM_512 + ",ML-KEM-512", + EncryptionConstants.ALGO_ID_KEYTRANSPORT_MLKEM_768 + ",ML-KEM-768", + EncryptionConstants.ALGO_ID_KEYTRANSPORT_MLKEM_1024 + ",ML-KEM-1024", + }) + void testMLKEMWrongRecipientPrivateKeyFailsCleanly(String keyEncapsulationUri, String jcaAlgorithm) + throws Exception { Assumptions.assumeTrue(mlKemAvailable, "ML-KEM requires BouncyCastle 1.84+ and Java 21+ (javax.crypto.KEM)"); - PublicKey recipientAPub = keyPairs.get("ML-KEM-768").getPublic(); - byte[] encryptedXml = encryptToRecipient(recipientAPub); + PublicKey recipientAPub = keyPairs.get(jcaAlgorithm).getPublic(); + byte[] encryptedXml = encryptToRecipient(recipientAPub, keyEncapsulationUri); // A second, independent recipient - not the one the message was encrypted to. - KeyPairGenerator kpg = KeyPairGenerator.getInstance("ML-KEM-768", "BC"); + KeyPairGenerator kpg = KeyPairGenerator.getInstance(jcaAlgorithm, "BC"); PrivateKey wrongPrivateKey = kpg.generateKeyPair().getPrivate(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); @@ -254,13 +259,18 @@ void testMLKEMWrongRecipientPrivateKeyFailsCleanly() throws Exception { () -> unwrapCipher.decryptKey(ek, encData.getEncryptionMethod().getAlgorithm())); } - @Test - void testMLKEMTruncatedEncapsulationRejected() throws Exception { + @ParameterizedTest + @CsvSource({ + EncryptionConstants.ALGO_ID_KEYTRANSPORT_MLKEM_512 + ",ML-KEM-512", + EncryptionConstants.ALGO_ID_KEYTRANSPORT_MLKEM_768 + ",ML-KEM-768", + EncryptionConstants.ALGO_ID_KEYTRANSPORT_MLKEM_1024 + ",ML-KEM-1024", + }) + void testMLKEMTruncatedEncapsulationRejected(String keyEncapsulationUri, String jcaAlgorithm) throws Exception { Assumptions.assumeTrue(mlKemAvailable, "ML-KEM requires BouncyCastle 1.84+ and Java 21+ (javax.crypto.KEM)"); - PrivateKey privKey = keyPairs.get("ML-KEM-768").getPrivate(); - PublicKey pubKey = keyPairs.get("ML-KEM-768").getPublic(); - byte[] encryptedXml = encryptToRecipient(pubKey); + PrivateKey privKey = keyPairs.get(jcaAlgorithm).getPrivate(); + PublicKey pubKey = keyPairs.get(jcaAlgorithm).getPublic(); + byte[] encryptedXml = encryptToRecipient(pubKey, keyEncapsulationUri); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); @@ -296,13 +306,11 @@ void testMLKEMTruncatedEncapsulationRejected() throws Exception { } /** - * Runs the encrypt half of the ML-KEM-768 round trip (same structure as - * {@link #testMLKEMEncryptDecrypt}) and returns the serialised encrypted XML, for tests that - * want to corrupt or otherwise interfere with the decrypt half. + * Runs the encrypt half of the round trip for the given key encapsulation algorithm (same + * structure as {@link #testMLKEMEncryptDecrypt}) and returns the serialised encrypted XML, + * for tests that want to corrupt or otherwise interfere with the decrypt half. */ - private byte[] encryptToRecipient(PublicKey pubKey) throws Exception { - String keyEncapsulationUri = EncryptionConstants.ALGO_ID_KEYTRANSPORT_MLKEM_768; - + private byte[] encryptToRecipient(PublicKey pubKey, String keyEncapsulationUri) throws Exception { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); Document doc = dbf.newDocumentBuilder().newDocument(); diff --git a/src/test/java/org/apache/xml/security/test/stax/encryption/StaxMLKEMEncryptionTest.java b/src/test/java/org/apache/xml/security/test/stax/encryption/StaxMLKEMEncryptionTest.java index 7a4d3c37a..f32d2526b 100644 --- a/src/test/java/org/apache/xml/security/test/stax/encryption/StaxMLKEMEncryptionTest.java +++ b/src/test/java/org/apache/xml/security/test/stax/encryption/StaxMLKEMEncryptionTest.java @@ -61,7 +61,6 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.w3c.dom.Document; @@ -278,15 +277,21 @@ private Document decryptUsingDOM(Document document, Key privateKey) throws Excep return cipher.doFinal(document, ee); } - @Test - void testMLKEMStaxWrongRecipientPrivateKeyFailsCleanly() throws Exception { + @ParameterizedTest + @CsvSource({ + EncryptionConstants.ALGO_ID_KEYTRANSPORT_MLKEM_512 + ",ML-KEM-512", + EncryptionConstants.ALGO_ID_KEYTRANSPORT_MLKEM_768 + ",ML-KEM-768", + EncryptionConstants.ALGO_ID_KEYTRANSPORT_MLKEM_1024 + ",ML-KEM-1024" + }) + void testMLKEMStaxWrongRecipientPrivateKeyFailsCleanly(String keyEncapsulationUri, String jcaAlgorithm) + throws Exception { Assumptions.assumeTrue(mlKemAvailable, "ML-KEM requires BouncyCastle 1.84+ and Java 21+ (javax.crypto.KEM)"); - KeyPair kp = keyPairs.get("ML-KEM-768"); - Document document = encryptToRecipient(kp.getPublic()); + KeyPair kp = keyPairs.get(jcaAlgorithm); + Document document = encryptToRecipient(kp.getPublic(), keyEncapsulationUri); // A second, independent recipient - not the one the message was encrypted to. - KeyPairGenerator kpg = KeyPairGenerator.getInstance("ML-KEM-768", "BC"); + KeyPairGenerator kpg = KeyPairGenerator.getInstance(jcaAlgorithm, "BC"); PrivateKey wrongPrivateKey = kpg.generateKeyPair().getPrivate(); // See the equivalent DOM-path test (XMLEncryptionMLKEMTest) for why this must throw @@ -294,12 +299,18 @@ void testMLKEMStaxWrongRecipientPrivateKeyFailsCleanly() throws Exception { assertThrows(XMLEncryptionException.class, () -> decryptUsingDOM(document, wrongPrivateKey)); } - @Test - void testMLKEMStaxTruncatedEncapsulationRejected() throws Exception { + @ParameterizedTest + @CsvSource({ + EncryptionConstants.ALGO_ID_KEYTRANSPORT_MLKEM_512 + ",ML-KEM-512", + EncryptionConstants.ALGO_ID_KEYTRANSPORT_MLKEM_768 + ",ML-KEM-768", + EncryptionConstants.ALGO_ID_KEYTRANSPORT_MLKEM_1024 + ",ML-KEM-1024" + }) + void testMLKEMStaxTruncatedEncapsulationRejected(String keyEncapsulationUri, String jcaAlgorithm) + throws Exception { Assumptions.assumeTrue(mlKemAvailable, "ML-KEM requires BouncyCastle 1.84+ and Java 21+ (javax.crypto.KEM)"); - KeyPair kp = keyPairs.get("ML-KEM-768"); - Document document = encryptToRecipient(kp.getPublic()); + KeyPair kp = keyPairs.get(jcaAlgorithm); + Document document = encryptToRecipient(kp.getPublic(), keyEncapsulationUri); // Truncate the EncryptedKey's CipherValue to well under half its length - shorter than // any ML-KEM variant's encapsulationSize() - before it is parsed into an EncryptedKey @@ -324,11 +335,12 @@ void testMLKEMStaxTruncatedEncapsulationRejected() throws Exception { } /** - * Runs the encrypt half of the ML-KEM-768 round trip (same properties as - * {@link #testMLKEMEncryptDecrypt}) and returns the parsed resulting document, for tests - * that want to corrupt or otherwise interfere with the decrypt half. + * Runs the encrypt half of the round trip for the given key encapsulation algorithm (same + * properties as {@link #testMLKEMEncryptDecrypt}) and returns the parsed resulting document, + * for tests that want to corrupt or otherwise interfere with the decrypt half. */ - private Document encryptToRecipient(java.security.PublicKey pubKey) throws Exception { + private Document encryptToRecipient(java.security.PublicKey pubKey, String keyEncapsulationUri) + throws Exception { XMLSecurityProperties properties = new XMLSecurityProperties(); List actions = new ArrayList<>(); actions.add(XMLSecurityConstants.ENCRYPTION); @@ -341,7 +353,7 @@ private Document encryptToRecipient(java.security.PublicKey pubKey) throws Excep properties.setEncryptionSymAlgorithm("http://www.w3.org/2009/xmlenc11#aes256-gcm"); properties.setEncryptionKeyTransportAlgorithm(EncryptionConstants.ALGO_ID_KEYTRANSPORT_GENERIC_HYBRID); - properties.setEncryptionKeyEncapsulationAlgorithm(EncryptionConstants.ALGO_ID_KEYTRANSPORT_MLKEM_768); + properties.setEncryptionKeyEncapsulationAlgorithm(keyEncapsulationUri); properties.setEncryptionDataEncapsulationAlgorithm(EncryptionConstants.ALGO_ID_KEYWRAP_AES256); properties.setEncryptionTransportKey(pubKey);