Home | History | Annotate | Line # | Download | only in test
      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