Skip to content

Commit 49c4d01

Browse files
committed
Add KAT test Windows support (C backend only) and unify across platforms
Unix KAT tests rely on shell pipelines (sha256sum, cut, xargs) not available on Windows. Introduce test/src/kat_client.py — a portable Python script that runs gen_KAT binaries, SHA-256-hashes their stdout, and checks against META.yml, replacing the shell one-liner: ``` set -o pipefail; $W $MLKEM512_DIR/bin/gen_KAT512 | $SHA256SUM | \ cut -d " " -f 1 | xargs ./META.sh ML-KEM-512 kat-sha256 ``` Add gen_KAT{512,768,1024}, gen_KAT, run_kat, and test targets to Makefile.Microsoft_nmake. quickcheck now aliases test. To keep KAT checking uniform across platforms, the Unix Makefile is migrated to the same Python script. A single mechanism now drives KAT verification on both Unix and Windows. META.sh and the SHA256SUM detection block are removed as they are no longer needed. Signed-off-by: willieyz <willie.zhao@chelpis.com>
1 parent 12b096d commit 49c4d01

4 files changed

Lines changed: 131 additions & 57 deletions

File tree

META.sh

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

Makefile

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,18 +49,12 @@ build: func kat acvp wycheproof
4949
test: run_kat run_func run_acvp run_wycheproof run_unit run_alloc run_rng_fail
5050
$(Q)echo " Everything checks fine!"
5151

52-
# Detect available SHA256 command
53-
SHA256SUM := $(shell command -v shasum >/dev/null 2>&1 && echo "shasum -a 256" || (command -v sha256sum >/dev/null 2>&1 && echo "sha256sum" || echo ""))
54-
ifeq ($(SHA256SUM),)
55-
$(error Neither 'shasum' nor 'sha256sum' found. Please install one of these tools.)
56-
endif
57-
5852
run_kat_512: kat_512
59-
set -o pipefail; $(W) $(MLKEM512_DIR)/bin/gen_KAT512 | $(SHA256SUM) | cut -d " " -f 1 | xargs ./META.sh ML-KEM-512 kat-sha256
53+
EXEC_WRAPPER="$(EXEC_WRAPPER)" python3 test/src/kat_client.py --scheme 512
6054
run_kat_768: kat_768
61-
set -o pipefail; $(W) $(MLKEM768_DIR)/bin/gen_KAT768 | $(SHA256SUM) | cut -d " " -f 1 | xargs ./META.sh ML-KEM-768 kat-sha256
55+
EXEC_WRAPPER="$(EXEC_WRAPPER)" python3 test/src/kat_client.py --scheme 768
6256
run_kat_1024: kat_1024
63-
set -o pipefail; $(W) $(MLKEM1024_DIR)/bin/gen_KAT1024 | $(SHA256SUM) | cut -d " " -f 1 | xargs ./META.sh ML-KEM-1024 kat-sha256
57+
EXEC_WRAPPER="$(EXEC_WRAPPER)" python3 test/src/kat_client.py --scheme 1024
6458
run_kat: run_kat_512 run_kat_768 run_kat_1024
6559

6660
run_func_512: func_512

Makefile.Microsoft_nmake

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,15 +110,40 @@ acvp_mlkem1024: $(OBJ_FILES_1024) $(MLKEM1024_BUILD_DIR)\test\acvp\acvp_mlkem.ob
110110
@if NOT EXIST $(MLKEM1024_BUILD_DIR)\bin mkdir $(MLKEM1024_BUILD_DIR)\bin
111111
$(CC) $(CFLAGS) /D MLK_CONFIG_PARAMETER_SET=1024 /Fe$(MLKEM1024_BUILD_DIR)\bin\acvp_mlkem1024 $** /link
112112

113+
# compile KAT generator for mlkem512
114+
gen_KAT512: $(OBJ_FILES_512) $(MLKEM512_BUILD_DIR)\test\gen_KAT.obj $(BUILD_DIR)\randombytes\notrandombytes.obj
115+
@if NOT EXIST $(MLKEM512_BUILD_DIR)\bin mkdir $(MLKEM512_BUILD_DIR)\bin
116+
$(CC) $(CFLAGS) /D MLK_CONFIG_PARAMETER_SET=512 /Fe$(MLKEM512_BUILD_DIR)\bin\gen_KAT512 $** /link
117+
118+
# compile KAT generator for mlkem768
119+
gen_KAT768: $(OBJ_FILES_768) $(MLKEM768_BUILD_DIR)\test\gen_KAT.obj $(BUILD_DIR)\randombytes\notrandombytes.obj
120+
@if NOT EXIST $(MLKEM768_BUILD_DIR)\bin mkdir $(MLKEM768_BUILD_DIR)\bin
121+
$(CC) $(CFLAGS) /D MLK_CONFIG_PARAMETER_SET=768 /Fe$(MLKEM768_BUILD_DIR)\bin\gen_KAT768 $** /link
122+
123+
# compile KAT generator for mlkem1024
124+
gen_KAT1024: $(OBJ_FILES_1024) $(MLKEM1024_BUILD_DIR)\test\gen_KAT.obj $(BUILD_DIR)\randombytes\notrandombytes.obj
125+
@if NOT EXIST $(MLKEM1024_BUILD_DIR)\bin mkdir $(MLKEM1024_BUILD_DIR)\bin
126+
$(CC) $(CFLAGS) /D MLK_CONFIG_PARAMETER_SET=1024 /Fe$(MLKEM1024_BUILD_DIR)\bin\gen_KAT1024 $** /link
127+
113128
acvp: acvp_mlkem512 acvp_mlkem768 acvp_mlkem1024
114129

130+
gen_KAT: gen_KAT512 gen_KAT768 gen_KAT1024
131+
132+
run_kat: gen_KAT
133+
python test/src/kat_client.py
134+
115135
run_acvp: acvp
116136
python test/acvp/acvp_client.py
117137

118-
quickcheck: test_mlkem512 test_mlkem768 test_mlkem1024 run_acvp
138+
run_func: test_mlkem512 test_mlkem768 test_mlkem1024
119139
$(MLKEM512_BUILD_DIR)\bin\test_mlkem512.exe
120-
$(MLKEM768_BUILD_DIR)\bin\test_mlkem768.exe
121-
$(MLKEM1024_BUILD_DIR)\bin\test_mlkem1024.exe
140+
$(MLKEM768_BUILD_DIR)\bin\test_mlkem768.exe
141+
$(MLKEM1024_BUILD_DIR)\bin\test_mlkem1024.exe
142+
143+
test: run_func run_acvp run_kat
144+
@echo Everything checks fine!
145+
146+
quickcheck: test
122147

123148
clean:
124149
-DEL $(BUILD_DIR)

test/src/kat_client.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) The mlkem-native project authors
3+
# SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT
4+
5+
import hashlib
6+
import os
7+
import subprocess
8+
import sys
9+
import argparse
10+
from pathlib import Path
11+
12+
13+
# Check if we need to use a wrapper for execution (e.g. QEMU)
14+
exec_prefix = os.environ.get("EXEC_WRAPPER", "")
15+
exec_prefix = exec_prefix.split(" ") if exec_prefix != "" else []
16+
17+
18+
def err(msg, **kwargs):
19+
print(msg, file=sys.stderr, **kwargs)
20+
21+
22+
def info(msg, **kwargs):
23+
print(msg, **kwargs)
24+
25+
26+
def read_meta_hashes():
27+
hashes = {}
28+
current_name = None
29+
with open("META.yml") as f:
30+
for line in f:
31+
line = line.strip()
32+
if line.startswith("- name:"):
33+
current_name = line.split(":", 1)[1].strip()
34+
elif line.startswith("kat-sha256:") and current_name:
35+
hashes[current_name] = line.split(":", 1)[1].strip()
36+
return hashes
37+
38+
39+
def get_kat_binary(level):
40+
suffix = ".exe" if sys.platform == "win32" else ""
41+
return Path("test/build") / f"mlkem{level}" / "bin" / f"gen_KAT{level}{suffix}"
42+
43+
44+
def run_kat_single(level, ref_hash):
45+
scheme_name = f"ML-KEM-{level}"
46+
binary = get_kat_binary(level)
47+
48+
if not binary.exists():
49+
err(f"Binary not found: {binary}")
50+
return False
51+
52+
cmd = exec_prefix + [str(binary)]
53+
result = subprocess.run(cmd, capture_output=True)
54+
55+
if result.returncode != 0:
56+
err("FAIL!")
57+
err(f"{cmd} failed with error code {result.returncode}")
58+
err(result.stderr.decode("utf-8", errors="replace"))
59+
return False
60+
61+
computed = hashlib.sha256(result.stdout).hexdigest()
62+
63+
if computed == ref_hash:
64+
info(f"META.yml {scheme_name} kat-sha256: OK")
65+
return True
66+
else:
67+
err(f"META.yml {scheme_name} kat-sha256: FAIL ({ref_hash} != {computed})")
68+
return False
69+
70+
71+
def main():
72+
parser = argparse.ArgumentParser()
73+
parser.add_argument(
74+
"--scheme",
75+
choices=["512", "768", "1024"],
76+
help="Run KAT for only one parameter set (default: all)",
77+
)
78+
args = parser.parse_args()
79+
80+
levels = [int(args.scheme)] if args.scheme else [512, 768, 1024]
81+
ref_hashes = read_meta_hashes()
82+
83+
failed = False
84+
for level in levels:
85+
scheme_name = f"ML-KEM-{level}"
86+
ref = ref_hashes.get(scheme_name)
87+
if ref is None:
88+
err(f"META.yml: no kat-sha256 entry for {scheme_name}")
89+
failed = True
90+
continue
91+
if not run_kat_single(level, ref):
92+
failed = True
93+
94+
if failed:
95+
sys.exit(1)
96+
info("ALL GOOD!")
97+
98+
99+
if __name__ == "__main__":
100+
main()

0 commit comments

Comments
 (0)