github.com/ethereum/go-ethereum@v1.16.1/crypto/secp256k1/libsecp256k1/tools/test_vectors_musig2_generate.py (about)

     1  #!/usr/bin/env python3
     2  
     3  import sys
     4  import json
     5  import textwrap
     6  
     7  max_pubkeys = 0
     8  
     9  if len(sys.argv) < 2:
    10      print(
    11          "This script converts BIP MuSig2 test vectors in a given directory to a C file that can be used in the test framework."
    12      )
    13      print("Usage: %s <dir>" % sys.argv[0])
    14      sys.exit(1)
    15  
    16  
    17  def hexstr_to_intarray(str):
    18      return ", ".join([f"0x{b:02X}" for b in bytes.fromhex(str)])
    19  
    20  
    21  def create_init(name):
    22      return """
    23  static const struct musig_%s_vector musig_%s_vector = {
    24  """ % (
    25          name,
    26          name,
    27      )
    28  
    29  
    30  def init_array(key):
    31      return textwrap.indent("{ %s },\n" % hexstr_to_intarray(data[key]), 4 * " ")
    32  
    33  
    34  def init_arrays(key):
    35      s = textwrap.indent("{\n", 4 * " ")
    36      s += textwrap.indent(
    37          ",\n".join(["{ %s }" % hexstr_to_intarray(x) for x in data[key]]), 8 * " "
    38      )
    39      s += textwrap.indent("\n},\n", 4 * " ")
    40      return s
    41  
    42  
    43  def init_indices(array):
    44      return " %d, { %s }" % (
    45          len(array),
    46          ", ".join(map(str, array) if len(array) > 0 else "0"),
    47      )
    48  
    49  
    50  def init_is_xonly(case):
    51      if len(case["tweak_indices"]) > 0:
    52          return ", ".join(map(lambda x: "1" if x else "0", case["is_xonly"]))
    53      return "0"
    54  
    55  
    56  def init_optional_expected(case):
    57      return hexstr_to_intarray(case["expected"]) if "expected" in case else 0
    58  
    59  
    60  def init_cases(cases, f):
    61      s = textwrap.indent("{\n", 4 * " ")
    62      for (i, case) in enumerate(cases):
    63          s += textwrap.indent("%s\n" % f(case), 8 * " ")
    64      s += textwrap.indent("},\n", 4 * " ")
    65      return s
    66  
    67  
    68  def finish_init():
    69      return "};\n"
    70  
    71  
    72  s = (
    73      """/**
    74   * Automatically generated by %s.
    75   *
    76   * The test vectors for the KeySort function are included in this file. They can
    77   * be found in src/modules/extrakeys/tests_impl.h. */
    78  """
    79      % sys.argv[0]
    80  )
    81  
    82  
    83  s += """
    84  enum MUSIG_ERROR {
    85      MUSIG_PUBKEY,
    86      MUSIG_TWEAK,
    87      MUSIG_PUBNONCE,
    88      MUSIG_AGGNONCE,
    89      MUSIG_SECNONCE,
    90      MUSIG_SIG,
    91      MUSIG_SIG_VERIFY,
    92      MUSIG_OTHER
    93  };
    94  """
    95  
    96  # key agg vectors
    97  with open(sys.argv[1] + "/key_agg_vectors.json", "r") as f:
    98      data = json.load(f)
    99  
   100      max_key_indices = max(
   101          len(test_case["key_indices"]) for test_case in data["valid_test_cases"]
   102      )
   103      max_tweak_indices = max(
   104          len(test_case["tweak_indices"]) for test_case in data["error_test_cases"]
   105      )
   106      num_pubkeys = len(data["pubkeys"])
   107      max_pubkeys = max(num_pubkeys, max_pubkeys)
   108      num_tweaks = len(data["tweaks"])
   109      num_valid_cases = len(data["valid_test_cases"])
   110      num_error_cases = len(data["error_test_cases"])
   111  
   112      # Add structures for valid and error cases
   113      s += (
   114          """
   115  struct musig_key_agg_valid_test_case {
   116      size_t key_indices_len;
   117      size_t key_indices[%d];
   118      unsigned char expected[32];
   119  };
   120  """
   121          % max_key_indices
   122      )
   123      s += """
   124  struct musig_key_agg_error_test_case {
   125      size_t key_indices_len;
   126      size_t key_indices[%d];
   127      size_t tweak_indices_len;
   128      size_t tweak_indices[%d];
   129      int is_xonly[%d];
   130      enum MUSIG_ERROR error;
   131  };
   132  """ % (
   133          max_key_indices,
   134          max_tweak_indices,
   135          max_tweak_indices,
   136      )
   137  
   138      # Add structure for entire vector
   139      s += """
   140  struct musig_key_agg_vector {
   141      unsigned char pubkeys[%d][33];
   142      unsigned char tweaks[%d][32];
   143      struct musig_key_agg_valid_test_case valid_case[%d];
   144      struct musig_key_agg_error_test_case error_case[%d];
   145  };
   146  """ % (
   147          num_pubkeys,
   148          num_tweaks,
   149          num_valid_cases,
   150          num_error_cases,
   151      )
   152  
   153      s += create_init("key_agg")
   154      # Add pubkeys and tweaks to the vector
   155      s += init_arrays("pubkeys")
   156      s += init_arrays("tweaks")
   157  
   158      # Add valid cases to the vector
   159      s += init_cases(
   160          data["valid_test_cases"],
   161          lambda case: "{ %s, { %s }},"
   162          % (init_indices(case["key_indices"]), hexstr_to_intarray(case["expected"])),
   163      )
   164  
   165      def comment_to_error(case):
   166          comment = case["comment"]
   167          if "public key" in comment.lower():
   168              return "MUSIG_PUBKEY"
   169          elif "tweak" in comment.lower():
   170              return "MUSIG_TWEAK"
   171          else:
   172              sys.exit("Unknown error")
   173  
   174      # Add error cases to the vector
   175      s += init_cases(
   176          data["error_test_cases"],
   177          lambda case: "{ %s, %s, { %s }, %s },"
   178          % (
   179              init_indices(case["key_indices"]),
   180              init_indices(case["tweak_indices"]),
   181              init_is_xonly(case),
   182              comment_to_error(case),
   183          ),
   184      )
   185  
   186      s += finish_init()
   187  
   188  # nonce gen vectors
   189  with open(sys.argv[1] + "/nonce_gen_vectors.json", "r") as f:
   190      data = json.load(f)
   191  
   192      # The MuSig2 implementation only allows messages of length 32
   193      data["test_cases"] = list(
   194          filter(lambda c: c["msg"] is None or len(c["msg"]) == 64, data["test_cases"])
   195      )
   196  
   197      num_tests = len(data["test_cases"])
   198  
   199      s += """
   200  struct musig_nonce_gen_test_case {
   201      unsigned char rand_[32];
   202      int has_sk;
   203      unsigned char sk[32];
   204      unsigned char pk[33];
   205      int has_aggpk;
   206      unsigned char aggpk[32];
   207      int has_msg;
   208      unsigned char msg[32];
   209      int has_extra_in;
   210      unsigned char extra_in[32];
   211      unsigned char expected_secnonce[97];
   212      unsigned char expected_pubnonce[66];
   213  };
   214  """
   215  
   216      s += (
   217          """
   218  struct musig_nonce_gen_vector {
   219      struct musig_nonce_gen_test_case test_case[%d];
   220  };
   221  """
   222          % num_tests
   223      )
   224  
   225      s += create_init("nonce_gen")
   226  
   227      def init_array_maybe(array):
   228          return "%d , { %s }" % (
   229              0 if array is None else 1,
   230              hexstr_to_intarray(array) if array is not None else 0,
   231          )
   232  
   233      s += init_cases(
   234          data["test_cases"],
   235          lambda case: "{ { %s },  %s, { %s }, %s, %s, %s, { %s }, { %s } },"
   236          % (
   237              hexstr_to_intarray(case["rand_"]),
   238              init_array_maybe(case["sk"]),
   239              hexstr_to_intarray(case["pk"]),
   240              init_array_maybe(case["aggpk"]),
   241              init_array_maybe(case["msg"]),
   242              init_array_maybe(case["extra_in"]),
   243              hexstr_to_intarray(case["expected_secnonce"]),
   244              hexstr_to_intarray(case["expected_pubnonce"]),
   245          ),
   246      )
   247  
   248      s += finish_init()
   249  
   250  # nonce agg vectors
   251  with open(sys.argv[1] + "/nonce_agg_vectors.json", "r") as f:
   252      data = json.load(f)
   253  
   254      num_pnonces = len(data["pnonces"])
   255      num_valid_cases = len(data["valid_test_cases"])
   256      num_error_cases = len(data["error_test_cases"])
   257  
   258      pnonce_indices_len = 2
   259      for case in data["valid_test_cases"] + data["error_test_cases"]:
   260          assert len(case["pnonce_indices"]) == pnonce_indices_len
   261  
   262      # Add structures for valid and error cases
   263      s += """
   264  struct musig_nonce_agg_test_case {
   265      size_t pnonce_indices[2];
   266      /* if valid case */
   267      unsigned char expected[66];
   268      /* if error case */
   269      int invalid_nonce_idx;
   270  };
   271  """
   272      # Add structure for entire vector
   273      s += """
   274  struct musig_nonce_agg_vector {
   275      unsigned char pnonces[%d][66];
   276      struct musig_nonce_agg_test_case valid_case[%d];
   277      struct musig_nonce_agg_test_case error_case[%d];
   278  };
   279  """ % (
   280          num_pnonces,
   281          num_valid_cases,
   282          num_error_cases,
   283      )
   284  
   285      s += create_init("nonce_agg")
   286      s += init_arrays("pnonces")
   287  
   288      for cases in (data["valid_test_cases"], data["error_test_cases"]):
   289          s += init_cases(
   290              cases,
   291              lambda case: "{ { %s }, { %s }, %d },"
   292              % (
   293                  ", ".join(map(str, case["pnonce_indices"])),
   294                  init_optional_expected(case),
   295                  case["error"]["signer"] if "error" in case else 0,
   296              ),
   297          )
   298      s += finish_init()
   299  
   300  # sign/verify vectors
   301  with open(sys.argv[1] + "/sign_verify_vectors.json", "r") as f:
   302      data = json.load(f)
   303  
   304      # The MuSig2 implementation only allows messages of length 32
   305      assert list(filter(lambda x: len(x) == 64, data["msgs"]))[0] == data["msgs"][0]
   306      data["msgs"] = [data["msgs"][0]]
   307  
   308      def filter_msg32(k):
   309          return list(filter(lambda x: x["msg_index"] == 0, data[k]))
   310  
   311      data["valid_test_cases"] = filter_msg32("valid_test_cases")
   312      data["sign_error_test_cases"] = filter_msg32("sign_error_test_cases")
   313      data["verify_error_test_cases"] = filter_msg32("verify_error_test_cases")
   314      data["verify_fail_test_cases"] = filter_msg32("verify_fail_test_cases")
   315  
   316      num_pubkeys = len(data["pubkeys"])
   317      max_pubkeys = max(num_pubkeys, max_pubkeys)
   318      num_secnonces = len(data["secnonces"])
   319      num_pubnonces = len(data["pnonces"])
   320      num_aggnonces = len(data["aggnonces"])
   321      num_msgs = len(data["msgs"])
   322      num_valid_cases = len(data["valid_test_cases"])
   323      num_sign_error_cases = len(data["sign_error_test_cases"])
   324      num_verify_fail_cases = len(data["verify_fail_test_cases"])
   325      num_verify_error_cases = len(data["verify_error_test_cases"])
   326  
   327      all_cases = (
   328          data["valid_test_cases"]
   329          + data["sign_error_test_cases"]
   330          + data["verify_error_test_cases"]
   331          + data["verify_fail_test_cases"]
   332      )
   333      max_key_indices = max(len(test_case["key_indices"]) for test_case in all_cases)
   334      max_nonce_indices = max(
   335          len(test_case["nonce_indices"]) if "nonce_indices" in test_case else 0
   336          for test_case in all_cases
   337      )
   338      # Add structures for valid and error cases
   339      s += (
   340          """
   341  /* Omit pubnonces in the test vectors because our partial signature verification
   342   * implementation is able to accept the aggnonce directly. */
   343  struct musig_valid_case {
   344      size_t key_indices_len;
   345      size_t key_indices[%d];
   346      size_t aggnonce_index;
   347      size_t msg_index;
   348      size_t signer_index;
   349      unsigned char expected[32];
   350  };
   351  """
   352          % max_key_indices
   353      )
   354  
   355      s += (
   356          """
   357  struct musig_sign_error_case {
   358      size_t key_indices_len;
   359      size_t key_indices[%d];
   360      size_t aggnonce_index;
   361      size_t msg_index;
   362      size_t secnonce_index;
   363      enum MUSIG_ERROR error;
   364  };
   365  """
   366          % max_key_indices
   367      )
   368  
   369      s += """
   370  struct musig_verify_fail_error_case {
   371      unsigned char sig[32];
   372      size_t key_indices_len;
   373      size_t key_indices[%d];
   374      size_t nonce_indices_len;
   375      size_t nonce_indices[%d];
   376      size_t msg_index;
   377      size_t signer_index;
   378      enum MUSIG_ERROR error;
   379  };
   380  """ % (
   381          max_key_indices,
   382          max_nonce_indices,
   383      )
   384  
   385      # Add structure for entire vector
   386      s += """
   387  struct musig_sign_verify_vector {
   388      unsigned char sk[32];
   389      unsigned char pubkeys[%d][33];
   390      unsigned char secnonces[%d][194];
   391      unsigned char pubnonces[%d][194];
   392      unsigned char aggnonces[%d][66];
   393      unsigned char msgs[%d][32];
   394      struct musig_valid_case valid_case[%d];
   395      struct musig_sign_error_case sign_error_case[%d];
   396      struct musig_verify_fail_error_case verify_fail_case[%d];
   397      struct musig_verify_fail_error_case verify_error_case[%d];
   398  };
   399  """ % (
   400          num_pubkeys,
   401          num_secnonces,
   402          num_pubnonces,
   403          num_aggnonces,
   404          num_msgs,
   405          num_valid_cases,
   406          num_sign_error_cases,
   407          num_verify_fail_cases,
   408          num_verify_error_cases,
   409      )
   410  
   411      s += create_init("sign_verify")
   412      s += init_array("sk")
   413      s += init_arrays("pubkeys")
   414      s += init_arrays("secnonces")
   415      s += init_arrays("pnonces")
   416      s += init_arrays("aggnonces")
   417      s += init_arrays("msgs")
   418  
   419      s += init_cases(
   420          data["valid_test_cases"],
   421          lambda case: "{ %s, %d, %d, %d, { %s }},"
   422          % (
   423              init_indices(case["key_indices"]),
   424              case["aggnonce_index"],
   425              case["msg_index"],
   426              case["signer_index"],
   427              init_optional_expected(case),
   428          ),
   429      )
   430  
   431      def sign_error(case):
   432          comment = case["comment"]
   433          if "pubkey" in comment or "public key" in comment:
   434              return "MUSIG_PUBKEY"
   435          elif "Aggregate nonce" in comment:
   436              return "MUSIG_AGGNONCE"
   437          elif "Secnonce" in comment:
   438              return "MUSIG_SECNONCE"
   439          else:
   440              sys.exit("Unknown sign error")
   441  
   442      s += init_cases(
   443          data["sign_error_test_cases"],
   444          lambda case: "{ %s, %d, %d, %d, %s },"
   445          % (
   446              init_indices(case["key_indices"]),
   447              case["aggnonce_index"],
   448              case["msg_index"],
   449              case["secnonce_index"],
   450              sign_error(case),
   451          ),
   452      )
   453  
   454      def verify_error(case):
   455          comment = case["comment"]
   456          if "exceeds" in comment:
   457              return "MUSIG_SIG"
   458          elif "Wrong signer" in comment or "Wrong signature" in comment:
   459              return "MUSIG_SIG_VERIFY"
   460          elif "pubnonce" in comment:
   461              return "MUSIG_PUBNONCE"
   462          elif "pubkey" in comment:
   463              return "MUSIG_PUBKEY"
   464          else:
   465              sys.exit("Unknown verify error")
   466  
   467      for cases in ("verify_fail_test_cases", "verify_error_test_cases"):
   468          s += init_cases(
   469              data[cases],
   470              lambda case: "{ { %s }, %s, %s, %d, %d, %s },"
   471              % (
   472                  hexstr_to_intarray(case["sig"]),
   473                  init_indices(case["key_indices"]),
   474                  init_indices(case["nonce_indices"]),
   475                  case["msg_index"],
   476                  case["signer_index"],
   477                  verify_error(case),
   478              ),
   479          )
   480  
   481      s += finish_init()
   482  
   483  # tweak vectors
   484  with open(sys.argv[1] + "/tweak_vectors.json", "r") as f:
   485      data = json.load(f)
   486  
   487      num_pubkeys = len(data["pubkeys"])
   488      max_pubkeys = max(num_pubkeys, max_pubkeys)
   489      num_pubnonces = len(data["pnonces"])
   490      num_tweaks = len(data["tweaks"])
   491      num_valid_cases = len(data["valid_test_cases"])
   492      num_error_cases = len(data["error_test_cases"])
   493  
   494      all_cases = data["valid_test_cases"] + data["error_test_cases"]
   495      max_key_indices = max(len(test_case["key_indices"]) for test_case in all_cases)
   496      max_tweak_indices = max(len(test_case["tweak_indices"]) for test_case in all_cases)
   497      max_nonce_indices = max(len(test_case["nonce_indices"]) for test_case in all_cases)
   498      # Add structures for valid and error cases
   499      s += """
   500  struct musig_tweak_case {
   501      size_t key_indices_len;
   502      size_t key_indices[%d];
   503      size_t nonce_indices_len;
   504      size_t nonce_indices[%d];
   505      size_t tweak_indices_len;
   506      size_t tweak_indices[%d];
   507      int is_xonly[%d];
   508      size_t signer_index;
   509      unsigned char expected[32];
   510  };
   511  """ % (
   512          max_key_indices,
   513          max_nonce_indices,
   514          max_tweak_indices,
   515          max_tweak_indices,
   516      )
   517  
   518      # Add structure for entire vector
   519      s += """
   520  struct musig_tweak_vector {
   521      unsigned char sk[32];
   522      unsigned char secnonce[97];
   523      unsigned char aggnonce[66];
   524      unsigned char msg[32];
   525      unsigned char pubkeys[%d][33];
   526      unsigned char pubnonces[%d][194];
   527      unsigned char tweaks[%d][32];
   528      struct musig_tweak_case valid_case[%d];
   529      struct musig_tweak_case error_case[%d];
   530  };
   531  """ % (
   532          num_pubkeys,
   533          num_pubnonces,
   534          num_tweaks,
   535          num_valid_cases,
   536          num_error_cases,
   537      )
   538      s += create_init("tweak")
   539      s += init_array("sk")
   540      s += init_array("secnonce")
   541      s += init_array("aggnonce")
   542      s += init_array("msg")
   543      s += init_arrays("pubkeys")
   544      s += init_arrays("pnonces")
   545      s += init_arrays("tweaks")
   546  
   547      s += init_cases(
   548          data["valid_test_cases"],
   549          lambda case: "{ %s, %s, %s, { %s }, %d, { %s }},"
   550          % (
   551              init_indices(case["key_indices"]),
   552              init_indices(case["nonce_indices"]),
   553              init_indices(case["tweak_indices"]),
   554              init_is_xonly(case),
   555              case["signer_index"],
   556              init_optional_expected(case),
   557          ),
   558      )
   559  
   560      s += init_cases(
   561          data["error_test_cases"],
   562          lambda case: "{ %s, %s, %s, { %s }, %d, { %s }},"
   563          % (
   564              init_indices(case["key_indices"]),
   565              init_indices(case["nonce_indices"]),
   566              init_indices(case["tweak_indices"]),
   567              init_is_xonly(case),
   568              case["signer_index"],
   569              init_optional_expected(case),
   570          ),
   571      )
   572  
   573      s += finish_init()
   574  
   575  # sigagg vectors
   576  with open(sys.argv[1] + "/sig_agg_vectors.json", "r") as f:
   577      data = json.load(f)
   578  
   579      num_pubkeys = len(data["pubkeys"])
   580      max_pubkeys = max(num_pubkeys, max_pubkeys)
   581      num_tweaks = len(data["tweaks"])
   582      num_psigs = len(data["psigs"])
   583      num_valid_cases = len(data["valid_test_cases"])
   584      num_error_cases = len(data["error_test_cases"])
   585  
   586      all_cases = data["valid_test_cases"] + data["error_test_cases"]
   587      max_key_indices = max(len(test_case["key_indices"]) for test_case in all_cases)
   588      max_tweak_indices = max(len(test_case["tweak_indices"]) for test_case in all_cases)
   589      max_psig_indices = max(len(test_case["psig_indices"]) for test_case in all_cases)
   590  
   591      # Add structures for valid and error cases
   592      s += """
   593  /* Omit pubnonces in the test vectors because they're only needed for
   594   * implementations that do not directly accept an aggnonce. */
   595  struct musig_sig_agg_case {
   596      size_t key_indices_len;
   597      size_t key_indices[%d];
   598      size_t tweak_indices_len;
   599      size_t tweak_indices[%d];
   600      int is_xonly[%d];
   601      unsigned char aggnonce[66];
   602      size_t psig_indices_len;
   603      size_t psig_indices[%d];
   604      /* if valid case */
   605      unsigned char expected[64];
   606      /* if error case */
   607      int invalid_sig_idx;
   608  };
   609  """ % (
   610          max_key_indices,
   611          max_tweak_indices,
   612          max_tweak_indices,
   613          max_psig_indices,
   614      )
   615  
   616      # Add structure for entire vector
   617      s += """
   618  struct musig_sig_agg_vector {
   619      unsigned char pubkeys[%d][33];
   620      unsigned char tweaks[%d][32];
   621      unsigned char psigs[%d][32];
   622      unsigned char msg[32];
   623      struct musig_sig_agg_case valid_case[%d];
   624      struct musig_sig_agg_case error_case[%d];
   625  };
   626  """ % (
   627          num_pubkeys,
   628          num_tweaks,
   629          num_psigs,
   630          num_valid_cases,
   631          num_error_cases,
   632      )
   633  
   634      s += create_init("sig_agg")
   635      s += init_arrays("pubkeys")
   636      s += init_arrays("tweaks")
   637      s += init_arrays("psigs")
   638      s += init_array("msg")
   639  
   640      for cases in (data["valid_test_cases"], data["error_test_cases"]):
   641          s += init_cases(
   642              cases,
   643              lambda case: "{ %s, %s, { %s }, { %s }, %s, { %s }, %d },"
   644              % (
   645                  init_indices(case["key_indices"]),
   646                  init_indices(case["tweak_indices"]),
   647                  init_is_xonly(case),
   648                  hexstr_to_intarray(case["aggnonce"]),
   649                  init_indices(case["psig_indices"]),
   650                  init_optional_expected(case),
   651                  case["error"]["signer"] if "error" in case else 0,
   652              ),
   653          )
   654      s += finish_init()
   655  s += "enum { MUSIG_VECTORS_MAX_PUBKEYS = %d };" % max_pubkeys
   656  print(s)