1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-01 11:58:28 +00:00

fixup! feat(core)): forbid multisig to singlesig change outputs

This commit is contained in:
Ondřej Vejpustek 2024-11-28 13:56:36 +01:00
parent c5200261fb
commit b7d26c794b
2 changed files with 17 additions and 29 deletions

View File

@ -17,15 +17,11 @@ _BIP32_MAX_LAST_ELEMENT = const(1_000_000)
class ChangeDetector: class ChangeDetector:
def __init__(self) -> None: def __init__(self) -> None:
from .matchcheck import ( from .matchcheck import (
MultisigChecker,
MultisigFingerprintChecker, MultisigFingerprintChecker,
ScriptTypeChecker, ScriptTypeChecker,
WalletPathChecker, WalletPathChecker,
) )
# Whether all inputs are multisig or all inputs are singlesig, used to validate change-output.
self.multisig = MultisigChecker()
# Checksum of multisig inputs, used to validate change-output. # Checksum of multisig inputs, used to validate change-output.
self.multisig_fingerprint = MultisigFingerprintChecker() self.multisig_fingerprint = MultisigFingerprintChecker()
@ -40,37 +36,30 @@ class ChangeDetector:
self.wallet_path.add_input(txi) self.wallet_path.add_input(txi)
self.script_type.add_input(txi) self.script_type.add_input(txi)
self.multisig_fingerprint.add_input(txi) self.multisig_fingerprint.add_input(txi)
self.multisig.add_input(txi)
def check_input(self, txi: TxInput) -> None: def check_input(self, txi: TxInput) -> None:
self.wallet_path.check_input(txi) self.wallet_path.check_input(txi)
self.script_type.check_input(txi) self.script_type.check_input(txi)
self.multisig_fingerprint.check_input(txi) self.multisig_fingerprint.check_input(txi)
self.multisig.check_input(txi)
def output_is_change(self, txo: TxOutput) -> bool: def output_is_change(self, txo: TxOutput) -> bool:
if txo.script_type not in common.CHANGE_OUTPUT_SCRIPT_TYPES: if txo.script_type not in common.CHANGE_OUTPUT_SCRIPT_TYPES:
return False return False
if txo.multisig: if txo.multisig and not common.multisig_uses_single_path(txo.multisig):
if not ( # An address that uses different derivation paths for different xpubs
self.multisig_fingerprint.output_matches(txo) # could be difficult to discover if the user did not note all the paths.
and common.multisig_uses_single_path( # The reason is that each path ends with an address index, which can
txo.multisig # have 1,000,000 possible values. If the address is a t-out-of-n
) # An address that uses different derivation paths for different xpubs # multisig, the total number of possible paths is 1,000,000^n. This can
# could be difficult to discover if the user did not note all the paths. # be exploited by an attacker who has compromised the user's computer.
# The reason is that each path ends with an address index, which can # The attacker could randomize the address indices and then demand a
# have 1,000,000 possible values. If the address is a t-out-of-n # ransom from the user to reveal the paths. To prevent this, we require
# multisig, the total number of possible paths is 1,000,000^n. This can # that all xpubs use the same derivation path.
# be exploited by an attacker who has compromised the user's computer. return False
# The attacker could randomize the address indices and then demand a
# ransom from the user to reveal the paths. To prevent this, we require
# that all xpubs use the same derivation path.
):
return False
return ( return (
self.multisig.output_matches(txo) self.multisig_fingerprint.output_matches(txo)
and self.wallet_path.output_matches(txo) and self.wallet_path.output_matches(txo)
and self.script_type.output_matches(txo) and self.script_type.output_matches(txo)
and len(txo.address_n) >= common.BIP32_WALLET_DEPTH and len(txo.address_n) >= common.BIP32_WALLET_DEPTH

View File

@ -103,15 +103,14 @@ class MultisigFingerprintChecker(MatchChecker):
from .. import multisig from .. import multisig
if not txio.multisig: if not txio.multisig:
return None # The fingerprint of a singlesig input or output is defined as an empty byte string.
# This has two consequences: First, a singlesig output matches if and only if all
# the added inputs are singlesig. Second, a multisig output does not match if any of
# the added inputs is singlesig.
return bytes()
return multisig.multisig_fingerprint(txio.multisig) return multisig.multisig_fingerprint(txio.multisig)
class MultisigChecker(MatchChecker):
def attribute_from_tx(self, txio: TxInput | TxOutput) -> Any:
return txio.multisig is not None
class ScriptTypeChecker(MatchChecker): class ScriptTypeChecker(MatchChecker):
def attribute_from_tx(self, txio: TxInput | TxOutput) -> Any: def attribute_from_tx(self, txio: TxInput | TxOutput) -> Any:
from trezor.enums import InputScriptType from trezor.enums import InputScriptType