xref: /freebsd/crypto/openssl/test/mldsa_wycheproof_parse.py (revision e7be843b4a162e68651d3911f0357ed464915629)
1*e7be843bSPierre Pronchery#!/usr/bin/env python
2*e7be843bSPierre Pronchery# Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
3*e7be843bSPierre Pronchery#
4*e7be843bSPierre Pronchery# Licensed under the Apache License 2.0 (the "License").  You may not use
5*e7be843bSPierre Pronchery# this file except in compliance with the License.  You can obtain a copy
6*e7be843bSPierre Pronchery# in the file LICENSE in the source distribution or at
7*e7be843bSPierre Pronchery# https://www.openssl.org/source/license.html
8*e7be843bSPierre Pronchery
9*e7be843bSPierre Pronchery# A python program written to parse (version 1) of the WYCHEPROOF test vectors for
10*e7be843bSPierre Pronchery# ML_DSA. The 6 files that can be processed by this utility can be downloaded
11*e7be843bSPierre Pronchery# from
12*e7be843bSPierre Pronchery#  https://github.com/C2SP/wycheproof/blob/8e7fa6f87e6993d7b613cf48b46512a32df8084a/testvectors_v1/mldsa_*_standard_*_test.json")
13*e7be843bSPierre Pronchery# and output from this utility to
14*e7be843bSPierre Pronchery# test/recipes/30-test_evp_data/evppkey_ml_dsa_44_wycheproof_sign.txt
15*e7be843bSPierre Pronchery# test/recipes/30-test_evp_data/evppkey_ml_dsa_65_wycheproof_sign.txt
16*e7be843bSPierre Pronchery# test/recipes/30-test_evp_data/evppkey_ml_dsa_87_wycheproof_sign.txt
17*e7be843bSPierre Pronchery# test/recipes/30-test_evp_data/evppkey_ml_dsa_44_wycheproof_verify.txt
18*e7be843bSPierre Pronchery# test/recipes/30-test_evp_data/evppkey_ml_dsa_65_wycheproof_verify.txt
19*e7be843bSPierre Pronchery# test/recipes/30-test_evp_data/evppkey_ml_dsa_87_wycheproof_verify.txt
20*e7be843bSPierre Pronchery#
21*e7be843bSPierre Pronchery# e.g. python3 ./test/mldsa_wycheproof_parse.py -alg ML-DSA-44 ./wycheproof/testvectors_v1/mldsa_44_standard_sign_test.json > test/recipes/30-test_evp_data/evppkey_ml_dsa_44_wycheproof_sign.txt
22*e7be843bSPierre Pronchery
23*e7be843bSPierre Proncheryimport json
24*e7be843bSPierre Proncheryimport argparse
25*e7be843bSPierre Proncheryimport datetime
26*e7be843bSPierre Proncheryfrom _ast import Or
27*e7be843bSPierre Pronchery
28*e7be843bSPierre Proncherydef print_label(label, value):
29*e7be843bSPierre Pronchery    print(label + " = " + value)
30*e7be843bSPierre Pronchery
31*e7be843bSPierre Proncherydef print_hexlabel(label, tag, value):
32*e7be843bSPierre Pronchery    print(label + " = hex" + tag + ":" + value)
33*e7be843bSPierre Pronchery
34*e7be843bSPierre Proncherydef parse_ml_dsa_sig_gen(alg, groups):
35*e7be843bSPierre Pronchery    grpId = 1
36*e7be843bSPierre Pronchery    for grp in groups:
37*e7be843bSPierre Pronchery        keyOnly = False
38*e7be843bSPierre Pronchery        first = True
39*e7be843bSPierre Pronchery        name = alg.replace('-', '_')
40*e7be843bSPierre Pronchery        keyname = name + "_" + str(grpId)
41*e7be843bSPierre Pronchery        grpId += 1
42*e7be843bSPierre Pronchery
43*e7be843bSPierre Pronchery        for tst in grp['tests']:
44*e7be843bSPierre Pronchery            if first:
45*e7be843bSPierre Pronchery                first = False
46*e7be843bSPierre Pronchery                if 'flags' in tst:
47*e7be843bSPierre Pronchery                    if 'IncorrectPrivateKeyLength' in tst['flags'] or 'InvalidPrivateKey' in tst['flags']:
48*e7be843bSPierre Pronchery                        keyOnly = True
49*e7be843bSPierre Pronchery                if not keyOnly:
50*e7be843bSPierre Pronchery                    print("")
51*e7be843bSPierre Pronchery                    print_label("PrivateKeyRaw", keyname + ":" + alg + ":" + grp['privateKey'])
52*e7be843bSPierre Pronchery            testname = name + "_" + str(tst['tcId'])
53*e7be843bSPierre Pronchery            print("\n# " + str(tst['tcId']) + " " + tst['comment'])
54*e7be843bSPierre Pronchery
55*e7be843bSPierre Pronchery            print_label("FIPSversion", ">=3.5.0")
56*e7be843bSPierre Pronchery            if keyOnly:
57*e7be843bSPierre Pronchery                print_label("KeyFromData", alg)
58*e7be843bSPierre Pronchery                print_hexlabel("Ctrl", "priv", grp['privateKey'])
59*e7be843bSPierre Pronchery                print_label("Result", "KEY_FROMDATA_ERROR")
60*e7be843bSPierre Pronchery            else:
61*e7be843bSPierre Pronchery                print_label("Sign-Message", alg + ":" + keyname)
62*e7be843bSPierre Pronchery                print_label("Input", tst['msg'])
63*e7be843bSPierre Pronchery                print_label("Output", tst['sig'])
64*e7be843bSPierre Pronchery                if 'ctx' in tst:
65*e7be843bSPierre Pronchery                    print_hexlabel("Ctrl", "context-string", tst['ctx'])
66*e7be843bSPierre Pronchery                print_label("Ctrl", "message-encoding:1")
67*e7be843bSPierre Pronchery                print_label("Ctrl", "deterministic:1")
68*e7be843bSPierre Pronchery                if tst['result'] == "invalid":
69*e7be843bSPierre Pronchery                    print_label("Result", "PKEY_CTRL_ERROR")
70*e7be843bSPierre Pronchery
71*e7be843bSPierre Proncherydef parse_ml_dsa_sig_ver(alg, groups):
72*e7be843bSPierre Pronchery    grpId = 1
73*e7be843bSPierre Pronchery    for grp in groups:
74*e7be843bSPierre Pronchery        keyOnly = False
75*e7be843bSPierre Pronchery        first = True
76*e7be843bSPierre Pronchery        name = alg.replace('-', '_')
77*e7be843bSPierre Pronchery        keyname = name + "_" + str(grpId)
78*e7be843bSPierre Pronchery        grpId += 1
79*e7be843bSPierre Pronchery
80*e7be843bSPierre Pronchery        for tst in grp['tests']:
81*e7be843bSPierre Pronchery            if first:
82*e7be843bSPierre Pronchery                first = False
83*e7be843bSPierre Pronchery                if 'flags' in tst:
84*e7be843bSPierre Pronchery                    if 'IncorrectPublicKeyLength' in tst['flags'] or 'InvalidPublicKey' in tst['flags']:
85*e7be843bSPierre Pronchery                        keyOnly = True
86*e7be843bSPierre Pronchery                if not keyOnly:
87*e7be843bSPierre Pronchery                    print("")
88*e7be843bSPierre Pronchery                    print_label("PublicKeyRaw", keyname + ":" + alg + ":" + grp['publicKey'])
89*e7be843bSPierre Pronchery            testname = name + "_" + str(tst['tcId'])
90*e7be843bSPierre Pronchery            print("\n# " + str(tst['tcId']) + " " + tst['comment'])
91*e7be843bSPierre Pronchery
92*e7be843bSPierre Pronchery            print_label("FIPSversion", ">=3.5.0")
93*e7be843bSPierre Pronchery            if keyOnly:
94*e7be843bSPierre Pronchery                print_label("KeyFromData", alg)
95*e7be843bSPierre Pronchery                print_hexlabel("Ctrl", "pub", grp['publicKey'])
96*e7be843bSPierre Pronchery                print_label("Result", "KEY_FROMDATA_ERROR")
97*e7be843bSPierre Pronchery            else:
98*e7be843bSPierre Pronchery                print_label("Verify-Message-Public", alg + ":" + keyname)
99*e7be843bSPierre Pronchery                print_label("Input", tst['msg'])
100*e7be843bSPierre Pronchery                print_label("Output", tst['sig'])
101*e7be843bSPierre Pronchery                if 'ctx' in tst:
102*e7be843bSPierre Pronchery                    print_hexlabel("Ctrl", "context-string", tst['ctx'])
103*e7be843bSPierre Pronchery                print_label("Ctrl", "message-encoding:1")
104*e7be843bSPierre Pronchery                print_label("Ctrl", "deterministic:1")
105*e7be843bSPierre Pronchery                if tst['result'] == "invalid":
106*e7be843bSPierre Pronchery                    if 'InvalidContext' in tst['flags']:
107*e7be843bSPierre Pronchery                        print_label("Result", "PKEY_CTRL_ERROR")
108*e7be843bSPierre Pronchery                    else:
109*e7be843bSPierre Pronchery                        print_label("Result", "VERIFY_ERROR")
110*e7be843bSPierre Pronchery
111*e7be843bSPierre Proncheryparser = argparse.ArgumentParser(description="")
112*e7be843bSPierre Proncheryparser.add_argument('filename', type=str)
113*e7be843bSPierre Proncheryparser.add_argument('-alg', type=str)
114*e7be843bSPierre Proncheryargs = parser.parse_args()
115*e7be843bSPierre Pronchery
116*e7be843bSPierre Pronchery# Open and read the JSON file
117*e7be843bSPierre Proncherywith open(args.filename, 'r') as file:
118*e7be843bSPierre Pronchery    data = json.load(file)
119*e7be843bSPierre Pronchery
120*e7be843bSPierre Proncheryyear = datetime.date.today().year
121*e7be843bSPierre Proncheryversion = data['generatorVersion']
122*e7be843bSPierre Proncheryalgorithm = data['algorithm']
123*e7be843bSPierre Proncherymode = data['testGroups'][0]['type']
124*e7be843bSPierre Pronchery
125*e7be843bSPierre Proncheryprint("# Copyright " + str(year) + " The OpenSSL Project Authors. All Rights Reserved.")
126*e7be843bSPierre Proncheryprint("#")
127*e7be843bSPierre Proncheryprint("# Licensed under the Apache License 2.0 (the \"License\").  You may not use")
128*e7be843bSPierre Proncheryprint("# this file except in compliance with the License.  You can obtain a copy")
129*e7be843bSPierre Proncheryprint("# in the file LICENSE in the source distribution or at")
130*e7be843bSPierre Proncheryprint("# https://www.openssl.org/source/license.html\n")
131*e7be843bSPierre Proncheryprint("# Wycheproof test data for " + algorithm + " " + mode + " generated from")
132*e7be843bSPierre Proncheryprint("# https://github.com/C2SP/wycheproof/blob/8e7fa6f87e6993d7b613cf48b46512a32df8084a/testvectors_v1/mldsa_*_standard_*_test.json")
133*e7be843bSPierre Pronchery
134*e7be843bSPierre Proncheryprint("# [version " + str(version) + "]")
135*e7be843bSPierre Pronchery
136*e7be843bSPierre Proncheryif algorithm == "ML-DSA":
137*e7be843bSPierre Pronchery    if mode == 'MlDsaSign':
138*e7be843bSPierre Pronchery        parse_ml_dsa_sig_gen(args.alg, data['testGroups'])
139*e7be843bSPierre Pronchery    elif mode == 'MlDsaVerify':
140*e7be843bSPierre Pronchery        parse_ml_dsa_sig_ver(args.alg, data['testGroups'])
141*e7be843bSPierre Pronchery    else:
142*e7be843bSPierre Pronchery        print("Unsupported mode " + mode)
143*e7be843bSPierre Proncheryelse:
144*e7be843bSPierre Pronchery    print("Unsupported algorithm " + algorithm)
145