diff --git a/core/src/apps/binance/get_address.py b/core/src/apps/binance/get_address.py index 88f4434375..1b114e3be9 100644 --- a/core/src/apps/binance/get_address.py +++ b/core/src/apps/binance/get_address.py @@ -1,31 +1,34 @@ from typing import TYPE_CHECKING -from trezor.messages import BinanceAddress -from trezor.ui.layouts import show_address - -from apps.common import paths -from apps.common.keychain import Keychain, auto_keychain - -from .helpers import address_from_public_key +from apps.common.keychain import auto_keychain if TYPE_CHECKING: - from trezor.messages import BinanceGetAddress + from trezor.messages import BinanceGetAddress, BinanceAddress from trezor.wire import Context + from apps.common.keychain import Keychain @auto_keychain(__name__) async def get_address( ctx: Context, msg: BinanceGetAddress, keychain: Keychain ) -> BinanceAddress: + from trezor.messages import BinanceAddress + from trezor.ui.layouts import show_address + + from apps.common import paths + + from .helpers import address_from_public_key + HRP = "bnb" + address_n = msg.address_n # local_cache_attribute - await paths.validate_path(ctx, keychain, msg.address_n) + await paths.validate_path(ctx, keychain, address_n) - node = keychain.derive(msg.address_n) + node = keychain.derive(address_n) pubkey = node.public_key() address = address_from_public_key(pubkey, HRP) if msg.show_display: - title = paths.address_n_to_str(msg.address_n) - await show_address(ctx, address=address, title=title) + title = paths.address_n_to_str(address_n) + await show_address(ctx, address, title=title) return BinanceAddress(address=address) diff --git a/core/src/apps/binance/get_public_key.py b/core/src/apps/binance/get_public_key.py index 9690740740..c8789cfd20 100644 --- a/core/src/apps/binance/get_public_key.py +++ b/core/src/apps/binance/get_public_key.py @@ -1,21 +1,24 @@ from typing import TYPE_CHECKING -from ubinascii import hexlify -from trezor.messages import BinancePublicKey -from trezor.ui.layouts import show_pubkey - -from apps.common import paths -from apps.common.keychain import Keychain, auto_keychain +from apps.common.keychain import auto_keychain if TYPE_CHECKING: - from trezor.messages import BinanceGetPublicKey + from trezor.messages import BinanceGetPublicKey, BinancePublicKey from trezor.wire import Context + from apps.common.keychain import Keychain @auto_keychain(__name__) async def get_public_key( ctx: Context, msg: BinanceGetPublicKey, keychain: Keychain ) -> BinancePublicKey: + from ubinascii import hexlify + + from trezor.messages import BinancePublicKey + from trezor.ui.layouts import show_pubkey + + from apps.common import paths + await paths.validate_path(ctx, keychain, msg.address_n) node = keychain.derive(msg.address_n) pubkey = node.public_key() diff --git a/core/src/apps/binance/helpers.py b/core/src/apps/binance/helpers.py index 5223b4c60f..1bafaf18bd 100644 --- a/core/src/apps/binance/helpers.py +++ b/core/src/apps/binance/helpers.py @@ -1,33 +1,52 @@ from micropython import const from typing import TYPE_CHECKING -from trezor import wire -from trezor.crypto import bech32 -from trezor.crypto.scripts import sha256_ripemd160 -from trezor.messages import BinanceCancelMsg, BinanceOrderMsg, BinanceTransferMsg - if TYPE_CHECKING: from trezor.messages import BinanceInputOutput, BinanceSignTx from trezor.protobuf import MessageType -ENVELOPE_BLUEPRINT = '{{"account_number":"{account_number}","chain_id":"{chain_id}","data":null,"memo":"{memo}","msgs":[{msgs}],"sequence":"{sequence}","source":"{source}"}}' -MSG_TRANSFER_BLUEPRINT = '{{"inputs":[{inputs}],"outputs":[{outputs}]}}' -MSG_NEWORDER_BLUEPRINT = '{{"id":"{id}","ordertype":{ordertype},"price":{price},"quantity":{quantity},"sender":"{sender}","side":{side},"symbol":"{symbol}","timeinforce":{timeinforce}}}' -MSG_CANCEL_BLUEPRINT = '{{"refid":"{refid}","sender":"{sender}","symbol":"{symbol}"}}' -INPUT_OUTPUT_BLUEPRINT = '{{"address":"{address}","coins":[{coins}]}}' -COIN_BLUEPRINT = '{{"amount":{amount},"denom":"{denom}"}}' - # 1*10^8 Jagers equal 1 BNB https://www.binance.vision/glossary/jager DECIMALS = const(8) +# NOTE: it is 11 bytes smaller to define it on top level than inside a function +def _make_input_output(input_output: BinanceInputOutput) -> str: + COIN_BLUEPRINT = '{{"amount":{},"denom":"{}"}}' + INPUT_OUTPUT_BLUEPRINT = '{{"address":"{}","coins":[{}]}}' + + coins = ",".join( + COIN_BLUEPRINT.format(c.amount, c.denom) for c in input_output.coins + ) + return INPUT_OUTPUT_BLUEPRINT.format(input_output.address, coins) + + def produce_json_for_signing(envelope: BinanceSignTx, msg: MessageType) -> str: + from trezor.messages import BinanceCancelMsg, BinanceOrderMsg, BinanceTransferMsg + from trezor import wire + + # NOTE: not defining kwargs in format string saves 7 bytes per each argument + ENVELOPE_BLUEPRINT = '{{"account_number":"{}","chain_id":"{}","data":null,"memo":"{}","msgs":[{}],"sequence":"{}","source":"{}"}}' + MSG_TRANSFER_BLUEPRINT = '{{"inputs":[{}],"outputs":[{}]}}' + MSG_NEWORDER_BLUEPRINT = '{{"id":"{}","ordertype":{},"price":{},"quantity":{},"sender":"{}","side":{},"symbol":"{}","timeinforce":{}}}' + MSG_CANCEL_BLUEPRINT = '{{"refid":"{}","sender":"{}","symbol":"{}"}}' + if BinanceTransferMsg.is_type_of(msg): - json_msg = produce_transfer_json(msg) + inputs = ",".join(_make_input_output(i) for i in msg.inputs) + outputs = ",".join(_make_input_output(o) for o in msg.outputs) + json_msg = MSG_TRANSFER_BLUEPRINT.format(inputs, outputs) elif BinanceOrderMsg.is_type_of(msg): - json_msg = produce_neworder_json(msg) + json_msg = MSG_NEWORDER_BLUEPRINT.format( + msg.id, + msg.ordertype, + msg.price, + msg.quantity, + msg.sender, + msg.side, + msg.symbol, + msg.timeinforce, + ) elif BinanceCancelMsg.is_type_of(msg): - json_msg = produce_cancel_json(msg) + json_msg = MSG_CANCEL_BLUEPRINT.format(msg.refid, msg.sender, msg.symbol) else: raise wire.ProcessError("input message unrecognized") @@ -35,45 +54,12 @@ def produce_json_for_signing(envelope: BinanceSignTx, msg: MessageType) -> str: raise wire.DataError("Source is invalid") return ENVELOPE_BLUEPRINT.format( - account_number=envelope.account_number, - chain_id=envelope.chain_id, - memo=envelope.memo, - msgs=json_msg, - sequence=envelope.sequence, - source=envelope.source, - ) - - -def produce_transfer_json(msg: BinanceTransferMsg) -> str: - def make_input_output(input_output: BinanceInputOutput) -> str: - coins = ",".join( - COIN_BLUEPRINT.format(amount=c.amount, denom=c.denom) - for c in input_output.coins - ) - return INPUT_OUTPUT_BLUEPRINT.format(address=input_output.address, coins=coins) - - inputs = ",".join(make_input_output(i) for i in msg.inputs) - outputs = ",".join(make_input_output(o) for o in msg.outputs) - - return MSG_TRANSFER_BLUEPRINT.format(inputs=inputs, outputs=outputs) - - -def produce_neworder_json(msg: BinanceOrderMsg) -> str: - return MSG_NEWORDER_BLUEPRINT.format( - id=msg.id, - ordertype=msg.ordertype, - price=msg.price, - quantity=msg.quantity, - sender=msg.sender, - side=msg.side, - symbol=msg.symbol, - timeinforce=msg.timeinforce, - ) - - -def produce_cancel_json(msg: BinanceCancelMsg) -> str: - return MSG_CANCEL_BLUEPRINT.format( - refid=msg.refid, sender=msg.sender, symbol=msg.symbol + envelope.account_number, + envelope.chain_id, + envelope.memo, + json_msg, + envelope.sequence, + envelope.source, ) @@ -83,6 +69,8 @@ def address_from_public_key(pubkey: bytes, hrp: str) -> str: Address_Bech32 = HRP + '1' + bech32.encode(convert8BitsTo5Bits(RIPEMD160(SHA256(compressed public key)))) HRP - bnb for productions, tbnb for tests """ + from trezor.crypto import bech32 + from trezor.crypto.scripts import sha256_ripemd160 h = sha256_ripemd160(pubkey).digest() diff --git a/core/src/apps/binance/layout.py b/core/src/apps/binance/layout.py index 897fe4725b..f7d6856576 100644 --- a/core/src/apps/binance/layout.py +++ b/core/src/apps/binance/layout.py @@ -1,11 +1,10 @@ from typing import TYPE_CHECKING -from trezor.enums import BinanceOrderSide, ButtonRequestType +from trezor.enums import ButtonRequestType from trezor.strings import format_amount from trezor.ui.layouts import confirm_properties -from trezor.ui.layouts.altcoin import confirm_transfer_binance -from . import helpers +from .helpers import DECIMALS if TYPE_CHECKING: from trezor.messages import ( @@ -18,14 +17,16 @@ if TYPE_CHECKING: async def require_confirm_transfer(ctx: Context, msg: BinanceTransferMsg) -> None: - items = [] + from trezor.ui.layouts.altcoin import confirm_transfer_binance + + items: list[tuple[str, str, str]] = [] def make_input_output_pages(msg: BinanceInputOutput, direction: str) -> None: for coin in msg.coins: items.append( ( direction, - format_amount(coin.amount, helpers.DECIMALS) + " " + coin.denom, + format_amount(coin.amount, DECIMALS) + " " + coin.denom, msg.address, ) ) @@ -43,18 +44,20 @@ async def require_confirm_cancel(ctx: Context, msg: BinanceCancelMsg) -> None: await confirm_properties( ctx, "confirm_cancel", - title="Confirm cancel", - props=[ + "Confirm cancel", + ( ("Sender address:", str(msg.sender)), ("Pair:", str(msg.symbol)), ("Order ID:", str(msg.refid)), - ], + ), hold=True, br_code=ButtonRequestType.SignTx, ) async def require_confirm_order(ctx: Context, msg: BinanceOrderMsg) -> None: + from trezor.enums import BinanceOrderSide + if msg.side == BinanceOrderSide.BUY: side = "Buy" elif msg.side == BinanceOrderSide.SELL: @@ -65,14 +68,14 @@ async def require_confirm_order(ctx: Context, msg: BinanceOrderMsg) -> None: await confirm_properties( ctx, "confirm_order", - title="Confirm order", - props=[ + "Confirm order", + ( ("Sender address:", str(msg.sender)), ("Pair:", str(msg.symbol)), ("Side:", side), - ("Quantity:", format_amount(msg.quantity, helpers.DECIMALS)), - ("Price:", format_amount(msg.price, helpers.DECIMALS)), - ], + ("Quantity:", format_amount(msg.quantity, DECIMALS)), + ("Price:", format_amount(msg.price, DECIMALS)), + ), hold=True, br_code=ButtonRequestType.SignTx, ) diff --git a/core/src/apps/binance/sign_tx.py b/core/src/apps/binance/sign_tx.py index 4c699a1a6a..3c1bfca8b9 100644 --- a/core/src/apps/binance/sign_tx.py +++ b/core/src/apps/binance/sign_tx.py @@ -1,30 +1,33 @@ from typing import TYPE_CHECKING -from trezor import wire -from trezor.crypto.curve import secp256k1 -from trezor.crypto.hashlib import sha256 -from trezor.enums import MessageType -from trezor.messages import ( - BinanceCancelMsg, - BinanceOrderMsg, - BinanceSignedTx, - BinanceTransferMsg, - BinanceTxRequest, -) - -from apps.common import paths -from apps.common.keychain import Keychain, auto_keychain - -from . import helpers, layout +from apps.common.keychain import auto_keychain if TYPE_CHECKING: - from trezor.messages import BinanceSignTx + from trezor.messages import BinanceSignTx, BinanceSignedTx + from apps.common.keychain import Keychain + from trezor.wire import Context @auto_keychain(__name__) async def sign_tx( - ctx: wire.Context, envelope: BinanceSignTx, keychain: Keychain + ctx: Context, envelope: BinanceSignTx, keychain: Keychain ) -> BinanceSignedTx: + from trezor import wire + from trezor.crypto.curve import secp256k1 + from trezor.crypto.hashlib import sha256 + from trezor.enums import MessageType + from trezor.messages import ( + BinanceCancelMsg, + BinanceOrderMsg, + BinanceSignedTx, + BinanceTransferMsg, + BinanceTxRequest, + ) + + from apps.common import paths + + from . import helpers, layout + # create transaction message -> sign it -> create signature/pubkey message -> serialize all if envelope.msg_count > 1: raise wire.DataError("Multiple messages not supported.") @@ -55,11 +58,8 @@ async def sign_tx( else: raise wire.ProcessError("input message unrecognized") - signature_bytes = generate_content_signature(msg_json.encode(), node.private_key()) + # generate_content_signature + msghash = sha256(msg_json.encode()).digest() + signature_bytes = secp256k1.sign(node.private_key(), msghash)[1:65] return BinanceSignedTx(signature=signature_bytes, public_key=node.public_key()) - - -def generate_content_signature(json: bytes, private_key: bytes) -> bytes: - msghash = sha256(json).digest() - return secp256k1.sign(private_key, msghash)[1:65] diff --git a/core/tests/test_apps.binance.sign_tx.py b/core/tests/test_apps.binance.sign_tx.py index 0d7f70debd..3db0d3ad1a 100644 --- a/core/tests/test_apps.binance.sign_tx.py +++ b/core/tests/test_apps.binance.sign_tx.py @@ -5,7 +5,6 @@ from trezor.crypto.hashlib import sha256 if not utils.BITCOIN_ONLY: from apps.binance.helpers import produce_json_for_signing - from apps.binance.sign_tx import generate_content_signature, sign_tx from trezor.messages import BinanceCancelMsg from trezor.messages import BinanceCoin from trezor.messages import BinanceInputOutput @@ -14,6 +13,12 @@ if not utils.BITCOIN_ONLY: from trezor.messages import BinanceTransferMsg +# NOTE: copy-pasted from apps.binance.sign_tx +def generate_content_signature(json: bytes, private_key: bytes) -> bytes: + msghash = sha256(json).digest() + return secp256k1.sign(private_key, msghash)[1:65] + + @unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin") class TestBinanceSign(unittest.TestCase): def test_order_signature(self):