tests_wycheproof_generate.py (3539B)
1 #!/usr/bin/env python3 2 # Copyright (c) 2023 Random "Randy" Lattice and Sean Andersen 3 # Distributed under the MIT software license, see the accompanying 4 # file COPYING or https://www.opensource.org/licenses/mit-license.php. 5 ''' 6 Generate a C file with ECDSA testvectors from the Wycheproof project. 7 ''' 8 9 import json 10 import sys 11 12 filename_input = sys.argv[1] 13 14 with open(filename_input) as f: 15 doc = json.load(f) 16 17 num_groups = len(doc['testGroups']) 18 19 def to_c_array(x): 20 if x == "": 21 return "" 22 s = ',0x'.join(a+b for a,b in zip(x[::2], x[1::2])) 23 return "0x" + s 24 25 26 num_vectors = 0 27 offset_msg_running, offset_pk_running, offset_sig = 0, 0, 0 28 out = "" 29 messages = "" 30 signatures = "" 31 public_keys = "" 32 cache_msgs = {} 33 cache_public_keys = {} 34 35 for i in range(num_groups): 36 group = doc['testGroups'][i] 37 num_tests = len(group['tests']) 38 public_key = group['publicKey'] 39 for j in range(num_tests): 40 test_vector = group['tests'][j] 41 # // 2 to convert hex to byte length 42 sig_size = len(test_vector['sig']) // 2 43 msg_size = len(test_vector['msg']) // 2 44 45 if test_vector['result'] == "invalid": 46 expected_verify = 0 47 elif test_vector['result'] == "valid": 48 expected_verify = 1 49 else: 50 raise ValueError("invalid result field") 51 52 if num_vectors != 0 and sig_size != 0: 53 signatures += ",\n " 54 55 new_msg = False 56 msg = to_c_array(test_vector['msg']) 57 msg_offset = offset_msg_running 58 # check for repeated msg 59 if msg not in cache_msgs: 60 if num_vectors != 0 and msg_size != 0: 61 messages += ",\n " 62 cache_msgs[msg] = offset_msg_running 63 messages += msg 64 new_msg = True 65 else: 66 msg_offset = cache_msgs[msg] 67 68 new_pk = False 69 pk = to_c_array(public_key['uncompressed']) 70 pk_offset = offset_pk_running 71 # check for repeated pk 72 if pk not in cache_public_keys: 73 if num_vectors != 0: 74 public_keys += ",\n " 75 cache_public_keys[pk] = offset_pk_running 76 public_keys += pk 77 new_pk = True 78 else: 79 pk_offset = cache_public_keys[pk] 80 81 signatures += to_c_array(test_vector['sig']) 82 83 out += " /" + "* tcId: " + str(test_vector['tcId']) + ". " + test_vector['comment'] + " *" + "/\n" 84 out += f" {{{pk_offset}, {msg_offset}, {msg_size}, {offset_sig}, {sig_size}, {expected_verify} }},\n" 85 if new_msg: 86 offset_msg_running += msg_size 87 if new_pk: 88 offset_pk_running += 65 89 offset_sig += sig_size 90 num_vectors += 1 91 92 struct_definition = """ 93 typedef struct { 94 size_t pk_offset; 95 size_t msg_offset; 96 size_t msg_len; 97 size_t sig_offset; 98 size_t sig_len; 99 int expected_verify; 100 } wycheproof_ecdsa_testvector; 101 """ 102 103 104 print("/* Note: this file was autogenerated using tests_wycheproof_generate.py. Do not edit. */") 105 print(f"#define SECP256K1_ECDSA_WYCHEPROOF_NUMBER_TESTVECTORS ({num_vectors})") 106 107 print(struct_definition) 108 109 print("static const unsigned char wycheproof_ecdsa_messages[] = { " + messages + "};\n") 110 print("static const unsigned char wycheproof_ecdsa_public_keys[] = { " + public_keys + "};\n") 111 print("static const unsigned char wycheproof_ecdsa_signatures[] = { " + signatures + "};\n") 112 113 print("static const wycheproof_ecdsa_testvector testvectors[SECP256K1_ECDSA_WYCHEPROOF_NUMBER_TESTVECTORS] = {") 114 print(out) 115 print("};")