Skip to content

Commit ec75433

Browse files
authored
Merge pull request #1119 from pq-code-package/acvp-on-the-fly
ACVP: Download testvectors transparently on the fly
2 parents a4ca960 + b57d14b commit ec75433

21 files changed

+120
-6339
lines changed

.github/workflows/base.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,31 @@ jobs:
6161
- name: tests func
6262
run: |
6363
./scripts/tests func --check-namespace
64+
quickcheck-acvp:
65+
strategy:
66+
fail-fast: false
67+
matrix:
68+
external:
69+
- ${{ github.repository_owner != 'pq-code-package' }}
70+
target:
71+
- runner: pqcp-arm64
72+
name: 'aarch64'
73+
- runner: ubuntu-latest
74+
name: 'x86_64'
75+
acvp-version: [v1.1.0.38, v1.1.0.39, v1.1.0.40]
76+
exclude:
77+
- {external: true,
78+
target: {
79+
runner: pqcp-arm64,
80+
name: 'aarch64'
81+
}}
82+
name: Quickcheck ACVP (${{ matrix.target.name }}, ${{ matrix.acvp-version }})
83+
runs-on: ${{ matrix.target.runner }}
84+
steps:
85+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
86+
- name: Run ACVP test
87+
run: |
88+
./scripts/tests acvp --version ${{ matrix.acvp-version }}
6489
quickcheck_bench:
6590
strategy:
6691
fail-fast: false

.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.

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ run_func_1024: func_1024
5050
run_func: run_func_512 run_func_768 run_func_1024
5151

5252
run_acvp: acvp
53-
python3 ./test/acvp_client.py
53+
python3 ./test/acvp_client.py $(if $(ACVP_VERSION),--version $(ACVP_VERSION))
5454

5555
func_512: $(MLKEM512_DIR)/bin/test_mlkem512
5656
$(Q)echo " FUNC ML-KEM-512: $^"

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,10 @@ See [BUILDING.md](BUILDING.md) for more information.
5050

5151
## Applications
5252

53-
mlkem-native is used in [libOQS](https://github.com/open-quantum-safe/liboqs/) of the Open Quantum Safe project, and in AWS' Cryptography library [AWS-LC](https://github.com/aws/aws-lc/).
53+
mlkem-native is used in
54+
- [libOQS](https://github.com/open-quantum-safe/liboqs/) of the Open Quantum Safe project since [0.13.0](https://github.com/open-quantum-safe/liboqs/releases/tag/0.13.0) (as the default ML-KEM implementation)
55+
- AWS' Cryptography library [AWS-LC](https://github.com/aws/aws-lc/) since [v1.50.0](https://github.com/aws/aws-lc/releases/tag/v1.50.0)
56+
- The [rustls](https://github.com/rustls/rustls) TLS library written in Rust since [0.23.28](https://github.com/rustls/rustls/releases/tag/v%2F0.23.28) (through AWS-LC as the default cryptography provider)
5457

5558
## Formal Verification
5659

scripts/tests

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,10 @@ class Tests:
506506
if stack_flags:
507507
env_update["STACK_ANALYSIS_FLAGS"] = " ".join(stack_flags)
508508

509+
# Add ACVP version for ACVP tests
510+
if test_type == TEST_TYPES.ACVP and hasattr(self.args, "version"):
511+
env_update["ACVP_VERSION"] = self.args.version
512+
509513
env = os.environ.copy()
510514
env.update(env_update)
511515

@@ -1054,6 +1058,11 @@ def cli():
10541058
acvp_parser = cmd_subparsers.add_parser(
10551059
"acvp", help="Run ACVP client", parents=[common_parser]
10561060
)
1061+
acvp_parser.add_argument(
1062+
"--version",
1063+
default="v1.1.0.40",
1064+
help="ACVP test vector version (default: v1.1.0.40)",
1065+
)
10571066

10581067
# examples arguments
10591068
examples_parser = cmd_subparsers.add_parser(

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)