tests_wycheproof_generate_ecdsa.py raw

   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("};")
 112