Skip to content

Commit 606aa20

Browse files
committed
ACVP: Download testvectors transparently on-the-fly
This commit changes the acvp_client.py to automatically download the required testvectors file for a given version of the ACVP Server. The main benefit of this change is that changes to the testvectors (which have been happening much more frequent than I expected) do not pollute our git history to the same extend anymore. The local copies of the ACVP files are removed. Note that before we were testing v1.0.0.40, v1.0.0.39, and v1.0.0.38. This commit changes that to only use v1.0.0.40 by default. Additional versions can be tested by calling the acvp_client.py directly with the --version flag. This will be added to CI in the next commit. Signed-off-by: Matthias J. Kannwischer <matthias@kannwischer.eu>
1 parent a4ca960 commit 606aa20

17 files changed

+81
-6337
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@
55
.idea
66
test/build
77
__pycache__/
8+
# Downloaded ACVP test data
9+
test/.acvp-data/

BIBLIOGRAPHY.md

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,6 @@
77
This file lists the citations made throughout the mlkem-native
88
source code and documentation.
99

10-
### `ACVP_Server`
11-
12-
* ACVP Test Vectors
13-
* Author(s):
14-
- National Institute of Standards and Technology
15-
* URL: https://github.com/usnistgov/ACVP-Server/tree/master/gen-val/json-files
16-
* Referenced from:
17-
- [test/acvp_data/README.md](test/acvp_data/README.md)
18-
19-
### `ACVP_Spec`
20-
21-
* ACVP ML-KEM JSON Specification
22-
* Author(s):
23-
- Christopher Celi
24-
* URL: https://pages.nist.gov/ACVP/draft-celi-acvp-ml-kem.html
25-
* Referenced from:
26-
- [test/acvp_data/README.md](test/acvp_data/README.md)
27-
2810
### `AVX2_NTT`
2911

3012
* Faster AVX2 optimized NTT multiplication for Ring-LWE lattice cryptography.

BIBLIOGRAPHY.yml

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -150,16 +150,6 @@
150150
name: Cycle counting on Apple M1
151151
url: https://gist.github.com/dougallj/5bafb113492047c865c0c8cfbc930155#file-m1_robsize-c-L390
152152

153-
- id: ACVP_Server
154-
author: National Institute of Standards and Technology
155-
name: ACVP Test Vectors
156-
url: https://github.com/usnistgov/ACVP-Server/tree/master/gen-val/json-files
157-
158-
- id: ACVP_Spec
159-
author: Celi, Christopher
160-
name: ACVP ML-KEM JSON Specification
161-
url: https://pages.nist.gov/ACVP/draft-celi-acvp-ml-kem.html
162-
163153
- id: tiny_sha3
164154
name: tiny_sha3
165155
author: Saarinen, Markku-Juhani O.

test/acvp_client.py

Lines changed: 79 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,49 +3,64 @@
33
# SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT
44

55
# ACVP client for ML-KEM
6-
#
7-
# Processes json files from
8-
# https://github.com/usnistgov/ACVP-Server/blob/master/gen-val/json-files
9-
#
6+
# See https://pages.nist.gov/ACVP/draft-celi-acvp-ml-kem.html and
7+
# https://github.com/usnistgov/ACVP-Server/tree/master/gen-val/json-files
108
# Invokes `acvp_mlkem{lvl}` under the hood.
119

1210
import argparse
1311
import os
1412
import json
1513
import sys
1614
import subprocess
15+
import urllib.request
16+
from pathlib import Path
1717

1818
# Check if we need to use a wrapper for execution (e.g. QEMU)
1919
exec_prefix = os.environ.get("EXEC_WRAPPER", "")
2020
exec_prefix = exec_prefix.split(" ") if exec_prefix != "" else []
2121

22-
acvp_dir = "test/acvp_data"
23-
acvp_jsons = [
24-
(
25-
f"{acvp_dir}/acvp_v1.1.0.38_keyGen_prompt.json",
26-
f"{acvp_dir}/acvp_v1.1.0.38_keyGen_expectedResults.json",
27-
),
28-
(
29-
f"{acvp_dir}/acvp_v1.1.0.39_keyGen_prompt.json",
30-
f"{acvp_dir}/acvp_v1.1.0.39_keyGen_expectedResults.json",
31-
),
32-
(
33-
f"{acvp_dir}/acvp_v1.1.0.40_keyGen_prompt.json",
34-
f"{acvp_dir}/acvp_v1.1.0.40_keyGen_expectedResults.json",
35-
),
36-
(
37-
f"{acvp_dir}/acvp_v1.1.0.38_encapDecap_prompt.json",
38-
f"{acvp_dir}/acvp_v1.1.0.38_encapDecap_expectedResults.json",
39-
),
40-
(
41-
f"{acvp_dir}/acvp_v1.1.0.39_encapDecap_prompt.json",
42-
f"{acvp_dir}/acvp_v1.1.0.39_encapDecap_expectedResults.json",
43-
),
44-
(
45-
f"{acvp_dir}/acvp_v1.1.0.40_encapDecap_prompt.json",
46-
f"{acvp_dir}/acvp_v1.1.0.40_encapDecap_expectedResults.json",
47-
),
48-
]
22+
23+
def download_acvp_files(version="v1.1.0.40"):
24+
"""Download ACVP test files for the specified version if not present."""
25+
base_url = f"https://raw.githubusercontent.com/usnistgov/ACVP-Server/{version}/gen-val/json-files"
26+
27+
# Files we need to download for ML-KEM
28+
files_to_download = [
29+
"ML-KEM-keyGen-FIPS203/prompt.json",
30+
"ML-KEM-keyGen-FIPS203/expectedResults.json",
31+
"ML-KEM-encapDecap-FIPS203/prompt.json",
32+
"ML-KEM-encapDecap-FIPS203/expectedResults.json",
33+
]
34+
35+
# Create directory structure
36+
data_dir = Path(f"test/.acvp-data/{version}/files")
37+
data_dir.mkdir(parents=True, exist_ok=True)
38+
39+
for file_path in files_to_download:
40+
local_file = data_dir / file_path
41+
local_file.parent.mkdir(parents=True, exist_ok=True)
42+
43+
if not local_file.exists():
44+
url = f"{base_url}/{file_path}"
45+
print(f"Downloading {file_path}...", file=sys.stderr)
46+
try:
47+
urllib.request.urlretrieve(url, local_file)
48+
# Verify the file is valid JSON
49+
with open(local_file, "r") as f:
50+
json.load(f)
51+
except json.JSONDecodeError as e:
52+
print(
53+
f"Error: Downloaded file {file_path} is not valid JSON: {e}",
54+
file=sys.stderr,
55+
)
56+
local_file.unlink(missing_ok=True)
57+
return False
58+
except Exception as e:
59+
print(f"Error downloading {file_path}: {e}", file=sys.stderr)
60+
local_file.unlink(missing_ok=True)
61+
return False
62+
63+
return True
4964

5065

5166
def loadAcvpData(prompt, expectedResults):
@@ -59,9 +74,21 @@ def loadAcvpData(prompt, expectedResults):
5974
return (prompt, promptData, expectedResults, expectedResultsData)
6075

6176

62-
def loadDefaultAcvpData():
77+
def loadDefaultAcvpData(version="v1.1.0.40"):
78+
79+
data_dir = f"test/.acvp-data/{version}/files"
80+
acvp_jsons_for_version = [
81+
(
82+
f"{data_dir}/ML-KEM-keyGen-FIPS203/prompt.json",
83+
f"{data_dir}/ML-KEM-keyGen-FIPS203/expectedResults.json",
84+
),
85+
(
86+
f"{data_dir}/ML-KEM-encapDecap-FIPS203/prompt.json",
87+
f"{data_dir}/ML-KEM-encapDecap-FIPS203/expectedResults.json",
88+
),
89+
]
6390
acvp_data = []
64-
for prompt, expectedResults in acvp_jsons:
91+
for prompt, expectedResults in acvp_jsons_for_version:
6592
acvp_data.append(loadAcvpData(prompt, expectedResults))
6693
return acvp_data
6794

@@ -280,7 +307,7 @@ def runTest(data, output):
280307
info("ALL GOOD!")
281308

282309

283-
def test(prompt, expected, output):
310+
def test(prompt, expected, output, version="v1.1.0.40"):
284311
assert (
285312
prompt is not None or output is None
286313
), "cannot produce output if there is no input"
@@ -293,8 +320,8 @@ def test(prompt, expected, output):
293320
if prompt is not None:
294321
data = [loadAcvpData(prompt, expected)]
295322
else:
296-
# otherwise, load default data from acvp_data
297-
data = loadDefaultAcvpData()
323+
# load data from downloaded files
324+
data = loadDefaultAcvpData(version)
298325

299326
runTest(data, output)
300327

@@ -312,5 +339,20 @@ def test(prompt, expected, output):
312339
parser.add_argument(
313340
"-o", "--output", help="Path to output file in json format", required=False
314341
)
342+
parser.add_argument(
343+
"--version",
344+
"-v",
345+
default="v1.1.0.40",
346+
help="ACVP test vector version (default: v1.1.0.40)",
347+
)
315348
args = parser.parse_args()
316-
test(args.prompt, args.expected, args.output)
349+
350+
if args.prompt is None:
351+
print(f"Using ACVP test vectors version {args.version}", file=sys.stderr)
352+
353+
# Download files if needed
354+
if not download_acvp_files(args.version):
355+
print("Failed to download ACVP test files", file=sys.stderr)
356+
sys.exit(1)
357+
358+
test(args.prompt, args.expected, args.output, args.version)

test/acvp_data/README.md

Lines changed: 0 additions & 8 deletions
This file was deleted.

0 commit comments

Comments
 (0)