@@ -49,13 +49,23 @@ def download_file(url, dest, token):
4949 time .sleep (wait )
5050
5151
52+ def acvp_version_has_tr1 (version ):
53+ """Whether `version` ships the FIPS204-tr1 sigGen vectors (v1.1.0.43+)."""
54+ try :
55+ parts = tuple (int (x ) for x in version .lstrip ("v" ).split ("." ))
56+ except ValueError :
57+ # Non-numeric ref (branch or commit); assume the vectors are present.
58+ return True
59+ return parts >= (1 , 1 , 0 , 43 )
60+
61+
5262def download_acvp_files (version ):
5363 """Download ACVP test files for the specified version if not present."""
5464 api_base = (
5565 "https://api.github.com/repos/usnistgov/ACVP-Server/contents/gen-val/json-files"
5666 )
5767
58- # Files we need to download for ML-KEM
68+ # Files we need to download for ML-DSA
5969 files_to_download = [
6070 "ML-DSA-keyGen-FIPS204/prompt.json" ,
6171 "ML-DSA-keyGen-FIPS204/expectedResults.json" ,
@@ -65,6 +75,13 @@ def download_acvp_files(version):
6575 "ML-DSA-sigVer-FIPS204/expectedResults.json" ,
6676 ]
6777
78+ # The FIPS204-tr1 sigGen vectors add seed/expanded key-format groups.
79+ if acvp_version_has_tr1 (version ):
80+ files_to_download += [
81+ "ML-DSA-sigGen-FIPS204-tr1/prompt.json" ,
82+ "ML-DSA-sigGen-FIPS204-tr1/expectedResults.json" ,
83+ ]
84+
6885 # Create directory structure
6986 data_dir = Path (f"test/acvp/.acvp-data/{ version } /files" )
7087 data_dir .mkdir (parents = True , exist_ok = True )
@@ -153,6 +170,17 @@ def loadDefaultAcvpData(version, supported_modes=None):
153170 f"{ data_dir } /ML-DSA-sigVer-FIPS204/expectedResults.json" ,
154171 ),
155172 ]
173+
174+ # FIPS204-tr1 sigGen vectors (seed/expanded key formats) exist from v1.1.0.43.
175+ if acvp_version_has_tr1 (version ):
176+ acvp_jsons_for_version .append (
177+ (
178+ "sigGen" ,
179+ f"{ data_dir } /ML-DSA-sigGen-FIPS204-tr1/prompt.json" ,
180+ f"{ data_dir } /ML-DSA-sigGen-FIPS204-tr1/expectedResults.json" ,
181+ )
182+ )
183+
156184 acvp_data = []
157185 for mode , prompt , expectedResults in acvp_jsons_for_version :
158186 if mode not in supported_modes :
@@ -270,6 +298,13 @@ def unsupported_hash(tg, tc):
270298 return f"hash algorithm { hashAlg } unavailable in this Python's hashlib"
271299
272300
301+ def seed_key_format (tg , tc ):
302+ # keyFormat 'seed' cases need keyGen to expand the key into the private key.
303+ if tg .get ("keyFormat" ) == "seed" :
304+ return "seed key format requires the keyGen API"
305+ return None
306+
307+
273308def filter_test_cases (acvp_data , should_drop ):
274309 # Drop cases for which should_drop(tg, tc) returns a reason (None keeps).
275310 # Reasons come from the prompt but are applied to expected data too.
@@ -299,6 +334,13 @@ def run_sigGen_test(tg, tc):
299334
300335 assert tg ["testType" ] == "AFT"
301336
337+ # keyFormat 'seed' passes a seed for the binary to expand into the private
338+ # key; 'expanded' (or absent) passes the expanded sk directly.
339+ if tg .get ("keyFormat" ) == "seed" :
340+ sk_arg = f"seed={ tc ['seed' ]} "
341+ else :
342+ sk_arg = f"sk={ tc ['sk' ]} "
343+
302344 is_deterministic = tg ["deterministic" ] is True
303345 if "preHash" in tg and tg ["preHash" ] == "preHash" :
304346 assert len (tc ["context" ]) <= 2 * 255
@@ -315,7 +357,7 @@ def run_sigGen_test(tg, tc):
315357 target ,
316358 f"message={ tc ['message' ]} " ,
317359 f"context={ tc ['context' ]} " ,
318- f"sk= { tc [ 'sk' ] } " ,
360+ sk_arg ,
319361 ]
320362 else :
321363 ph = compute_hash (tc ["message" ], tc ["hashAlg" ])
@@ -327,7 +369,7 @@ def run_sigGen_test(tg, tc):
327369 target ,
328370 f"ph={ ph } " ,
329371 f"context={ tc ['context' ]} " ,
330- f"sk= { tc [ 'sk' ] } " ,
372+ sk_arg ,
331373 f"hashAlg={ tc ['hashAlg' ]} " ,
332374 ]
333375 elif tg ["signatureInterface" ] == "external" :
@@ -340,7 +382,7 @@ def run_sigGen_test(tg, tc):
340382 acvp_bin ,
341383 target ,
342384 f"message={ tc ['message' ]} " ,
343- f"sk= { tc [ 'sk' ] } " ,
385+ sk_arg ,
344386 f"context={ tc ['context' ]} " ,
345387 ]
346388 else : # signatureInterface=internal
@@ -359,7 +401,7 @@ def run_sigGen_test(tg, tc):
359401 acvp_bin ,
360402 target ,
361403 f"message={ msg } " ,
362- f"sk= { tc [ 'sk' ] } " ,
404+ sk_arg ,
363405 f"externalMu={ externalMu } " ,
364406 ]
365407
@@ -555,6 +597,14 @@ def test(
555597 info ("No test data to run (all modes disabled in this build)" )
556598 return
557599
600+ # Seed key-format cases require keyGen to expand the key; drop them when the
601+ # build does not provide it (unconditionally, unlike --skip-unsupported).
602+ if supported_modes is not None and "keyGen" not in supported_modes :
603+ seed_reasons = filter_test_cases (data , seed_key_format )
604+ if seed_reasons :
605+ summary = ", " .join (sorted (set (seed_reasons )))
606+ info (f"Skipping { len (seed_reasons )} test case(s): { summary } " )
607+
558608 reasons = filter_test_cases (data , unsupported_hash )
559609 if reasons :
560610 summary = ", " .join (sorted (set (reasons )))
@@ -583,8 +633,8 @@ def test(
583633parser .add_argument (
584634 "--version" ,
585635 "-v" ,
586- default = "v1.1.0.41 " ,
587- help = "ACVP test vector version (default: v1.1.0.41 )" ,
636+ default = "v1.1.0.43 " ,
637+ help = "ACVP test vector version (default: v1.1.0.43 )" ,
588638)
589639parser .add_argument (
590640 "--no-keygen" ,
0 commit comments