mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-22 06:18:07 +00:00
feat(core): Support Taproot in SignatureVerifier.
This commit is contained in:
parent
a17cdb7cfa
commit
84e31310a1
1
core/.changelog.d/1656.added.2
Normal file
1
core/.changelog.d/1656.added.2
Normal file
@ -0,0 +1 @@
|
|||||||
|
Support replacement transactions with Taproot inputs in Bitcoin.
|
1
core/.changelog.d/1656.added.3
Normal file
1
core/.changelog.d/1656.added.3
Normal file
@ -0,0 +1 @@
|
|||||||
|
Support pre-signed external Taproot inputs in Bitcoin.
|
@ -374,7 +374,7 @@ class Bitcoin:
|
|||||||
verifier = SignatureVerifier(
|
verifier = SignatureVerifier(
|
||||||
script_pubkey, txi.script_sig, txi.witness, self.coin
|
script_pubkey, txi.script_sig, txi.witness, self.coin
|
||||||
)
|
)
|
||||||
verifier.ensure_hash_type(SIGHASH_ALL)
|
verifier.ensure_hash_type((SIGHASH_ALL_TAPROOT, self.get_hash_type(txi)))
|
||||||
tx_digest = await self.get_tx_digest(
|
tx_digest = await self.get_tx_digest(
|
||||||
orig.verification_index,
|
orig.verification_index,
|
||||||
txi,
|
txi,
|
||||||
@ -439,7 +439,7 @@ class Bitcoin:
|
|||||||
script_pubkey, txi.script_sig, txi.witness, self.coin
|
script_pubkey, txi.script_sig, txi.witness, self.coin
|
||||||
)
|
)
|
||||||
|
|
||||||
verifier.ensure_hash_type(self.get_hash_type(txi))
|
verifier.ensure_hash_type((SIGHASH_ALL_TAPROOT, self.get_hash_type(txi)))
|
||||||
|
|
||||||
tx_digest = await self.get_tx_digest(
|
tx_digest = await self.get_tx_digest(
|
||||||
i,
|
i,
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
from trezor import utils, wire
|
from trezor import utils, wire
|
||||||
from trezor.crypto import der
|
from trezor.crypto import der
|
||||||
from trezor.crypto.curve import secp256k1
|
from trezor.crypto.curve import bip340, secp256k1
|
||||||
from trezor.crypto.hashlib import sha256
|
from trezor.crypto.hashlib import sha256
|
||||||
|
|
||||||
from .common import ecdsa_hash_pubkey
|
from .common import OP_0, OP_1, ecdsa_hash_pubkey
|
||||||
from .scripts import (
|
from .scripts import (
|
||||||
output_script_native_segwit,
|
output_script_native_segwit,
|
||||||
output_script_p2pkh,
|
output_script_p2pkh,
|
||||||
@ -11,13 +11,16 @@ from .scripts import (
|
|||||||
parse_input_script_multisig,
|
parse_input_script_multisig,
|
||||||
parse_input_script_p2pkh,
|
parse_input_script_p2pkh,
|
||||||
parse_output_script_multisig,
|
parse_output_script_multisig,
|
||||||
|
parse_output_script_p2tr,
|
||||||
parse_witness_multisig,
|
parse_witness_multisig,
|
||||||
|
parse_witness_p2tr,
|
||||||
parse_witness_p2wpkh,
|
parse_witness_p2wpkh,
|
||||||
write_input_script_p2wpkh_in_p2sh,
|
write_input_script_p2wpkh_in_p2sh,
|
||||||
write_input_script_p2wsh_in_p2sh,
|
write_input_script_p2wsh_in_p2sh,
|
||||||
)
|
)
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
|
from typing import Sequence
|
||||||
from apps.common.coininfo import CoinInfo
|
from apps.common.coininfo import CoinInfo
|
||||||
|
|
||||||
|
|
||||||
@ -32,6 +35,7 @@ class SignatureVerifier:
|
|||||||
self.threshold = 1
|
self.threshold = 1
|
||||||
self.public_keys: list[memoryview] = []
|
self.public_keys: list[memoryview] = []
|
||||||
self.signatures: list[tuple[memoryview, int]] = []
|
self.signatures: list[tuple[memoryview, int]] = []
|
||||||
|
self.is_taproot = False
|
||||||
|
|
||||||
if not script_sig:
|
if not script_sig:
|
||||||
if not witness:
|
if not witness:
|
||||||
@ -44,12 +48,16 @@ class SignatureVerifier:
|
|||||||
raise wire.DataError("Invalid public key hash")
|
raise wire.DataError("Invalid public key hash")
|
||||||
self.public_keys = [public_key]
|
self.public_keys = [public_key]
|
||||||
self.signatures = [(signature, hash_type)]
|
self.signatures = [(signature, hash_type)]
|
||||||
elif len(script_pubkey) == 34: # P2WSH
|
elif len(script_pubkey) == 34 and script_pubkey[0] == OP_0: # P2WSH
|
||||||
script, self.signatures = parse_witness_multisig(witness)
|
script, self.signatures = parse_witness_multisig(witness)
|
||||||
script_hash = sha256(script).digest()
|
script_hash = sha256(script).digest()
|
||||||
if output_script_native_segwit(0, script_hash) != script_pubkey:
|
if output_script_native_segwit(0, script_hash) != script_pubkey:
|
||||||
raise wire.DataError("Invalid script hash")
|
raise wire.DataError("Invalid script hash")
|
||||||
self.public_keys, self.threshold = parse_output_script_multisig(script)
|
self.public_keys, self.threshold = parse_output_script_multisig(script)
|
||||||
|
elif len(script_pubkey) == 34 and script_pubkey[0] == OP_1: # P2TR
|
||||||
|
self.is_taproot = True
|
||||||
|
self.public_keys = [parse_output_script_p2tr(script_pubkey)]
|
||||||
|
self.signatures = [parse_witness_p2tr(witness)]
|
||||||
else:
|
else:
|
||||||
raise wire.DataError("Unsupported signature script")
|
raise wire.DataError("Unsupported signature script")
|
||||||
elif witness and witness != b"\x00":
|
elif witness and witness != b"\x00":
|
||||||
@ -98,8 +106,8 @@ class SignatureVerifier:
|
|||||||
if self.threshold != len(self.signatures):
|
if self.threshold != len(self.signatures):
|
||||||
raise wire.DataError("Invalid signature")
|
raise wire.DataError("Invalid signature")
|
||||||
|
|
||||||
def ensure_hash_type(self, hash_type: int) -> None:
|
def ensure_hash_type(self, hash_types: Sequence[int]) -> None:
|
||||||
if any(h != hash_type for _, h in self.signatures):
|
if any(h not in hash_types for _, h in self.signatures):
|
||||||
raise wire.DataError("Unsupported sighash type")
|
raise wire.DataError("Unsupported sighash type")
|
||||||
|
|
||||||
def verify(self, digest: bytes) -> None:
|
def verify(self, digest: bytes) -> None:
|
||||||
@ -108,6 +116,17 @@ class SignatureVerifier:
|
|||||||
# different hash type than expected, then verification will fail. To
|
# different hash type than expected, then verification will fail. To
|
||||||
# return the proper error message, the caller can optionally check the
|
# return the proper error message, the caller can optionally check the
|
||||||
# hash type by using ensure_hash_type() before calling verify.
|
# hash type by using ensure_hash_type() before calling verify.
|
||||||
|
|
||||||
|
if self.is_taproot:
|
||||||
|
self.verify_bip340(digest)
|
||||||
|
else:
|
||||||
|
self.verify_ecdsa(digest)
|
||||||
|
|
||||||
|
def verify_bip340(self, digest: bytes) -> None:
|
||||||
|
if not bip340.verify(self.public_keys[0], self.signatures[0][0], digest):
|
||||||
|
raise wire.DataError("Invalid signature")
|
||||||
|
|
||||||
|
def verify_ecdsa(self, digest: bytes) -> None:
|
||||||
try:
|
try:
|
||||||
i = 0
|
i = 0
|
||||||
for der_signature, _ in self.signatures:
|
for der_signature, _ in self.signatures:
|
||||||
|
Loading…
Reference in New Issue
Block a user