Skip to content

Commit

Permalink
8342442: Static ACVP sample tests
Browse files Browse the repository at this point in the history
Reviewed-by: mullan, bperez
  • Loading branch information
wangweij committed Nov 9, 2024
1 parent 325a2c3 commit f400896
Show file tree
Hide file tree
Showing 7 changed files with 669 additions and 0 deletions.
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

0 comments on commit f400896

Please sign in to comment.