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)