forked from openjdk/jdk
-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Reviewed-by: mullan, bperez
- Loading branch information
Showing
7 changed files
with
669 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
/* | ||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. | ||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
* | ||
* This code is free software; you can redistribute it and/or modify it | ||
* under the terms of the GNU General Public License version 2 only, as | ||
* published by the Free Software Foundation. | ||
* | ||
* This code is distributed in the hope that it will be useful, but WITHOUT | ||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
* version 2 for more details (a copy is included in the LICENSE file that | ||
* accompanied this code). | ||
* | ||
* You should have received a copy of the GNU General Public License version | ||
* 2 along with this work; if not, write to the Free Software Foundation, | ||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
* | ||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
* or visit www.oracle.com if you need additional information or have any | ||
* questions. | ||
*/ | ||
import jdk.test.lib.json.JSONValue; | ||
import jtreg.SkippedException; | ||
|
||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.security.Provider; | ||
import java.security.Security; | ||
|
||
/* | ||
* @test | ||
* @bug 8342442 | ||
* @library /test/lib | ||
*/ | ||
|
||
/// This test runs on `internalProjection.json`-style files generated | ||
/// by NIST's ACVP Server. See [https://github.com/usnistgov/ACVP-Server]. | ||
/// | ||
/// The files are either put into the `data` directory or another | ||
/// directory specified by the `test.acvp.data` test property. | ||
/// The test walks through the directory recursively and looks for | ||
/// file names equal to or ending with `internalProjection.json` and | ||
/// runs tests on them. | ||
/// | ||
/// Set the `test.acvp.alg` test property to only test the specified algorithm. | ||
/// | ||
/// Sample files can be downloaded from | ||
/// [https://github.com/usnistgov/ACVP-Server/tree/master/gen-val/json-files]. | ||
/// | ||
/// By default, the test uses system-preferred implementations. | ||
/// If you want to test a specific provider, set the | ||
/// `test.acvp.provider` test property. The provider must be | ||
/// registered. | ||
/// | ||
/// Tests for each algorithm must be compliant to its specification linked from | ||
/// [https://github.com/usnistgov/ACVP?tab=readme-ov-file#supported-algorithms]. | ||
/// | ||
/// Example: | ||
/// ``` | ||
/// jtreg -Dtest.acvp.provider=SunJCE \ | ||
/// -Dtest.acvp.alg=ML-KEM \ | ||
/// -Dtest.acvp.data=/path/to/json-files/ \ | ||
/// -jdk:/path/to/jdk Launcher.java | ||
/// ``` | ||
public class Launcher { | ||
|
||
private static final String ONLY_ALG | ||
= System.getProperty("test.acvp.alg"); | ||
|
||
private static final Provider PROVIDER; | ||
|
||
private static int count = 0; | ||
private static int invalidTest = 0; | ||
private static int unsupportedTest = 0; | ||
|
||
static { | ||
var provProp = System.getProperty("test.acvp.provider"); | ||
if (provProp != null) { | ||
var p = Security.getProvider(provProp); | ||
if (p == null) { | ||
System.err.println(provProp + " is not a registered provider name"); | ||
throw new RuntimeException(provProp + " is not a registered provider name"); | ||
} | ||
PROVIDER = p; | ||
} else { | ||
PROVIDER = null; | ||
} | ||
} | ||
|
||
public static void main(String[] args) throws Exception { | ||
|
||
var testDataProp = System.getProperty("test.acvp.data"); | ||
Path dataPath = testDataProp != null | ||
? Path.of(testDataProp) | ||
: Path.of(System.getProperty("test.src"), "data"); | ||
System.out.println("Data path: " + dataPath); | ||
|
||
if (PROVIDER != null) { | ||
System.out.println("Provider: " + PROVIDER.getName()); | ||
} | ||
if (ONLY_ALG != null) { | ||
System.out.println("Algorithm: " + ONLY_ALG); | ||
} | ||
|
||
try (var stream = Files.walk(dataPath)) { | ||
stream.filter(Files::isRegularFile) | ||
.filter(p -> p.getFileName().toString() | ||
.endsWith("internalProjection.json")) | ||
.forEach(Launcher::run); | ||
} | ||
|
||
if (count > 0) { | ||
System.out.println(); | ||
System.out.println("Test completed: " + count); | ||
System.out.println("Invalid tests: " + invalidTest); | ||
System.out.println("Unsupported tests: " + unsupportedTest); | ||
} else { | ||
throw new SkippedException("No supported test found"); | ||
} | ||
} | ||
|
||
static void run(Path test) { | ||
try { | ||
JSONValue kat; | ||
try { | ||
kat = JSONValue.parse(Files.readString(test)); | ||
} catch (Exception e) { | ||
System.out.println("Warning: cannot parse " + test + ". Skipped"); | ||
invalidTest++; | ||
return; | ||
} | ||
var alg = kat.get("algorithm").asString(); | ||
if (ONLY_ALG != null && !alg.equals(ONLY_ALG)) { | ||
return; | ||
} | ||
System.out.println(">>> Testing " + test + "..."); | ||
switch (alg) { | ||
case "ML-DSA" -> { | ||
ML_DSA_Test.run(kat, PROVIDER); | ||
count++; | ||
} | ||
case "ML-KEM" -> { | ||
ML_KEM_Test.run(kat, PROVIDER); | ||
count++; | ||
} | ||
case "SHA2-256", "SHA2-224", "SHA3-256", "SHA3-224" -> { | ||
SHA_Test.run(kat, PROVIDER); | ||
count++; | ||
} | ||
default -> { | ||
System.out.println("Skipped unsupported algorithm: " + alg); | ||
unsupportedTest++; | ||
} | ||
} | ||
} catch (RuntimeException re) { | ||
throw re; | ||
} catch (Exception e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
/* | ||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. | ||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
* | ||
* This code is free software; you can redistribute it and/or modify it | ||
* under the terms of the GNU General Public License version 2 only, as | ||
* published by the Free Software Foundation. | ||
* | ||
* This code is distributed in the hope that it will be useful, but WITHOUT | ||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
* version 2 for more details (a copy is included in the LICENSE file that | ||
* accompanied this code). | ||
* | ||
* You should have received a copy of the GNU General Public License version | ||
* 2 along with this work; if not, write to the Free Software Foundation, | ||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
* | ||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
* or visit www.oracle.com if you need additional information or have any | ||
* questions. | ||
*/ | ||
import jdk.test.lib.Asserts; | ||
import jdk.test.lib.json.JSONValue; | ||
import jdk.test.lib.security.FixedSecureRandom; | ||
|
||
import java.security.*; | ||
import java.security.spec.EncodedKeySpec; | ||
import java.security.spec.NamedParameterSpec; | ||
|
||
import static jdk.test.lib.Utils.toByteArray; | ||
|
||
// JSON spec at https://pages.nist.gov/ACVP/draft-celi-acvp-ml-dsa.html | ||
public class ML_DSA_Test { | ||
|
||
public static void run(JSONValue kat, Provider provider) throws Exception { | ||
var mode = kat.get("mode").asString(); | ||
switch (mode) { | ||
case "keyGen" -> keyGenTest(kat, provider); | ||
case "sigGen" -> sigGenTest(kat, provider); | ||
case "sigVer" -> sigVerTest(kat, provider); | ||
default -> throw new UnsupportedOperationException("Unknown mode: " + mode); | ||
} | ||
} | ||
|
||
static void keyGenTest(JSONValue kat, Provider p) throws Exception { | ||
var g = p == null | ||
? KeyPairGenerator.getInstance("ML-DSA") | ||
: KeyPairGenerator.getInstance("ML-DSA", p); | ||
var f = p == null | ||
? KeyFactory.getInstance("ML-DSA") | ||
: KeyFactory.getInstance("ML-DSA", p); | ||
for (var t : kat.get("testGroups").asArray()) { | ||
var pname = t.get("parameterSet").asString(); | ||
var np = new NamedParameterSpec(pname); | ||
System.out.println(">> " + pname); | ||
for (var c : t.get("tests").asArray()) { | ||
System.out.print(c.get("tcId").asString() + " "); | ||
g.initialize(np, new FixedSecureRandom(toByteArray(c.get("seed").asString()))); | ||
var kp = g.generateKeyPair(); | ||
var pk = f.getKeySpec(kp.getPublic(), EncodedKeySpec.class).getEncoded(); | ||
var sk = f.getKeySpec(kp.getPrivate(), EncodedKeySpec.class).getEncoded(); | ||
Asserts.assertEqualsByteArray(pk, toByteArray(c.get("pk").asString())); | ||
Asserts.assertEqualsByteArray(sk, toByteArray(c.get("sk").asString())); | ||
} | ||
System.out.println(); | ||
} | ||
} | ||
|
||
static void sigGenTest(JSONValue kat, Provider p) throws Exception { | ||
var s = p == null | ||
? Signature.getInstance("ML-DSA") | ||
: Signature.getInstance("ML-DSA", p); | ||
for (var t : kat.get("testGroups").asArray()) { | ||
var pname = t.get("parameterSet").asString(); | ||
var det = Boolean.parseBoolean(t.get("deterministic").asString()); | ||
System.out.println(">> " + pname + " sign"); | ||
for (var c : t.get("tests").asArray()) { | ||
System.out.print(Integer.parseInt(c.get("tcId").asString()) + " "); | ||
var sk = new PrivateKey() { | ||
public String getAlgorithm() { return pname; } | ||
public String getFormat() { return "RAW"; } | ||
public byte[] getEncoded() { return toByteArray(c.get("sk").asString()); } | ||
}; | ||
var sr = new FixedSecureRandom( | ||
det ? new byte[32] : toByteArray(c.get("rnd").asString())); | ||
s.initSign(sk, sr); | ||
s.update(toByteArray(c.get("message").asString())); | ||
var sig = s.sign(); | ||
Asserts.assertEqualsByteArray( | ||
sig, toByteArray(c.get("signature").asString())); | ||
} | ||
System.out.println(); | ||
} | ||
} | ||
|
||
static void sigVerTest(JSONValue kat, Provider p) throws Exception { | ||
var s = p == null | ||
? Signature.getInstance("ML-DSA") | ||
: Signature.getInstance("ML-DSA", p); | ||
for (var t : kat.get("testGroups").asArray()) { | ||
var pname = t.get("parameterSet").asString(); | ||
var pk = new PublicKey() { | ||
public String getAlgorithm() { return pname; } | ||
public String getFormat() { return "RAW"; } | ||
public byte[] getEncoded() { return toByteArray(t.get("pk").asString()); } | ||
}; | ||
System.out.println(">> " + pname + " verify"); | ||
for (var c : t.get("tests").asArray()) { | ||
System.out.print(c.get("tcId").asString() + " "); | ||
// Only ML-DSA sigVer has negative tests | ||
var expected = Boolean.parseBoolean(c.get("testPassed").asString()); | ||
var actual = true; | ||
try { | ||
s.initVerify(pk); | ||
s.update(toByteArray(c.get("message").asString())); | ||
actual = s.verify(toByteArray(c.get("signature").asString())); | ||
} catch (InvalidKeyException | SignatureException e) { | ||
actual = false; | ||
} | ||
Asserts.assertEQ(expected, actual); | ||
} | ||
System.out.println(); | ||
} | ||
} | ||
} |
Oops, something went wrong.