1 1.1 christos #!/usr/bin/env python3 2 1.1 christos # 3 1.1 christos # Copyright 2025 The OpenSSL Project Authors. All Rights Reserved. 4 1.1 christos # 5 1.1 christos # Licensed under the Apache License 2.0 (the "License"). You may not use 6 1.1 christos # this file except in compliance with the License. You can obtain a copy 7 1.1 christos # in the file LICENSE in the source distribution or at 8 1.1 christos # https://www.openssl.org/source/license.html 9 1.1 christos 10 1.1 christos # A python program written to parse (version 42) of the ACVP test vectors for 11 1.1 christos # SLH_DSA. The 3 files that can be processed by this utility can be downloaded 12 1.1 christos # from 13 1.1 christos # https://github.com/usnistgov/ACVP-Server/blob/master/gen-val/json-files/SLH-DSA-keyGen-FIPS204/internalProjection.json 14 1.1 christos # https://github.com/usnistgov/ACVP-Server/blob/master/gen-val/json-files/SLH-DSA-sigGen-FIPS204/internalProjection.json 15 1.1 christos # https://github.com/usnistgov/ACVP-Server/blob/master/gen-val/json-files/SLH-DSA-sigVer-FIPS204/internalProjection.json 16 1.1 christos # and output from this utility to 17 1.1 christos # test/recipes/30-test_evp_data/evppkey_slh_dsa_keygen.txt 18 1.1 christos # test/recipes/30-test_evp_data/evppkey_slh_dsa_siggen.txt 19 1.1 christos # test/recipes/30-test_evp_data/evppkey_slh_dsa_sigver.txt 20 1.1 christos # 21 1.1 christos # e.g. python3 slhdsa_parse.py ~/Downloads/keygen.json > ./test/recipes/30-test_evp_data/evppkey_slh_dsa_keygen.txt 22 1.1 christos # 23 1.1 christos import json 24 1.1 christos import argparse 25 1.1 christos import datetime 26 1.1 christos import re 27 1.1 christos import sys 28 1.1 christos 29 1.1 christos def eprint(*args, **kwargs): 30 1.1 christos print(*args, file=sys.stderr, **kwargs) 31 1.1 christos 32 1.1 christos def print_label(label, value): 33 1.1 christos print(label + " = " + value) 34 1.1 christos 35 1.1 christos def print_hexlabel(label, tag, value): 36 1.1 christos print(label + " = hex" + tag + ":" + value) 37 1.1 christos 38 1.1 christos def parse_slh_dsa_key_gen(groups): 39 1.1 christos for grp in groups: 40 1.1 christos name = grp['parameterSet'].replace('-', '_') 41 1.1 christos for tst in grp['tests']: 42 1.1 christos print(""); 43 1.1 christos print_label("FIPSversion", ">=3.5.0") 44 1.1 christos print_label("KeyGen", grp['parameterSet']) 45 1.1 christos print_label("KeyName", "tcId" + str(tst['tcId'])) 46 1.1 christos print_hexlabel("Ctrl", "seed", tst['skSeed'] + tst['skPrf'] + tst['pkSeed']) 47 1.1 christos print_hexlabel("CtrlOut", "pub", tst['pk']) 48 1.1 christos print_hexlabel("CtrlOut", "priv", tst['sk']) 49 1.1 christos 50 1.1 christos def parse_slh_dsa_sig_gen(groups): 51 1.1 christos coverage = set() 52 1.1 christos for grp in groups: 53 1.1 christos name = grp['parameterSet'].replace('-', '_') 54 1.1 christos encoding = "1" if grp['preHash'] != 'none' else "0" 55 1.1 christos deterministic = "1" if grp['deterministic'] else "0" 56 1.1 christos for tst in grp['tests']: 57 1.1 christos if tst['hashAlg'] != 'none': 58 1.1 christos continue 59 1.1 christos 60 1.1 christos # Check if there is a similar test already and skip if so 61 1.1 christos # Signature generation tests are very expensive, so we need to 62 1.1 christos # limit things substantially. By default, we only test one of 63 1.1 christos # the slow flavours and one of each kind of the fast ones. 64 1.1 christos if name.find('f') >= 0: 65 1.1 christos context = "1" if 'context' in tst else "0" 66 1.1 christos coverage_name = name + "_" + encoding + deterministic + context 67 1.1 christos else: 68 1.1 christos coverage_name = name 69 1.1 christos extended_test = coverage_name in coverage 70 1.1 christos coverage.add(coverage_name) 71 1.1 christos 72 1.1 christos # Emit test case 73 1.1 christos testname = name + "_" + str(tst['tcId']) 74 1.1 christos print(""); 75 1.1 christos print_label("PrivateKeyRaw", testname + ":" + grp['parameterSet'] + ":" + tst['sk']) 76 1.1 christos print(""); 77 1.1 christos print_label("FIPSversion", ">=3.5.0") 78 1.1 christos print_label("Sign-Message", grp['parameterSet'] + ":" + testname) 79 1.1 christos if extended_test: 80 1.1 christos print_label("Extended-Test", "1") 81 1.1 christos print_label("Input", tst['message']) 82 1.1 christos print_label("Output", tst['signature']) 83 1.1 christos if 'additionalRandomness' in tst: 84 1.1 christos print_hexlabel("Ctrl", "test-entropy", tst['additionalRandomness']); 85 1.1 christos print_label("Ctrl", "deterministic:" + deterministic) 86 1.1 christos print_label("Ctrl", "message-encoding:" + encoding) 87 1.1 christos if 'context' in tst: 88 1.1 christos print_hexlabel("Ctrl", "context-string", tst["context"]) 89 1.1 christos 90 1.1 christos def parse_slh_dsa_sig_ver(groups): 91 1.1 christos for grp in groups: 92 1.1 christos name = grp['parameterSet'].replace('-', '_') 93 1.1 christos encoding = "1" if grp['preHash'] != 'none' else "0" 94 1.1 christos for tst in grp['tests']: 95 1.1 christos if tst['hashAlg'] != 'none': 96 1.1 christos continue 97 1.1 christos testname = name + "_" + str(tst['tcId']) 98 1.1 christos print(""); 99 1.1 christos print_label("PublicKeyRaw", testname + ":" + grp['parameterSet'] + ":" + tst['pk']) 100 1.1 christos print(""); 101 1.1 christos if "reason" in tst: 102 1.1 christos print("# " + tst['reason']) 103 1.1 christos print_label("FIPSversion", ">=3.5.0") 104 1.1 christos print_label("Verify-Message-Public", grp['parameterSet'] + ":" + testname) 105 1.1 christos print_label("Input", tst['message']) 106 1.1 christos print_label("Output", tst['signature']) 107 1.1 christos if 'additionalRandomness' in tst: 108 1.1 christos print_hexlabel("Ctrl", "test-entropy", tst['additionalRandomness']); 109 1.1 christos print_label("Ctrl", "message-encoding:" + encoding) 110 1.1 christos if 'context' in tst: 111 1.1 christos print_hexlabel("Ctrl", "context-string", tst["context"]) 112 1.1 christos if not tst['testPassed']: 113 1.1 christos print_label("Result", "VERIFY_ERROR") 114 1.1 christos 115 1.1 christos parser = argparse.ArgumentParser(description="") 116 1.1 christos parser.add_argument('filename', type=str) 117 1.1 christos args = parser.parse_args() 118 1.1 christos 119 1.1 christos # Open and read the JSON file 120 1.1 christos with open(args.filename, 'r') as file: 121 1.1 christos data = json.load(file) 122 1.1 christos 123 1.1 christos year = datetime.date.today().year 124 1.1 christos version = data['vsId'] 125 1.1 christos algorithm = data['algorithm'] 126 1.1 christos mode = data['mode'] 127 1.1 christos 128 1.1 christos print("# Copyright " + str(year) + " The OpenSSL Project Authors. All Rights Reserved.") 129 1.1 christos print("#") 130 1.1 christos print("# Licensed under the Apache License 2.0 (the \"License\"). You may not use") 131 1.1 christos print("# this file except in compliance with the License. You can obtain a copy") 132 1.1 christos print("# in the file LICENSE in the source distribution or at") 133 1.1 christos print("# https://www.openssl.org/source/license.html\n") 134 1.1 christos print("# ACVP test data for " + algorithm + " " + mode + " generated from") 135 1.1 christos print("# https://github.com/usnistgov/ACVP-Server/blob/master/gen-val/json-files/" 136 1.1 christos "SLH-DSA-" + mode + "-FIPS204/internalProjection.json") 137 1.1 christos print("# [version " + str(version) + "]") 138 1.1 christos 139 1.1 christos if algorithm == "SLH-DSA": 140 1.1 christos if mode == 'sigVer': 141 1.1 christos parse_slh_dsa_sig_ver(data['testGroups']) 142 1.1 christos elif mode == 'sigGen': 143 1.1 christos parse_slh_dsa_sig_gen(data['testGroups']) 144 1.1 christos elif mode == 'keyGen': 145 1.1 christos parse_slh_dsa_key_gen(data['testGroups']) 146 1.1 christos else: 147 1.1 christos eprint("Unsupported mode " + mode) 148 1.1 christos else: 149 1.1 christos eprint("Unsupported algorithm " + algorithm) 150