|
96 | 96 | assert_equal,
|
97 | 97 | random_bytes,
|
98 | 98 | )
|
99 |
| -from test_framework.key import generate_privkey, compute_xonly_pubkey, sign_schnorr, tweak_add_privkey, ECKey |
| 99 | +from test_framework.key import ( |
| 100 | + generate_privkey, |
| 101 | + compute_xonly_pubkey, |
| 102 | + sign_schnorr, |
| 103 | + tweak_add_privkey, |
| 104 | + ECKey, |
| 105 | + SECP256K1 |
| 106 | +) |
100 | 107 | from test_framework.address import (
|
101 | 108 | hash160,
|
102 | 109 | program_to_witness,
|
@@ -661,6 +668,44 @@ def spenders_taproot_active():
|
661 | 668 | # Test with signature with bit flipped.
|
662 | 669 | add_spender(spenders, "sig/bitflip", tap=tap, key=secs[0], failure={"signature": bitflipper(default_signature)}, **ERR_SIG_SCHNORR)
|
663 | 670 |
|
| 671 | + # == Test involving an internal public key not on the curve == |
| 672 | + |
| 673 | + # X-only public keys are 32 bytes, but not every 32-byte array is a valid public key; only |
| 674 | + # around 50% of them are. This does not affect users using correct software; these "keys" have |
| 675 | + # no corresponding private key, and thus will never appear as output of key |
| 676 | + # generation/derivation/tweaking. |
| 677 | + # |
| 678 | + # Using an invalid public key as P2TR output key makes the UTXO unspendable. Revealing an |
| 679 | + # invalid public key as internal key in a P2TR script path spend also makes the spend invalid. |
| 680 | + # These conditions are explicitly spelled out in BIP341. |
| 681 | + # |
| 682 | + # It is however hard to create test vectors for this, because it involves "guessing" how a |
| 683 | + # hypothetical incorrect implementation deals with an obviously-invalid condition, and making |
| 684 | + # sure that guessed behavior (accepting it in certain condition) doesn't occur. |
| 685 | + # |
| 686 | + # The test case added here tries to detect a very specific bug a verifier could have: if they |
| 687 | + # don't verify whether or not a revealed internal public key in a script path spend is valid, |
| 688 | + # and (correctly) implement output_key == tweak(internal_key, tweakval) but (incorrectly) treat |
| 689 | + # tweak(invalid_key, tweakval) as equal the public key corresponding to private key tweakval. |
| 690 | + # This may seem like a far-fetched edge condition to test for, but in fact, the BIP341 wallet |
| 691 | + # pseudocode did exactly that (but obviously only triggerable by someone invoking the tweaking |
| 692 | + # function with an invalid public key, which shouldn't happen). |
| 693 | + |
| 694 | + # Generate an invalid public key |
| 695 | + while True: |
| 696 | + invalid_pub = random_bytes(32) |
| 697 | + if not SECP256K1.is_x_coord(int.from_bytes(invalid_pub, 'big')): |
| 698 | + break |
| 699 | + |
| 700 | + # Implement a test case that detects validation logic which maps invalid public keys to the |
| 701 | + # point at infinity in the tweaking logic. |
| 702 | + tap = taproot_construct(invalid_pub, [("true", CScript([OP_1]))], treat_internal_as_infinity=True) |
| 703 | + add_spender(spenders, "output/invalid_x", tap=tap, key_tweaked=tap.tweak, failure={"leaf": "true", "inputs": []}, **ERR_WITNESS_PROGRAM_MISMATCH) |
| 704 | + |
| 705 | + # Do the same thing without invalid point, to make sure there is no mistake in the test logic. |
| 706 | + tap = taproot_construct(pubs[0], [("true", CScript([OP_1]))]) |
| 707 | + add_spender(spenders, "output/invalid_x_mock", tap=tap, key=secs[0], leaf="true", inputs=[]) |
| 708 | + |
664 | 709 | # == Tests for signature hashing ==
|
665 | 710 |
|
666 | 711 | # Run all tests once with no annex, and once with a valid random annex.
|
|
0 commit comments