Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 162 additions & 0 deletions test/jdk/sun/security/provider/acvp/Launcher.java
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);
}
}
}
126 changes: 126 additions & 0 deletions test/jdk/sun/security/provider/acvp/ML_DSA_Test.java
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();
}
}
}
Loading