Skip to content

Commit

Permalink
Tests: Re-introduce ACVP client as standalone Python script
Browse files Browse the repository at this point in the history
The ACVP client was originally introduced as a standalone script
but later added to `tests`, imposing the test infrastructure's
dependencies.

This commit re-introduces the original standalone script and adds
it to `make quickcheck`. The ACVP test within the test scripts
remains for now, but should likely be reconciled.

Signed-off-by: Hanno Becker <[email protected]>
  • Loading branch information
hanno-becker authored and mkannwischer committed Dec 22, 2024
1 parent 8dceb71 commit 92f1f5c
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 1 deletion.
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ quickcheck: checkall
buildall: mlkem nistkat kat acvp
$(Q)echo " Everything builds fine!"

checkall: buildall check_kat check_nistkat check_func
checkall: buildall check_kat check_nistkat check_func check_acvp
$(Q)echo " Everything checks fine!"

check_kat: buildall
Expand All @@ -33,6 +33,9 @@ check_func: buildall
$(MLKEM768_DIR)/bin/test_mlkem768
$(MLKEM1024_DIR)/bin/test_mlkem1024

check_acvp: buildall
python3 ./test/acvp_client.py

lib: $(BUILD_DIR)/libmlkem.a

mlkem: \
Expand Down
116 changes: 116 additions & 0 deletions test/acvp_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Copyright (c) 2024 The mlkem-native project authors
# SPDX-License-Identifier: Apache-2.0

# ACVP client for ML-KEM
#
# Processes 'internalProjection.json' files from
# https://github.com/usnistgov/ACVP-Server/blob/master/gen-val/json-files
#
# Invokes `acvp_mlkem{lvl}` under the hood.

import json
import subprocess

acvp_dir = "test/acvp_data"
acvp_keygen_json = f"{acvp_dir}/acvp_keygen_internalProjection.json"
acvp_encapDecap_json = f"{acvp_dir}/acvp_encapDecap_internalProjection.json"

with open(acvp_keygen_json, "r") as f:
acvp_keygen_data = json.load(f)

with open(acvp_encapDecap_json, "r") as f:
acvp_encapDecap_data = json.load(f)


def get_acvp_binary(tg):
"""Convert JSON dict for ACVP test group to suitable ACVP binary."""
parameterSetToLevel = {
"ML-KEM-512": 512,
"ML-KEM-768": 768,
"ML-KEM-1024": 1024,
}
level = parameterSetToLevel[tg["parameterSet"]]
basedir = f"./test/build/mlkem{level}/bin"
acvp_bin = f"acvp_mlkem{level}"
return f"{basedir}/{acvp_bin}"


def run_encapDecap_test(tg, tc):
print(f"Running encapDecap test case {tc['tcId']} ({tg['function']}) ... ", end="")
if tg["function"] == "encapsulation":
acvp_bin = get_acvp_binary(tg)
acvp_call = [
acvp_bin,
"encapDecap",
"AFT",
"encapsulation",
f"ek={tc['ek']}",
f"m={tc['m']}",
]
result = subprocess.run(acvp_call, encoding="utf-8", capture_output=True)
if result.returncode != 0:
print("FAIL!")
print(f"{acvp_call} failed with error code {result.returncode}")
print(result.stderr)
exit(1)
# Extract results and compare to expected data
for l in result.stdout.splitlines():
(k, v) = l.split("=")
if v != tc[k]:
print("FAIL!")
print(f"Mismatching result for {k}: expected {tc[k]}, got {v}")
exit(1)
print("OK")
elif tg["function"] == "decapsulation":
acvp_bin = get_acvp_binary(tg)
acvp_call = [
acvp_bin,
"encapDecap",
"VAL",
"decapsulation",
f"dk={tg['dk']}",
f"c={tc['c']}",
]
result = subprocess.run(acvp_call, encoding="utf-8", capture_output=True)
if result.returncode != 0:
print("FAIL!")
print(f"{acvp_call} failed with error code {result.returncode}")
print(result.stderr)
exit(1)
# Extract results and compare to expected data
for l in result.stdout.splitlines():
(k, v) = l.split("=")
if v != tc[k]:
print("FAIL!")
print(f"Mismatching result for {k}: expected {tc[k]}, got {v}")
exit(1)
print("OK")


def run_keyGen_test(tg, tc):
print(f"Running keyGen test case {tc['tcId']} ... ", end="")
acvp_bin = get_acvp_binary(tg)
acvp_call = [acvp_bin, "keyGen", "AFT", f"z={tc['z']}", f"d={tc['d']}"]
result = subprocess.run(acvp_call, encoding="utf-8", capture_output=True)
if result.returncode != 0:
print("FAIL!")
print(f"{acvp_call} failed with error code {result.returncode}")
print(result.stderr)
exit(1)
# Extract results and compare to expected data
for l in result.stdout.splitlines():
(k, v) = l.split("=")
if v != tc[k]:
print("FAIL!")
print(f"Mismatching result for {k}: expected {tc[k]}, got {v}")
exit(1)
print("OK")


for tg in acvp_encapDecap_data["testGroups"]:
for tc in tg["tests"]:
run_encapDecap_test(tg, tc)

for tg in acvp_keygen_data["testGroups"]:
for tc in tg["tests"]:
run_keyGen_test(tg, tc)

0 comments on commit 92f1f5c

Please sign in to comment.