|
20 | 20 | #include "../../group.h" |
21 | 21 | #include "../../hash.h" |
22 | 22 | #include "../../util.h" |
| 23 | +#include "../../testutil.h" |
23 | 24 | #include "../../unit_test.h" |
24 | 25 |
|
25 | 26 | #include "vectors.h" |
@@ -608,6 +609,96 @@ static void sha256_tag_test(void) { |
608 | 609 | } |
609 | 610 | } |
610 | 611 |
|
| 612 | +DEFINE_SHA256_TRANSFORM_PROBE(sha256_musig_pubkey_agg) |
| 613 | +DEFINE_SHA256_TRANSFORM_PROBE(sha256_musig_nonce_gen) |
| 614 | +DEFINE_SHA256_TRANSFORM_PROBE(sha256_musig_nonce_gen_counter) |
| 615 | +DEFINE_SHA256_TRANSFORM_PROBE(sha256_musig_nonce_process) |
| 616 | +DEFINE_SHA256_TRANSFORM_PROBE(sha256_musig_partial_sign) |
| 617 | +DEFINE_SHA256_TRANSFORM_PROBE(sha256_musig_partial_sig_verify) |
| 618 | +/* Check that a ctx-provided SHA256 compression override takes effect in every musig |
| 619 | + * API function that uses the hash context. These functions share the same signing |
| 620 | + * session, so the setup is built only once with the default compression, then each |
| 621 | + * function is re-run with its own override enabled. */ |
| 622 | +static void musig_ctx_sha256(void) { |
| 623 | + secp256k1_context *ctx = secp256k1_context_clone(CTX); |
| 624 | + secp256k1_musig_keyagg_cache keyagg_cache, keyagg_cache_custom; |
| 625 | + secp256k1_musig_secnonce secnonce, secnonce_tmp, secnonce_unused; |
| 626 | + secp256k1_musig_pubnonce pubnonce, pubnonce_custom; |
| 627 | + secp256k1_musig_pubnonce pubnonce_counter, pubnonce_counter_custom; |
| 628 | + const secp256k1_musig_pubnonce *pubnonce_ptr[1]; |
| 629 | + secp256k1_musig_aggnonce aggnonce; |
| 630 | + secp256k1_musig_session session, session_custom; |
| 631 | + secp256k1_musig_partial_sig partial_sig, partial_sig_custom; |
| 632 | + const unsigned char sk[32] = {1}; |
| 633 | + const unsigned char msg32[32] = {1}; |
| 634 | + unsigned char secrand[32] = {1}; |
| 635 | + secp256k1_keypair keypair; |
| 636 | + secp256k1_pubkey pk; |
| 637 | + const secp256k1_pubkey *pks[1]; |
| 638 | + |
| 639 | + CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1); |
| 640 | + CHECK(secp256k1_keypair_pub(ctx, &pk, &keypair) == 1); |
| 641 | + pks[0] = &pk; |
| 642 | + pubnonce_ptr[0] = &pubnonce; |
| 643 | + |
| 644 | + /* Run a full signing session with the default compression. */ |
| 645 | + CHECK(secp256k1_musig_pubkey_agg(ctx, NULL, &keyagg_cache, pks, 1) == 1); |
| 646 | + CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce, &pubnonce, secrand, sk, &pk, NULL, NULL, NULL) == 1); |
| 647 | + CHECK(secp256k1_musig_nonce_agg(ctx, &aggnonce, pubnonce_ptr, 1) == 1); |
| 648 | + CHECK(secp256k1_musig_nonce_process(ctx, &session, &aggnonce, msg32, &keyagg_cache) == 1); |
| 649 | + /* partial_sign zeroes the secnonce to prevent reuse, so keep a copy for the override run */ |
| 650 | + memcpy(&secnonce_tmp, &secnonce, sizeof(secnonce_tmp)); |
| 651 | + CHECK(secp256k1_musig_partial_sign(ctx, &partial_sig, &secnonce, &keypair, &keyagg_cache, &session) == 1); |
| 652 | + CHECK(secp256k1_musig_partial_sig_verify(ctx, &partial_sig, &pubnonce, &pk, &keyagg_cache, &session) == 1); |
| 653 | + /* nonce_gen_counter is an alternative to nonce_gen and not part of the session above, |
| 654 | + * so run it separately with the default compression. */ |
| 655 | + CHECK(secp256k1_musig_nonce_gen_counter(ctx, &secnonce_unused, &pubnonce_counter, 0, &keypair, NULL, NULL, NULL) == 1); |
| 656 | + CHECK(!sha256_musig_pubkey_agg_called); |
| 657 | + CHECK(!sha256_musig_nonce_gen_called); |
| 658 | + CHECK(!sha256_musig_nonce_gen_counter_called); |
| 659 | + CHECK(!sha256_musig_nonce_process_called); |
| 660 | + CHECK(!sha256_musig_partial_sign_called); |
| 661 | + CHECK(!sha256_musig_partial_sig_verify_called); |
| 662 | + |
| 663 | + /* Re-run each function with its SHA256 compression override, set directly to bypass |
| 664 | + * the ctx setter sanity checks. Each override must change the output relative to the |
| 665 | + * default run above. The override runs reuse the default inputs from above, so each |
| 666 | + * function is exercised in isolation. */ |
| 667 | + ctx->hash_ctx.fn_sha256_compression = sha256_musig_pubkey_agg; |
| 668 | + CHECK(secp256k1_musig_pubkey_agg(ctx, NULL, &keyagg_cache_custom, pks, 1) == 1); |
| 669 | + CHECK(sha256_musig_pubkey_agg_called); |
| 670 | + CHECK(secp256k1_memcmp_var(keyagg_cache.data, keyagg_cache_custom.data, sizeof(keyagg_cache.data)) != 0); |
| 671 | + |
| 672 | + /* Restore secrand (nonce_gen zeroed it). */ |
| 673 | + secrand[0] = 1; |
| 674 | + ctx->hash_ctx.fn_sha256_compression = sha256_musig_nonce_gen; |
| 675 | + CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce_unused, &pubnonce_custom, secrand, sk, &pk, NULL, NULL, NULL) == 1); |
| 676 | + CHECK(sha256_musig_nonce_gen_called); |
| 677 | + CHECK(secp256k1_memcmp_var(pubnonce.data, pubnonce_custom.data, sizeof(pubnonce.data)) != 0); |
| 678 | + |
| 679 | + ctx->hash_ctx.fn_sha256_compression = sha256_musig_nonce_process; |
| 680 | + CHECK(secp256k1_musig_nonce_process(ctx, &session_custom, &aggnonce, msg32, &keyagg_cache) == 1); |
| 681 | + CHECK(sha256_musig_nonce_process_called); |
| 682 | + CHECK(secp256k1_memcmp_var(session.data, session_custom.data, sizeof(session.data)) != 0); |
| 683 | + |
| 684 | + ctx->hash_ctx.fn_sha256_compression = sha256_musig_partial_sign; |
| 685 | + CHECK(secp256k1_musig_partial_sign(ctx, &partial_sig_custom, &secnonce_tmp, &keypair, &keyagg_cache, &session) == 1); |
| 686 | + CHECK(sha256_musig_partial_sign_called); |
| 687 | + CHECK(secp256k1_memcmp_var(partial_sig.data, partial_sig_custom.data, sizeof(partial_sig.data)) != 0); |
| 688 | + |
| 689 | + /* The default partial sig no longer verifies once the override changes the KeyAgg coefficient. */ |
| 690 | + ctx->hash_ctx.fn_sha256_compression = sha256_musig_partial_sig_verify; |
| 691 | + CHECK(secp256k1_musig_partial_sig_verify(ctx, &partial_sig, &pubnonce, &pk, &keyagg_cache, &session) == 0); |
| 692 | + CHECK(sha256_musig_partial_sig_verify_called); |
| 693 | + |
| 694 | + ctx->hash_ctx.fn_sha256_compression = sha256_musig_nonce_gen_counter; |
| 695 | + CHECK(secp256k1_musig_nonce_gen_counter(ctx, &secnonce_unused, &pubnonce_counter_custom, 0, &keypair, NULL, NULL, NULL) == 1); |
| 696 | + CHECK(sha256_musig_nonce_gen_counter_called); |
| 697 | + CHECK(secp256k1_memcmp_var(pubnonce_counter.data, pubnonce_counter_custom.data, sizeof(pubnonce_counter.data)) != 0); |
| 698 | + |
| 699 | + secp256k1_context_destroy(ctx); |
| 700 | +} |
| 701 | + |
611 | 702 | /* Attempts to create a signature for the aggregate public key using given secret |
612 | 703 | * keys and keyagg_cache. */ |
613 | 704 | static void musig_tweak_test_helper(const secp256k1_xonly_pubkey* agg_pk, const unsigned char *sk0, const unsigned char *sk1, secp256k1_musig_keyagg_cache *keyagg_cache) { |
@@ -1172,6 +1263,7 @@ static const struct tf_test_entry tests_musig[] = { |
1172 | 1263 | CASE1(musig_nonce_test), |
1173 | 1264 | CASE1(musig_tweak_test), |
1174 | 1265 | CASE1(sha256_tag_test), |
| 1266 | + CASE1(musig_ctx_sha256), |
1175 | 1267 | CASE1(musig_test_vectors_keyagg), |
1176 | 1268 | CASE1(musig_test_vectors_noncegen), |
1177 | 1269 | CASE1(musig_test_vectors_nonceagg), |
|
0 commit comments