99import os
1010import subprocess
1111import sys
12+ import urllib .request
1213from concurrent .futures import ThreadPoolExecutor , as_completed
13-
14- # === JSON parsing functions ===
14+ from pathlib import Path
15+
16+ # === ACVP file downloading ===
17+
18+ def download_acvp_files (version = "v1.1.0.40" ):
19+ """Download ACVP test files for the specified version if not present."""
20+ base_url = f"https://raw.githubusercontent.com/usnistgov/ACVP-Server/{ version } /gen-val/json-files"
21+
22+ # Files we need to download
23+ files_to_download = [
24+ "SLH-DSA-keyGen-FIPS205/prompt.json" ,
25+ "SLH-DSA-keyGen-FIPS205/expectedResults.json" ,
26+ "SLH-DSA-sigGen-FIPS205/prompt.json" ,
27+ "SLH-DSA-sigGen-FIPS205/expectedResults.json" ,
28+ "SLH-DSA-sigVer-FIPS205/prompt.json" ,
29+ "SLH-DSA-sigVer-FIPS205/expectedResults.json" ,
30+ "SLH-DSA-sigVer-FIPS205/internalProjection.json"
31+ ]
32+
33+ # Create directory structure
34+ data_dir = Path (f"test/.acvp-data/{ version } /files" )
35+ data_dir .mkdir (parents = True , exist_ok = True )
36+
37+ for file_path in files_to_download :
38+ local_file = data_dir / file_path
39+ local_file .parent .mkdir (parents = True , exist_ok = True )
40+
41+ if not local_file .exists ():
42+ url = f"{ base_url } /{ file_path } "
43+ print (f"Downloading { file_path } ..." , file = sys .stderr )
44+ try :
45+ urllib .request .urlretrieve (url , local_file )
46+ # Verify the file is valid JSON
47+ with open (local_file , 'r' ) as f :
48+ json .load (f )
49+ except json .JSONDecodeError as e :
50+ print (f"Error: Downloaded file { file_path } is not valid JSON: { e } " , file = sys .stderr )
51+ local_file .unlink (missing_ok = True ) # Remove corrupted file
52+ return False
53+ except Exception as e :
54+ print (f"Error downloading { file_path } : { e } " , file = sys .stderr )
55+ local_file .unlink (missing_ok = True ) # Remove partial file
56+ return False
57+
58+ return True
59+
60+ # === JSON parsing functions ===
1561
1662def slhdsa_load_keygen (req_fn , res_fn ):
1763 with open (req_fn ) as f :
@@ -170,15 +216,22 @@ def build_command(x):
170216
171217def main ():
172218 parser = argparse .ArgumentParser (description = "SLH-DSA ACVP test runner" )
173- parser .add_argument ("--jobs" , "-j" , type = int , default = os .cpu_count () or 4 ,
219+ parser .add_argument ("--jobs" , "-j" , type = int , default = os .cpu_count () or 4 ,
174220 help = "Number of parallel jobs (default: auto-detect CPU cores)" )
221+ parser .add_argument ("--version" , "-v" , default = "v1.1.0.40" ,
222+ help = "ACVP test vector version (default: v1.1.0.40)" )
175223
176224 args = parser .parse_args ()
177225
178- print ("Generating test commands from ACVP JSON files..." , file = sys .stderr )
226+ print (f"Using ACVP test vectors version { args .version } " , file = sys .stderr )
227+
228+ # Download files if needed
229+ if not download_acvp_files (args .version ):
230+ print ("Failed to download ACVP test files" , file = sys .stderr )
231+ return 1
179232
180233 try :
181- json_path = ' test/ACVP-Server/gen-val/json- files/'
234+ json_path = f" test/.acvp-data/ { args . version } / files/"
182235
183236 keygen_kat = slhdsa_load_keygen (
184237 json_path + 'SLH-DSA-keyGen-FIPS205/prompt.json' ,
@@ -194,8 +247,7 @@ def main():
194247 json_path + 'SLH-DSA-sigVer-FIPS205/internalProjection.json' )
195248
196249 except FileNotFoundError as e :
197- print (f"Error: Could not find ACVP JSON files. Make sure submodule is initialized." , file = sys .stderr )
198- print (f"Run: git submodule update --init --recursive" , file = sys .stderr )
250+ print (f"Error: Could not find ACVP JSON files: { e } " , file = sys .stderr )
199251 return 1
200252
201253 total_tests = len (keygen_kat ) + len (siggen_kat ) + len (sigver_kat )
0 commit comments