1
0
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:
Andrew Kozlik 2021-10-29 21:50:51 +02:00 committed by Andrew Kozlik
parent a17cdb7cfa
commit 84e31310a1
4 changed files with 28 additions and 7 deletions

View File

@ -0,0 +1 @@
Support replacement transactions with Taproot inputs in Bitcoin.

View File

@ -0,0 +1 @@
Support pre-signed external Taproot inputs in Bitcoin.

View File

@ -374,7 +374,7 @@ class Bitcoin:
verifier = SignatureVerifier(
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(
orig.verification_index,
txi,
@ -439,7 +439,7 @@ class Bitcoin:
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(
i,

View File

@ -1,9 +1,9 @@
from trezor import utils, wire
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 .common import ecdsa_hash_pubkey
from .common import OP_0, OP_1, ecdsa_hash_pubkey
from .scripts import (
output_script_native_segwit,
output_script_p2pkh,
@ -11,13 +11,16 @@ from .scripts import (
parse_input_script_multisig,
parse_input_script_p2pkh,
parse_output_script_multisig,
parse_output_script_p2tr,
parse_witness_multisig,
parse_witness_p2tr,
parse_witness_p2wpkh,
write_input_script_p2wpkh_in_p2sh,
write_input_script_p2wsh_in_p2sh,
)
if False:
from typing import Sequence
from apps.common.coininfo import CoinInfo
@ -32,6 +35,7 @@ class SignatureVerifier:
self.threshold = 1
self.public_keys: list[memoryview] = []
self.signatures: list[tuple[memoryview, int]] = []
self.is_taproot = False
if not script_sig:
if not witness:
@ -44,12 +48,16 @@ class SignatureVerifier:
raise wire.DataError("Invalid public key hash")
self.public_keys = [public_key]
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_hash = sha256(script).digest()
if output_script_native_segwit(0, script_hash) != script_pubkey:
raise wire.DataError("Invalid script hash")
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:
raise wire.DataError("Unsupported signature script")
elif witness and witness != b"\x00":
@ -98,8 +106,8 @@ class SignatureVerifier:
if self.threshold != len(self.signatures):
raise wire.DataError("Invalid signature")
def ensure_hash_type(self, hash_type: int) -> None:
if any(h != hash_type for _, h in self.signatures):
def ensure_hash_type(self, hash_types: Sequence[int]) -> None:
if any(h not in hash_types for _, h in self.signatures):
raise wire.DataError("Unsupported sighash type")
def verify(self, digest: bytes) -> None:
@ -108,6 +116,17 @@ class SignatureVerifier:
# different hash type than expected, then verification will fail. To
# return the proper error message, the caller can optionally check the
# 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:
i = 0
for der_signature, _ in self.signatures: