mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-04-19 00:29:03 +00:00
feat(core): support ownership proof anti-exfil protocol
This commit is contained in:
parent
cb7ac84d8a
commit
12ccfcd43c
@ -131,10 +131,6 @@ class EcdsaSigner:
|
||||
)
|
||||
|
||||
|
||||
def ecdsa_sign(node: bip32.HDNode, digest: bytes) -> bytes:
|
||||
return EcdsaSigner(node, digest).sign()
|
||||
|
||||
|
||||
def bip340_sign(node: bip32.HDNode, digest: bytes) -> bytes:
|
||||
internal_private_key = node.private_key()
|
||||
output_private_key = bip340.tweak_secret_key(internal_private_key)
|
||||
|
@ -11,6 +11,17 @@ if TYPE_CHECKING:
|
||||
from .authorization import CoinJoinAuthorization
|
||||
|
||||
|
||||
async def send_request_entropy(nonce_commitment: bytes) -> bytes:
|
||||
from trezor.messages import OwnershipProofEntropy, OwnershipProofNonceCommitment
|
||||
from trezor.wire.context import call
|
||||
|
||||
req = OwnershipProofNonceCommitment()
|
||||
req.nonce_commitment = nonce_commitment
|
||||
resp = await call(req, OwnershipProofEntropy)
|
||||
assert resp.entropy is not None
|
||||
return resp.entropy
|
||||
|
||||
|
||||
@with_keychain
|
||||
async def get_ownership_proof(
|
||||
msg: GetOwnershipProof,
|
||||
@ -28,7 +39,7 @@ async def get_ownership_proof(
|
||||
|
||||
from . import addresses, common, scripts
|
||||
from .keychain import validate_path_against_script_type
|
||||
from .ownership import generate_proof, get_identifier
|
||||
from .ownership import ProofGenerator, get_identifier
|
||||
|
||||
script_type = msg.script_type # local_cache_attribute
|
||||
ownership_ids = msg.ownership_ids # local_cache_attribute
|
||||
@ -83,7 +94,7 @@ async def get_ownership_proof(
|
||||
TR.bitcoin__commitment_data,
|
||||
)
|
||||
|
||||
ownership_proof, signature = generate_proof(
|
||||
proof_generator = ProofGenerator(
|
||||
node,
|
||||
script_type,
|
||||
msg.multisig,
|
||||
@ -94,4 +105,13 @@ async def get_ownership_proof(
|
||||
msg.commitment_data,
|
||||
)
|
||||
|
||||
return OwnershipProof(ownership_proof=ownership_proof, signature=signature)
|
||||
if msg.entropy_commitment:
|
||||
# use anti-exfil protocol
|
||||
nonce_commitment = proof_generator.commit_nonce(msg.entropy_commitment)
|
||||
entropy = await send_request_entropy(nonce_commitment)
|
||||
_, signature = proof_generator.sign(entropy)
|
||||
return OwnershipProof(ownership_proof=b"", signature=signature)
|
||||
else:
|
||||
ownership_proof, signature = proof_generator.sign()
|
||||
assert ownership_proof is not None
|
||||
return OwnershipProof(ownership_proof=ownership_proof, signature=signature)
|
||||
|
@ -3,17 +3,18 @@ from typing import TYPE_CHECKING
|
||||
|
||||
from trezor import utils
|
||||
from trezor.crypto.hashlib import sha256
|
||||
from trezor.enums import InputScriptType
|
||||
from trezor.utils import HashWriter
|
||||
from trezor.wire import DataError
|
||||
|
||||
from apps.bitcoin.writers import write_bytes_prefixed
|
||||
from apps.common.readers import read_compact_size
|
||||
|
||||
from .common import EcdsaSigner
|
||||
from .scripts import read_bip322_signature_proof
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezor.crypto import bip32
|
||||
from trezor.enums import InputScriptType
|
||||
from trezor.messages import MultisigRedeemScriptType
|
||||
|
||||
from apps.common.coininfo import CoinInfo
|
||||
@ -29,55 +30,103 @@ _OWNERSHIP_ID_LEN = const(32)
|
||||
_OWNERSHIP_ID_KEY_PATH = [b"SLIP-0019", b"Ownership identification key"]
|
||||
|
||||
|
||||
def generate_proof(
|
||||
node: bip32.HDNode,
|
||||
script_type: InputScriptType,
|
||||
multisig: MultisigRedeemScriptType | None,
|
||||
coin: CoinInfo,
|
||||
user_confirmed: bool,
|
||||
ownership_ids: list[bytes],
|
||||
script_pubkey: bytes,
|
||||
commitment_data: bytes,
|
||||
) -> tuple[bytes, bytes]:
|
||||
from trezor.enums import InputScriptType
|
||||
class ProofGenerator:
|
||||
@staticmethod
|
||||
def is_ecdsa_script_type(script_type: InputScriptType) -> bool:
|
||||
return script_type in (
|
||||
InputScriptType.SPENDADDRESS,
|
||||
InputScriptType.SPENDMULTISIG,
|
||||
InputScriptType.SPENDWITNESS,
|
||||
InputScriptType.SPENDP2SHWITNESS,
|
||||
)
|
||||
|
||||
from apps.bitcoin.writers import write_bytes_fixed, write_compact_size, write_uint8
|
||||
@staticmethod
|
||||
def get_proof_body(
|
||||
user_confirmed: bool,
|
||||
ownership_ids: list[bytes],
|
||||
script_pubkey: bytes,
|
||||
commitment_data: bytes,
|
||||
) -> tuple[bytearray, bytes]:
|
||||
from apps.bitcoin.writers import (
|
||||
write_bytes_fixed,
|
||||
write_compact_size,
|
||||
write_uint8,
|
||||
)
|
||||
|
||||
from . import common
|
||||
from .scripts import write_bip322_signature_proof
|
||||
flags = 0
|
||||
if user_confirmed:
|
||||
flags |= _FLAG_USER_CONFIRMED
|
||||
|
||||
flags = 0
|
||||
if user_confirmed:
|
||||
flags |= _FLAG_USER_CONFIRMED
|
||||
proof = utils.empty_bytearray(
|
||||
4 + 1 + 1 + len(ownership_ids) * _OWNERSHIP_ID_LEN
|
||||
)
|
||||
|
||||
proof = utils.empty_bytearray(4 + 1 + 1 + len(ownership_ids) * _OWNERSHIP_ID_LEN)
|
||||
write_bytes_fixed(proof, _VERSION_MAGIC, 4)
|
||||
write_uint8(proof, flags)
|
||||
write_compact_size(proof, len(ownership_ids))
|
||||
for ownership_id in ownership_ids:
|
||||
write_bytes_fixed(proof, ownership_id, _OWNERSHIP_ID_LEN)
|
||||
|
||||
write_bytes_fixed(proof, _VERSION_MAGIC, 4)
|
||||
write_uint8(proof, flags)
|
||||
write_compact_size(proof, len(ownership_ids))
|
||||
for ownership_id in ownership_ids:
|
||||
write_bytes_fixed(proof, ownership_id, _OWNERSHIP_ID_LEN)
|
||||
sighash = HashWriter(sha256(proof))
|
||||
write_bytes_prefixed(sighash, script_pubkey)
|
||||
write_bytes_prefixed(sighash, commitment_data)
|
||||
digest = sighash.get_digest()
|
||||
|
||||
sighash = HashWriter(sha256(proof))
|
||||
write_bytes_prefixed(sighash, script_pubkey)
|
||||
write_bytes_prefixed(sighash, commitment_data)
|
||||
if script_type in (
|
||||
InputScriptType.SPENDADDRESS,
|
||||
InputScriptType.SPENDMULTISIG,
|
||||
InputScriptType.SPENDWITNESS,
|
||||
InputScriptType.SPENDP2SHWITNESS,
|
||||
):
|
||||
signature = common.ecdsa_sign(node, sighash.get_digest())
|
||||
elif script_type == InputScriptType.SPENDTAPROOT:
|
||||
signature = common.bip340_sign(node, sighash.get_digest())
|
||||
else:
|
||||
raise DataError("Unsupported script type.")
|
||||
public_key = node.public_key()
|
||||
write_bip322_signature_proof(
|
||||
proof, script_type, multisig, coin, public_key, signature
|
||||
)
|
||||
return proof, digest
|
||||
|
||||
return proof, signature
|
||||
def __init__(
|
||||
self,
|
||||
node: bip32.HDNode,
|
||||
script_type: InputScriptType,
|
||||
multisig: MultisigRedeemScriptType | None,
|
||||
coin: CoinInfo,
|
||||
user_confirmed: bool,
|
||||
ownership_ids: list[bytes],
|
||||
script_pubkey: bytes,
|
||||
commitment_data: bytes,
|
||||
) -> None:
|
||||
self.proof_body, self.digest = self.get_proof_body(
|
||||
user_confirmed, ownership_ids, script_pubkey, commitment_data
|
||||
)
|
||||
self.node = node
|
||||
self.multisig = multisig
|
||||
self.coin = coin
|
||||
self.script_type = script_type
|
||||
|
||||
if self.is_ecdsa_script_type(script_type):
|
||||
self.signer = EcdsaSigner(self.node, self.digest)
|
||||
|
||||
def commit_nonce(self, entropy_commitment: bytes) -> bytes:
|
||||
if not self.is_ecdsa_script_type(self.script_type):
|
||||
raise DataError("Unsupported script type.")
|
||||
|
||||
return self.signer.commit_nonce(entropy_commitment)
|
||||
|
||||
def sign(self, entropy: bytes | None = None) -> tuple[bytes | None, bytes]:
|
||||
|
||||
from . import common
|
||||
from .scripts import write_bip322_signature_proof
|
||||
|
||||
if self.is_ecdsa_script_type(self.script_type):
|
||||
signature = self.signer.sign(entropy)
|
||||
elif self.script_type == InputScriptType.SPENDTAPROOT:
|
||||
signature = common.bip340_sign(self.node, self.digest)
|
||||
else:
|
||||
raise DataError("Unsupported script type.")
|
||||
|
||||
if entropy is None:
|
||||
public_key = self.node.public_key()
|
||||
write_bip322_signature_proof(
|
||||
self.proof_body,
|
||||
self.script_type,
|
||||
self.multisig,
|
||||
self.coin,
|
||||
public_key,
|
||||
signature,
|
||||
)
|
||||
return self.proof_body, signature
|
||||
else:
|
||||
return None, signature
|
||||
|
||||
|
||||
def verify_nonownership(
|
||||
|
@ -43,7 +43,7 @@ class TestOwnershipProof(unittest.TestCase):
|
||||
),
|
||||
)
|
||||
|
||||
proof, signature = ownership.generate_proof(
|
||||
proof, signature = ownership.ProofGenerator(
|
||||
node=node,
|
||||
script_type=InputScriptType.SPENDWITNESS,
|
||||
multisig=None,
|
||||
@ -52,7 +52,7 @@ class TestOwnershipProof(unittest.TestCase):
|
||||
ownership_ids=[ownership_id],
|
||||
script_pubkey=script_pubkey,
|
||||
commitment_data=commitment_data,
|
||||
)
|
||||
).sign()
|
||||
self.assertEqual(
|
||||
signature,
|
||||
unhexlify(
|
||||
@ -95,7 +95,7 @@ class TestOwnershipProof(unittest.TestCase):
|
||||
),
|
||||
)
|
||||
|
||||
proof, signature = ownership.generate_proof(
|
||||
proof, signature = ownership.ProofGenerator(
|
||||
node=node,
|
||||
script_type=InputScriptType.SPENDP2SHWITNESS,
|
||||
multisig=None,
|
||||
@ -104,7 +104,7 @@ class TestOwnershipProof(unittest.TestCase):
|
||||
ownership_ids=[ownership_id],
|
||||
script_pubkey=script_pubkey,
|
||||
commitment_data=commitment_data,
|
||||
)
|
||||
).sign()
|
||||
self.assertEqual(
|
||||
signature,
|
||||
unhexlify(
|
||||
@ -146,7 +146,7 @@ class TestOwnershipProof(unittest.TestCase):
|
||||
),
|
||||
)
|
||||
|
||||
proof, signature = ownership.generate_proof(
|
||||
proof, signature = ownership.ProofGenerator(
|
||||
node=node,
|
||||
script_type=InputScriptType.SPENDTAPROOT,
|
||||
multisig=None,
|
||||
@ -155,7 +155,7 @@ class TestOwnershipProof(unittest.TestCase):
|
||||
ownership_ids=[ownership_id],
|
||||
script_pubkey=script_pubkey,
|
||||
commitment_data=commitment_data,
|
||||
)
|
||||
).sign()
|
||||
self.assertEqual(
|
||||
signature,
|
||||
unhexlify(
|
||||
@ -197,7 +197,7 @@ class TestOwnershipProof(unittest.TestCase):
|
||||
),
|
||||
)
|
||||
|
||||
proof, signature = ownership.generate_proof(
|
||||
proof, signature = ownership.ProofGenerator(
|
||||
node=node,
|
||||
script_type=InputScriptType.SPENDADDRESS,
|
||||
multisig=None,
|
||||
@ -206,7 +206,7 @@ class TestOwnershipProof(unittest.TestCase):
|
||||
ownership_ids=[ownership_id],
|
||||
script_pubkey=script_pubkey,
|
||||
commitment_data=commitment_data,
|
||||
)
|
||||
).sign()
|
||||
self.assertEqual(
|
||||
signature,
|
||||
unhexlify(
|
||||
@ -338,7 +338,7 @@ class TestOwnershipProof(unittest.TestCase):
|
||||
)
|
||||
|
||||
# Sign with the first key.
|
||||
_, signature = ownership.generate_proof(
|
||||
_, signature = ownership.ProofGenerator(
|
||||
node=keychains[0].derive([84 | HARDENED, 0 | HARDENED, 0 | HARDENED, 1, 0]),
|
||||
script_type=InputScriptType.SPENDWITNESS,
|
||||
multisig=multisig,
|
||||
@ -347,7 +347,7 @@ class TestOwnershipProof(unittest.TestCase):
|
||||
ownership_ids=ownership_ids,
|
||||
script_pubkey=script_pubkey,
|
||||
commitment_data=commitment_data,
|
||||
)
|
||||
).sign()
|
||||
self.assertEqual(
|
||||
signature,
|
||||
unhexlify(
|
||||
@ -357,7 +357,7 @@ class TestOwnershipProof(unittest.TestCase):
|
||||
multisig.signatures[0] = signature
|
||||
|
||||
# Sign with the third key.
|
||||
proof, signature = ownership.generate_proof(
|
||||
proof, signature = ownership.ProofGenerator(
|
||||
node=keychain.derive([84 | HARDENED, 0 | HARDENED, 0 | HARDENED, 1, 0]),
|
||||
script_type=InputScriptType.SPENDWITNESS,
|
||||
multisig=multisig,
|
||||
@ -366,7 +366,7 @@ class TestOwnershipProof(unittest.TestCase):
|
||||
ownership_ids=ownership_ids,
|
||||
script_pubkey=script_pubkey,
|
||||
commitment_data=commitment_data,
|
||||
)
|
||||
).sign()
|
||||
self.assertEqual(
|
||||
signature,
|
||||
unhexlify(
|
||||
@ -429,7 +429,7 @@ class TestOwnershipProof(unittest.TestCase):
|
||||
)
|
||||
|
||||
# Sign with the second key.
|
||||
_, signature = ownership.generate_proof(
|
||||
_, signature = ownership.ProofGenerator(
|
||||
node=keychain.derive([49 | HARDENED, 0 | HARDENED, 2 | HARDENED, 0, 1]),
|
||||
script_type=InputScriptType.SPENDP2SHWITNESS,
|
||||
multisig=multisig,
|
||||
@ -438,7 +438,7 @@ class TestOwnershipProof(unittest.TestCase):
|
||||
ownership_ids=ownership_ids,
|
||||
script_pubkey=script_pubkey,
|
||||
commitment_data=commitment_data,
|
||||
)
|
||||
).sign()
|
||||
self.assertEqual(
|
||||
signature,
|
||||
unhexlify(
|
||||
@ -448,7 +448,7 @@ class TestOwnershipProof(unittest.TestCase):
|
||||
multisig.signatures[1] = signature
|
||||
|
||||
# Sign with the fourth key.
|
||||
proof, signature = ownership.generate_proof(
|
||||
proof, signature = ownership.ProofGenerator(
|
||||
node=keychain.derive([49 | HARDENED, 0 | HARDENED, 4 | HARDENED, 0, 1]),
|
||||
script_type=InputScriptType.SPENDP2SHWITNESS,
|
||||
multisig=multisig,
|
||||
@ -457,7 +457,7 @@ class TestOwnershipProof(unittest.TestCase):
|
||||
ownership_ids=ownership_ids,
|
||||
script_pubkey=script_pubkey,
|
||||
commitment_data=commitment_data,
|
||||
)
|
||||
).sign()
|
||||
self.assertEqual(
|
||||
signature,
|
||||
unhexlify(
|
||||
@ -467,7 +467,7 @@ class TestOwnershipProof(unittest.TestCase):
|
||||
multisig.signatures[3] = signature
|
||||
|
||||
# Sign with the fifth key.
|
||||
proof, signature = ownership.generate_proof(
|
||||
proof, signature = ownership.ProofGenerator(
|
||||
node=keychain.derive([49 | HARDENED, 0 | HARDENED, 5 | HARDENED, 0, 1]),
|
||||
script_type=InputScriptType.SPENDP2SHWITNESS,
|
||||
multisig=multisig,
|
||||
@ -476,7 +476,7 @@ class TestOwnershipProof(unittest.TestCase):
|
||||
ownership_ids=ownership_ids,
|
||||
script_pubkey=script_pubkey,
|
||||
commitment_data=commitment_data,
|
||||
)
|
||||
).sign()
|
||||
self.assertEqual(
|
||||
signature,
|
||||
unhexlify(
|
||||
@ -539,7 +539,7 @@ class TestOwnershipProof(unittest.TestCase):
|
||||
)
|
||||
|
||||
# Sign with the first key.
|
||||
_, signature = ownership.generate_proof(
|
||||
_, signature = ownership.ProofGenerator(
|
||||
node=keychain.derive([48 | HARDENED, 0 | HARDENED, 1 | HARDENED, 0, 0]),
|
||||
script_type=InputScriptType.SPENDMULTISIG,
|
||||
multisig=multisig,
|
||||
@ -548,7 +548,7 @@ class TestOwnershipProof(unittest.TestCase):
|
||||
ownership_ids=ownership_ids,
|
||||
script_pubkey=script_pubkey,
|
||||
commitment_data=commitment_data,
|
||||
)
|
||||
).sign()
|
||||
self.assertEqual(
|
||||
signature,
|
||||
unhexlify(
|
||||
@ -558,7 +558,7 @@ class TestOwnershipProof(unittest.TestCase):
|
||||
multisig.signatures[0] = signature
|
||||
|
||||
# Sign with the third key.
|
||||
proof, signature = ownership.generate_proof(
|
||||
proof, signature = ownership.ProofGenerator(
|
||||
node=keychain.derive([48 | HARDENED, 0 | HARDENED, 2 | HARDENED, 0, 0]),
|
||||
script_type=InputScriptType.SPENDMULTISIG,
|
||||
multisig=multisig,
|
||||
@ -567,7 +567,7 @@ class TestOwnershipProof(unittest.TestCase):
|
||||
ownership_ids=ownership_ids,
|
||||
script_pubkey=script_pubkey,
|
||||
commitment_data=commitment_data,
|
||||
)
|
||||
).sign()
|
||||
self.assertEqual(
|
||||
signature,
|
||||
unhexlify(
|
||||
|
Loading…
Reference in New Issue
Block a user