Skip to content

Commit

Permalink
8349017: Update ML tests to verify against ACVP 1.1.0.38 version
Browse files Browse the repository at this point in the history
Reviewed-by: weijun
  • Loading branch information
rhalade committed Jan 31, 2025
1 parent 137ad5d commit 13d852a
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 3,175 deletions.
123 changes: 82 additions & 41 deletions test/jdk/sun/security/provider/acvp/Launcher.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, 2025, 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
Expand All @@ -20,13 +20,19 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

import jdk.test.lib.artifacts.Artifact;
import jdk.test.lib.artifacts.ArtifactResolver;
import jdk.test.lib.artifacts.ArtifactResolverException;
import jdk.test.lib.json.JSONValue;
import jtreg.SkippedException;

import java.nio.file.Files;
import java.io.InputStream;
import java.nio.file.Path;
import java.security.Provider;
import java.security.Security;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/*
* @test
Expand All @@ -35,19 +41,17 @@
* @modules java.base/sun.security.provider
*/

/// 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.
/// This test runs on `internalProjection.json`-style files generated by NIST's
/// ACVP Server ([GitHub repository](https://github.com/usnistgov/ACVP-Server)).
/// These files are included in ZIP archives available under the
/// [tags section](https://github.com/usnistgov/ACVP-Server/tags)
/// of the repository.
///
/// Set the `test.acvp.alg` test property to only test the specified algorithm.
/// The zip archive is either hosted on artifactory server or
/// specified with local path to the test.
/// The test looks for test data files in the archive listed with `TEST_FILES`.
///
/// Sample files can be downloaded from
/// [https://github.com/usnistgov/ACVP-Server/tree/master/gen-val/json-files].
/// These tests are currently compatible with ACVP version 1.1.0.38.
///
/// By default, the test uses system-preferred implementations.
/// If you want to test a specific provider, set the
Expand All @@ -58,19 +62,30 @@
/// [https://github.com/usnistgov/ACVP?tab=readme-ov-file#supported-algorithms].
///
/// Example:
///
/// Run locally with ArtifactResolver
/// ```
/// jtreg -Dtest.acvp.provider=SunJCE \
/// -Dtest.acvp.alg=ML-KEM \
/// -Dtest.acvp.data=/path/to/json-files/ \
/// -jdk:/path/to/jdk Launcher.java
/// jtreg -Djdk.test.lib.artifacts.ACVP-Server=<path-to-archive-file>
/// ```
public class Launcher {
/// OR host the zip archive on artifactory server.
///

private static final String ONLY_ALG
= System.getProperty("test.acvp.alg");
public class Launcher {

private static final Provider PROVIDER;

private static final String ACVP_BUNDLE_LOC = "jpg.tests.jdk";
private static final String ACVP_BUNDLE_NAME = "ACVP-Server";
private static final String ACVP_BUNDLE_VERSION = "1.1.0.38";
// Zip archive entry name, do not update to use File.separator
private static final String[] TEST_FILES = {
"gen-val/json-files/ML-DSA-keyGen-FIPS204/internalProjection.json",
"gen-val/json-files/ML-DSA-sigGen-FIPS204/internalProjection.json",
"gen-val/json-files/ML-DSA-sigVer-FIPS204/internalProjection.json",
"gen-val/json-files/ML-KEM-encapDecap-FIPS203/internalProjection.json",
"gen-val/json-files/ML-KEM-keyGen-FIPS203/internalProjection.json"
};

private static int count = 0;
private static int invalidTest = 0;
private static int unsupportedTest = 0;
Expand All @@ -91,24 +106,26 @@ public class Launcher {

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);
Path archivePath = fetchACVPServerTests(ACVP_SERVER_TESTS.class);
System.out.println("Data path: " + archivePath);

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);
// Read test data files from zip archive
try (ZipFile zf = new ZipFile(archivePath.toFile())) {
for (String testFile : TEST_FILES) {
// Zip archive entry name, do not update to use File.separator
String fullEntryName = ACVP_BUNDLE_NAME + "-" + ACVP_BUNDLE_VERSION + "/" + testFile;
System.out.println("Find and test with: " + fullEntryName);
ZipEntry ze = zf.getEntry(fullEntryName);
if (ze != null) {
run(zf.getInputStream(ze));
} else {
throw new RuntimeException("Entry not found: " + fullEntryName);
}
}
}

if (count > 0) {
Expand All @@ -117,25 +134,25 @@ public static void main(String[] args) throws Exception {
System.out.println("Invalid tests: " + invalidTest);
System.out.println("Unsupported tests: " + unsupportedTest);
} else {
throw new SkippedException("No supported test found");
throw new RuntimeException("No supported test found");
}

if (invalidTest != 0 || unsupportedTest != 0){
throw new RuntimeException("Invalid or Unsupported tests found");
}
}

static void run(Path test) {
static void run(InputStream test) {
try {
JSONValue kat;
try {
kat = JSONValue.parse(Files.readString(test));
try (test) {
kat = JSONValue.parse(new String(test.readAllBytes()));
} 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);
Expand All @@ -160,4 +177,28 @@ static void run(Path test) {
throw new RuntimeException(e);
}
}

private static Path fetchACVPServerTests(Class<?> clazz) {
try {
return ArtifactResolver.resolve(clazz).entrySet().stream()
.findAny().get().getValue();
} catch (ArtifactResolverException e) {
Throwable cause = e.getCause();
if (cause == null) {
throw new SkippedException("Cannot resolve artifact, "
+ "please check if JIB jar is present in classpath.", e);
}

throw new SkippedException("Fetch artifact failed: " + clazz, e);
}
}

@Artifact(
organization = ACVP_BUNDLE_LOC,
name = ACVP_BUNDLE_NAME,
revision = ACVP_BUNDLE_VERSION,
extension = "zip",
unpack = false)
private static class ACVP_SERVER_TESTS {
}
}
54 changes: 40 additions & 14 deletions test/jdk/sun/security/provider/acvp/ML_DSA_Test.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, 2025, 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
Expand Down Expand Up @@ -36,10 +36,6 @@ public class ML_DSA_Test {

public static void run(JSONValue kat, Provider provider) throws Exception {

// We only have ML-DSA test for internal functions, which
// is equivalent to the FIP 204 draft.
ML_DSA_Impls.version = ML_DSA_Impls.Version.DRAFT;

var mode = kat.get("mode").asString();
switch (mode) {
case "keyGen" -> keyGenTest(kat, provider);
Expand All @@ -50,7 +46,7 @@ public static void run(JSONValue kat, Provider provider) throws Exception {
}

static NamedParameterSpec genParams(String pname) {
return switch (pname) {
return switch (pname) {
case "ML-DSA-44" -> NamedParameterSpec.ML_DSA_44;
case "ML-DSA-65" -> NamedParameterSpec.ML_DSA_65;
case "ML-DSA-87" -> NamedParameterSpec.ML_DSA_87;
Expand Down Expand Up @@ -89,9 +85,23 @@ static void sigGenTest(JSONValue kat, Provider p) throws Exception {
: 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");
var det = Boolean.parseBoolean(t.get("deterministic").asString());
if (t.get("signatureInterface").asString().equals("internal")) {
ML_DSA_Impls.version = ML_DSA_Impls.Version.DRAFT;
} else {
ML_DSA_Impls.version = ML_DSA_Impls.Version.FINAL;
}
if (t.get("externalMu").asString().equals("true")) {
continue; // Not supported
}
for (var c : t.get("tests").asArray()) {
var cstr = c.get("context");
var ctxt = cstr == null ? new byte[0] : toByteArray(cstr.asString());
var hashAlg = c.get("hashAlg").asString();
if (!hashAlg.equals("none") || ctxt.length != 0) {
continue; // Not supported
}
System.out.print(Integer.parseInt(c.get("tcId").asString()) + " ");
var sk = new PrivateKey() {
public String getAlgorithm() { return pname; }
Expand All @@ -103,8 +113,7 @@ static void sigGenTest(JSONValue kat, Provider p) throws Exception {
s.initSign(sk, sr);
s.update(toByteArray(c.get("message").asString()));
var sig = s.sign();
Asserts.assertEqualsByteArray(
toByteArray(c.get("signature").asString()), sig);
Asserts.assertEqualsByteArray(toByteArray(c.get("signature").asString()), sig);
}
System.out.println();
}
Expand All @@ -116,14 +125,31 @@ static void sigVerTest(JSONValue kat, Provider p) throws Exception {
: 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");

if (t.get("signatureInterface").asString().equals("internal")) {
ML_DSA_Impls.version = ML_DSA_Impls.Version.DRAFT;
} else {
ML_DSA_Impls.version = ML_DSA_Impls.Version.FINAL;
}

if (t.get("externalMu").asString().equals("true")) {
continue; // Not supported
}

for (var c : t.get("tests").asArray()) {
var cstr = c.get("context");
var ctxt = cstr == null ? new byte[0] : toByteArray(cstr.asString());
var hashAlg = c.get("hashAlg").asString();
if (!hashAlg.equals("none") || ctxt.length != 0) {
continue; // Not supported
}
System.out.print(c.get("tcId").asString() + " ");
var pk = new PublicKey() {
public String getAlgorithm() { return pname; }
public String getFormat() { return "RAW"; }
public byte[] getEncoded() { return toByteArray(c.get("pk").asString()); }
};
// Only ML-DSA sigVer has negative tests
var expected = Boolean.parseBoolean(c.get("testPassed").asString());
var actual = true;
Expand Down
Loading

0 comments on commit 13d852a

Please sign in to comment.