tests_wycheproof_generate_ecdsa.py
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 from wycheproof_utils import to_c_array 13 14 filename_input = sys.argv[1] 15 16 with open(filename_input) as f: 17 doc = json.load(f) 18 19 num_groups = len(doc['testGroups']) 20 21 22 num_vectors = 0 23 offset_msg_running, offset_pk_running, offset_sig = 0, 0, 0 24 out = "" 25 messages = "" 26 signatures = "" 27 public_keys = "" 28 cache_msgs = {} 29 cache_public_keys = {} 30 31 for i in range(num_groups): 32 group = doc['testGroups'][i] 33 num_tests = len(group['tests']) 34 public_key = group['publicKey'] 35 for j in range(num_tests): 36 test_vector = group['tests'][j] 37 # // 2 to convert hex to byte length 38 sig_size = len(test_vector['sig']) // 2 39 msg_size = len(test_vector['msg']) // 2 40 41 if test_vector['result'] == "invalid": 42 expected_verify = 0 43 elif test_vector['result'] == "valid": 44 expected_verify = 1 45 else: 46 raise ValueError("invalid result field") 47 48 if num_vectors != 0 and sig_size != 0: 49 signatures += ",\n " 50 51 new_msg = False 52 msg = to_c_array(test_vector['msg']) 53 msg_offset = offset_msg_running 54 # check for repeated msg 55 if msg not in cache_msgs: 56 if num_vectors != 0 and msg_size != 0: 57 messages += ",\n " 58 cache_msgs[msg] = offset_msg_running 59 messages += msg 60 new_msg = True 61 else: 62 msg_offset = cache_msgs[msg] 63 64 new_pk = False 65 pk = to_c_array(public_key['uncompressed']) 66 pk_offset = offset_pk_running 67 # check for repeated pk 68 if pk not in cache_public_keys: 69 if num_vectors != 0: 70 public_keys += ",\n " 71 cache_public_keys[pk] = offset_pk_running 72 public_keys += pk 73 new_pk = True 74 else: 75 pk_offset = cache_public_keys[pk] 76 77 signatures += to_c_array(test_vector['sig']) 78 79 out += " /" + "* tcId: " + str(test_vector['tcId']) + ". " + test_vector['comment'] + " *" + "/\n" 80 out += f" {{{pk_offset}, {msg_offset}, {msg_size}, {offset_sig}, {sig_size}, {expected_verify} }},\n" 81 if new_msg: 82 offset_msg_running += msg_size 83 if new_pk: 84 offset_pk_running += 65 85 offset_sig += sig_size 86 num_vectors += 1 87 88 struct_definition = """ 89 typedef struct { 90 size_t pk_offset; 91 size_t msg_offset; 92 size_t msg_len; 93 size_t sig_offset; 94 size_t sig_len; 95 int expected_verify; 96 } wycheproof_ecdsa_testvector; 97 """ 98 99 100 print("/* Note: this file was autogenerated using tests_wycheproof_generate_ecdsa.py. Do not edit. */") 101 print(f"#define SECP256K1_ECDSA_WYCHEPROOF_NUMBER_TESTVECTORS ({num_vectors})") 102 103 print(struct_definition) 104 105 print("static const unsigned char wycheproof_ecdsa_messages[] = { " + messages + "};\n") 106 print("static const unsigned char wycheproof_ecdsa_public_keys[] = { " + public_keys + "};\n") 107 print("static const unsigned char wycheproof_ecdsa_signatures[] = { " + signatures + "};\n") 108 109 print("static const wycheproof_ecdsa_testvector testvectors[SECP256K1_ECDSA_WYCHEPROOF_NUMBER_TESTVECTORS] = {") 110 print(out) 111 print("};")