Skip to content

Commit f400896

Browse files
committed
8342442: Static ACVP sample tests
Reviewed-by: mullan, bperez
1 parent 325a2c3 commit f400896

File tree

7 files changed

+669
-0
lines changed

7 files changed

+669
-0
lines changed
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
import jdk.test.lib.json.JSONValue;
24+
import jtreg.SkippedException;
25+
26+
import java.nio.file.Files;
27+
import java.nio.file.Path;
28+
import java.security.Provider;
29+
import java.security.Security;
30+
31+
/*
32+
* @test
33+
* @bug 8342442
34+
* @library /test/lib
35+
*/
36+
37+
/// This test runs on `internalProjection.json`-style files generated
38+
/// by NIST's ACVP Server. See [https://github.com/usnistgov/ACVP-Server].
39+
///
40+
/// The files are either put into the `data` directory or another
41+
/// directory specified by the `test.acvp.data` test property.
42+
/// The test walks through the directory recursively and looks for
43+
/// file names equal to or ending with `internalProjection.json` and
44+
/// runs tests on them.
45+
///
46+
/// Set the `test.acvp.alg` test property to only test the specified algorithm.
47+
///
48+
/// Sample files can be downloaded from
49+
/// [https://github.com/usnistgov/ACVP-Server/tree/master/gen-val/json-files].
50+
///
51+
/// By default, the test uses system-preferred implementations.
52+
/// If you want to test a specific provider, set the
53+
/// `test.acvp.provider` test property. The provider must be
54+
/// registered.
55+
///
56+
/// Tests for each algorithm must be compliant to its specification linked from
57+
/// [https://github.com/usnistgov/ACVP?tab=readme-ov-file#supported-algorithms].
58+
///
59+
/// Example:
60+
/// ```
61+
/// jtreg -Dtest.acvp.provider=SunJCE \
62+
/// -Dtest.acvp.alg=ML-KEM \
63+
/// -Dtest.acvp.data=/path/to/json-files/ \
64+
/// -jdk:/path/to/jdk Launcher.java
65+
/// ```
66+
public class Launcher {
67+
68+
private static final String ONLY_ALG
69+
= System.getProperty("test.acvp.alg");
70+
71+
private static final Provider PROVIDER;
72+
73+
private static int count = 0;
74+
private static int invalidTest = 0;
75+
private static int unsupportedTest = 0;
76+
77+
static {
78+
var provProp = System.getProperty("test.acvp.provider");
79+
if (provProp != null) {
80+
var p = Security.getProvider(provProp);
81+
if (p == null) {
82+
System.err.println(provProp + " is not a registered provider name");
83+
throw new RuntimeException(provProp + " is not a registered provider name");
84+
}
85+
PROVIDER = p;
86+
} else {
87+
PROVIDER = null;
88+
}
89+
}
90+
91+
public static void main(String[] args) throws Exception {
92+
93+
var testDataProp = System.getProperty("test.acvp.data");
94+
Path dataPath = testDataProp != null
95+
? Path.of(testDataProp)
96+
: Path.of(System.getProperty("test.src"), "data");
97+
System.out.println("Data path: " + dataPath);
98+
99+
if (PROVIDER != null) {
100+
System.out.println("Provider: " + PROVIDER.getName());
101+
}
102+
if (ONLY_ALG != null) {
103+
System.out.println("Algorithm: " + ONLY_ALG);
104+
}
105+
106+
try (var stream = Files.walk(dataPath)) {
107+
stream.filter(Files::isRegularFile)
108+
.filter(p -> p.getFileName().toString()
109+
.endsWith("internalProjection.json"))
110+
.forEach(Launcher::run);
111+
}
112+
113+
if (count > 0) {
114+
System.out.println();
115+
System.out.println("Test completed: " + count);
116+
System.out.println("Invalid tests: " + invalidTest);
117+
System.out.println("Unsupported tests: " + unsupportedTest);
118+
} else {
119+
throw new SkippedException("No supported test found");
120+
}
121+
}
122+
123+
static void run(Path test) {
124+
try {
125+
JSONValue kat;
126+
try {
127+
kat = JSONValue.parse(Files.readString(test));
128+
} catch (Exception e) {
129+
System.out.println("Warning: cannot parse " + test + ". Skipped");
130+
invalidTest++;
131+
return;
132+
}
133+
var alg = kat.get("algorithm").asString();
134+
if (ONLY_ALG != null && !alg.equals(ONLY_ALG)) {
135+
return;
136+
}
137+
System.out.println(">>> Testing " + test + "...");
138+
switch (alg) {
139+
case "ML-DSA" -> {
140+
ML_DSA_Test.run(kat, PROVIDER);
141+
count++;
142+
}
143+
case "ML-KEM" -> {
144+
ML_KEM_Test.run(kat, PROVIDER);
145+
count++;
146+
}
147+
case "SHA2-256", "SHA2-224", "SHA3-256", "SHA3-224" -> {
148+
SHA_Test.run(kat, PROVIDER);
149+
count++;
150+
}
151+
default -> {
152+
System.out.println("Skipped unsupported algorithm: " + alg);
153+
unsupportedTest++;
154+
}
155+
}
156+
} catch (RuntimeException re) {
157+
throw re;
158+
} catch (Exception e) {
159+
throw new RuntimeException(e);
160+
}
161+
}
162+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
import jdk.test.lib.Asserts;
24+
import jdk.test.lib.json.JSONValue;
25+
import jdk.test.lib.security.FixedSecureRandom;
26+
27+
import java.security.*;
28+
import java.security.spec.EncodedKeySpec;
29+
import java.security.spec.NamedParameterSpec;
30+
31+
import static jdk.test.lib.Utils.toByteArray;
32+
33+
// JSON spec at https://pages.nist.gov/ACVP/draft-celi-acvp-ml-dsa.html
34+
public class ML_DSA_Test {
35+
36+
public static void run(JSONValue kat, Provider provider) throws Exception {
37+
var mode = kat.get("mode").asString();
38+
switch (mode) {
39+
case "keyGen" -> keyGenTest(kat, provider);
40+
case "sigGen" -> sigGenTest(kat, provider);
41+
case "sigVer" -> sigVerTest(kat, provider);
42+
default -> throw new UnsupportedOperationException("Unknown mode: " + mode);
43+
}
44+
}
45+
46+
static void keyGenTest(JSONValue kat, Provider p) throws Exception {
47+
var g = p == null
48+
? KeyPairGenerator.getInstance("ML-DSA")
49+
: KeyPairGenerator.getInstance("ML-DSA", p);
50+
var f = p == null
51+
? KeyFactory.getInstance("ML-DSA")
52+
: KeyFactory.getInstance("ML-DSA", p);
53+
for (var t : kat.get("testGroups").asArray()) {
54+
var pname = t.get("parameterSet").asString();
55+
var np = new NamedParameterSpec(pname);
56+
System.out.println(">> " + pname);
57+
for (var c : t.get("tests").asArray()) {
58+
System.out.print(c.get("tcId").asString() + " ");
59+
g.initialize(np, new FixedSecureRandom(toByteArray(c.get("seed").asString())));
60+
var kp = g.generateKeyPair();
61+
var pk = f.getKeySpec(kp.getPublic(), EncodedKeySpec.class).getEncoded();
62+
var sk = f.getKeySpec(kp.getPrivate(), EncodedKeySpec.class).getEncoded();
63+
Asserts.assertEqualsByteArray(pk, toByteArray(c.get("pk").asString()));
64+
Asserts.assertEqualsByteArray(sk, toByteArray(c.get("sk").asString()));
65+
}
66+
System.out.println();
67+
}
68+
}
69+
70+
static void sigGenTest(JSONValue kat, Provider p) throws Exception {
71+
var s = p == null
72+
? Signature.getInstance("ML-DSA")
73+
: Signature.getInstance("ML-DSA", p);
74+
for (var t : kat.get("testGroups").asArray()) {
75+
var pname = t.get("parameterSet").asString();
76+
var det = Boolean.parseBoolean(t.get("deterministic").asString());
77+
System.out.println(">> " + pname + " sign");
78+
for (var c : t.get("tests").asArray()) {
79+
System.out.print(Integer.parseInt(c.get("tcId").asString()) + " ");
80+
var sk = new PrivateKey() {
81+
public String getAlgorithm() { return pname; }
82+
public String getFormat() { return "RAW"; }
83+
public byte[] getEncoded() { return toByteArray(c.get("sk").asString()); }
84+
};
85+
var sr = new FixedSecureRandom(
86+
det ? new byte[32] : toByteArray(c.get("rnd").asString()));
87+
s.initSign(sk, sr);
88+
s.update(toByteArray(c.get("message").asString()));
89+
var sig = s.sign();
90+
Asserts.assertEqualsByteArray(
91+
sig, toByteArray(c.get("signature").asString()));
92+
}
93+
System.out.println();
94+
}
95+
}
96+
97+
static void sigVerTest(JSONValue kat, Provider p) throws Exception {
98+
var s = p == null
99+
? Signature.getInstance("ML-DSA")
100+
: Signature.getInstance("ML-DSA", p);
101+
for (var t : kat.get("testGroups").asArray()) {
102+
var pname = t.get("parameterSet").asString();
103+
var pk = new PublicKey() {
104+
public String getAlgorithm() { return pname; }
105+
public String getFormat() { return "RAW"; }
106+
public byte[] getEncoded() { return toByteArray(t.get("pk").asString()); }
107+
};
108+
System.out.println(">> " + pname + " verify");
109+
for (var c : t.get("tests").asArray()) {
110+
System.out.print(c.get("tcId").asString() + " ");
111+
// Only ML-DSA sigVer has negative tests
112+
var expected = Boolean.parseBoolean(c.get("testPassed").asString());
113+
var actual = true;
114+
try {
115+
s.initVerify(pk);
116+
s.update(toByteArray(c.get("message").asString()));
117+
actual = s.verify(toByteArray(c.get("signature").asString()));
118+
} catch (InvalidKeyException | SignatureException e) {
119+
actual = false;
120+
}
121+
Asserts.assertEQ(expected, actual);
122+
}
123+
System.out.println();
124+
}
125+
}
126+
}

0 commit comments

Comments
 (0)