From 0959947a88355c772322575fbe5a38f8cb5b1690 Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Tue, 12 Jan 2021 18:56:10 +0100 Subject: [PATCH] chore(common): Add GetNonce, Nonce and TxAckPaymentRequest message. [no changelog] --- common/protob/messages-bitcoin.proto | 37 +++++ common/protob/messages-management.proto | 16 +++ common/protob/messages.proto | 3 + core/src/trezor/enums/MessageType.py | 3 + core/src/trezor/enums/RequestType.py | 1 + core/src/trezor/enums/__init__.py | 4 + core/src/trezor/messages.py | 112 +++++++++++++++ legacy/firmware/protob/Makefile | 4 +- .../firmware/protob/messages-bitcoin.options | 2 + .../protob/messages-management.options | 2 + python/src/trezorlib/messages.py | 128 ++++++++++++++++++ 11 files changed, 310 insertions(+), 2 deletions(-) diff --git a/common/protob/messages-bitcoin.proto b/common/protob/messages-bitcoin.proto index 9dce9c0a3..3331c87fd 100644 --- a/common/protob/messages-bitcoin.proto +++ b/common/protob/messages-bitcoin.proto @@ -209,6 +209,7 @@ message SignTx { * @next TxAckPrevInput * @next TxAckPrevOutput * @next TxAckPrevExtraData + * @next TxAckPaymentRequest */ message TxRequest { optional RequestType request_type = 1; // what should be filled in TxAck message? @@ -225,6 +226,7 @@ message TxRequest { TXEXTRADATA = 4; TXORIGINPUT = 5; TXORIGOUTPUT = 6; + TXPAYMENTREQ = 7; } /** * Structure representing request details @@ -326,6 +328,7 @@ message TxAck { // optional uint32 block_height_bip115 = 9; // BIP-115 support dropped optional bytes orig_hash = 10; // tx_hash of the original transaction where this output was present (used when creating a replacement transaction) optional uint32 orig_index = 11; // index of the output in the original transaction (used when creating a replacement transaction) + optional uint32 payment_req_index = 12; // index of the PaymentRequest containing this output } } } @@ -369,6 +372,7 @@ message TxOutput { reserved 7, 8, 9; // fields which are in use, or have been in the past, in TxOutputType optional bytes orig_hash = 10; // tx_hash of the original transaction where this output was present (used when creating a replacement transaction) optional uint32 orig_index = 11; // index of the output in the original transaction (used when creating a replacement transaction) + optional uint32 payment_req_index = 12; // index of the PaymentRequest containing this output } /** Data type for metadata about previous transaction which contains the UTXO being spent. @@ -414,6 +418,39 @@ message PrevOutput { optional uint32 decred_script_version = 3; // only for Decred } +/** Data type of a payment request for a set of outputs. + * @next TxRequest + */ +message TxAckPaymentRequest { + optional bytes nonce = 1; // the nonce used in the signature computation + required string recipient_name = 2; // merchant's name or coordinator's name in case of CoinJoin + repeated PaymentRequestMemo memos = 3; // any memos that were signed as part of the request + optional uint64 amount = 4; // the sum of the external output amounts requested, required for non-CoinJoin + required bytes signature = 5; // the trusted party's signature of the paymentRequestDigest + + message PaymentRequestMemo { + optional TextMemo text_memo = 1; + optional RefundMemo refund_memo = 2; + optional CoinPurchaseMemo coin_purchase_memo = 3; + } + + message TextMemo { + required string text = 1; // plain-text note explaining the purpose of the payment request + } + + message RefundMemo { + required string address = 1; // the address where the payment should be refunded if necessary + required bytes mac = 2; // the MAC returned by GetAddress + } + + message CoinPurchaseMemo { + required uint32 coin_type = 1; // the SLIP-0044 coin type of the address + required string amount = 2; // the amount the address will receive as a human-readable string including units, e.g. "0.025 BTC" + required string address = 3; // the address where the coin purchase will be delivered + required bytes mac = 4; // the MAC returned by GetAddress + } +} + /** * Request: Data about input to be signed. * Wire-alias of TxAck. diff --git a/common/protob/messages-management.proto b/common/protob/messages-management.proto index 4288f6b6e..d4ba2432a 100644 --- a/common/protob/messages-management.proto +++ b/common/protob/messages-management.proto @@ -421,3 +421,19 @@ message CancelAuthorization { */ message RebootToBootloader { } + +/** + * Request: Ask device to generate a random nonce and store it in the session's cache + * @start + * @next Nonce + */ +message GetNonce { +} + +/** + * Response: Contains a random nonce + * @end + */ +message Nonce { + required bytes nonce = 1; // a 32-byte random value generated by Trezor +} diff --git a/common/protob/messages.proto b/common/protob/messages.proto index 4a7dbacaf..0c9d9914a 100644 --- a/common/protob/messages.proto +++ b/common/protob/messages.proto @@ -96,6 +96,8 @@ enum MessageType { MessageType_ButtonRequest = 26 [(bitcoin_only) = true, (wire_out) = true]; MessageType_ButtonAck = 27 [(bitcoin_only) = true, (wire_in) = true, (wire_tiny) = true, (wire_no_fsm) = true]; MessageType_ApplyFlags = 28 [(bitcoin_only) = true, (wire_in) = true]; + MessageType_GetNonce = 31 [(bitcoin_only) = true, (wire_in) = true]; + MessageType_Nonce = 33 [(bitcoin_only) = true, (wire_out) = true]; MessageType_BackupDevice = 34 [(bitcoin_only) = true, (wire_in) = true]; MessageType_EntropyRequest = 35 [(bitcoin_only) = true, (wire_out) = true]; MessageType_EntropyAck = 36 [(bitcoin_only) = true, (wire_in) = true]; @@ -136,6 +138,7 @@ enum MessageType { MessageType_TxAck = 22 [(bitcoin_only) = true, (wire_in) = true]; MessageType_GetAddress = 29 [(bitcoin_only) = true, (wire_in) = true]; MessageType_Address = 30 [(bitcoin_only) = true, (wire_out) = true]; + MessageType_TxAckPaymentRequest = 37 [(wire_in) = true]; MessageType_SignMessage = 38 [(bitcoin_only) = true, (wire_in) = true]; MessageType_VerifyMessage = 39 [(bitcoin_only) = true, (wire_in) = true]; MessageType_MessageSignature = 40 [(bitcoin_only) = true, (wire_out) = true]; diff --git a/core/src/trezor/enums/MessageType.py b/core/src/trezor/enums/MessageType.py index 359c54506..74ff942fa 100644 --- a/core/src/trezor/enums/MessageType.py +++ b/core/src/trezor/enums/MessageType.py @@ -23,6 +23,8 @@ ApplySettings = 25 ButtonRequest = 26 ButtonAck = 27 ApplyFlags = 28 +GetNonce = 31 +Nonce = 33 BackupDevice = 34 EntropyRequest = 35 EntropyAck = 36 @@ -86,6 +88,7 @@ if not utils.BITCOIN_ONLY: SetU2FCounter = 63 GetNextU2FCounter = 80 NextU2FCounter = 81 + TxAckPaymentRequest = 37 EthereumGetPublicKey = 450 EthereumPublicKey = 451 EthereumGetAddress = 56 diff --git a/core/src/trezor/enums/RequestType.py b/core/src/trezor/enums/RequestType.py index db6b734e6..29bf1af21 100644 --- a/core/src/trezor/enums/RequestType.py +++ b/core/src/trezor/enums/RequestType.py @@ -9,3 +9,4 @@ TXFINISHED = 3 TXEXTRADATA = 4 TXORIGINPUT = 5 TXORIGOUTPUT = 6 +TXPAYMENTREQ = 7 diff --git a/core/src/trezor/enums/__init__.py b/core/src/trezor/enums/__init__.py index f7f3ddc73..d1ad8b59a 100644 --- a/core/src/trezor/enums/__init__.py +++ b/core/src/trezor/enums/__init__.py @@ -23,6 +23,8 @@ if TYPE_CHECKING: ButtonRequest = 26 ButtonAck = 27 ApplyFlags = 28 + GetNonce = 31 + Nonce = 33 BackupDevice = 34 EntropyRequest = 35 EntropyAck = 36 @@ -55,6 +57,7 @@ if TYPE_CHECKING: TxAck = 22 GetAddress = 29 Address = 30 + TxAckPaymentRequest = 37 SignMessage = 38 VerifyMessage = 39 MessageSignature = 40 @@ -324,6 +327,7 @@ if TYPE_CHECKING: TXEXTRADATA = 4 TXORIGINPUT = 5 TXORIGOUTPUT = 6 + TXPAYMENTREQ = 7 class CardanoDerivationType(IntEnum): LEDGER = 0 diff --git a/core/src/trezor/messages.py b/core/src/trezor/messages.py index 536be2859..392f196bf 100644 --- a/core/src/trezor/messages.py +++ b/core/src/trezor/messages.py @@ -683,6 +683,7 @@ if TYPE_CHECKING: op_return_data: "bytes | None" orig_hash: "bytes | None" orig_index: "int | None" + payment_req_index: "int | None" def __init__( self, @@ -695,6 +696,7 @@ if TYPE_CHECKING: op_return_data: "bytes | None" = None, orig_hash: "bytes | None" = None, orig_index: "int | None" = None, + payment_req_index: "int | None" = None, ) -> None: pass @@ -772,6 +774,28 @@ if TYPE_CHECKING: def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["PrevOutput"]: return isinstance(msg, cls) + class TxAckPaymentRequest(protobuf.MessageType): + nonce: "bytes | None" + recipient_name: "str" + memos: "list[PaymentRequestMemo]" + amount: "int | None" + signature: "bytes" + + def __init__( + self, + *, + recipient_name: "str", + signature: "bytes", + memos: "list[PaymentRequestMemo] | None" = None, + nonce: "bytes | None" = None, + amount: "int | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["TxAckPaymentRequest"]: + return isinstance(msg, cls) + class TxAckInput(protobuf.MessageType): tx: "TxAckInputWrapper" @@ -978,6 +1002,74 @@ if TYPE_CHECKING: def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["TxRequestSerializedType"]: return isinstance(msg, cls) + class PaymentRequestMemo(protobuf.MessageType): + text_memo: "TextMemo | None" + refund_memo: "RefundMemo | None" + coin_purchase_memo: "CoinPurchaseMemo | None" + + def __init__( + self, + *, + text_memo: "TextMemo | None" = None, + refund_memo: "RefundMemo | None" = None, + coin_purchase_memo: "CoinPurchaseMemo | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["PaymentRequestMemo"]: + return isinstance(msg, cls) + + class TextMemo(protobuf.MessageType): + text: "str" + + def __init__( + self, + *, + text: "str", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["TextMemo"]: + return isinstance(msg, cls) + + class RefundMemo(protobuf.MessageType): + address: "str" + mac: "bytes" + + def __init__( + self, + *, + address: "str", + mac: "bytes", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["RefundMemo"]: + return isinstance(msg, cls) + + class CoinPurchaseMemo(protobuf.MessageType): + coin_type: "int" + amount: "str" + address: "str" + mac: "bytes" + + def __init__( + self, + *, + coin_type: "int", + amount: "str", + address: "str", + mac: "bytes", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["CoinPurchaseMemo"]: + return isinstance(msg, cls) + class TxAckInputWrapper(protobuf.MessageType): input: "TxInput" @@ -2258,6 +2350,26 @@ if TYPE_CHECKING: def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["RebootToBootloader"]: return isinstance(msg, cls) + class GetNonce(protobuf.MessageType): + + @classmethod + def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["GetNonce"]: + return isinstance(msg, cls) + + class Nonce(protobuf.MessageType): + nonce: "bytes" + + def __init__( + self, + *, + nonce: "bytes", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["Nonce"]: + return isinstance(msg, cls) + class DebugLinkDecision(protobuf.MessageType): button: "DebugButton | None" swipe: "DebugSwipeDirection | None" diff --git a/legacy/firmware/protob/Makefile b/legacy/firmware/protob/Makefile index 6f7a020bf..e4d7b6d83 100644 --- a/legacy/firmware/protob/Makefile +++ b/legacy/firmware/protob/Makefile @@ -5,8 +5,8 @@ endif SKIPPED_MESSAGES := Binance Cardano DebugMonero Eos Monero Ontology Ripple SdProtect Tezos WebAuthn \ DebugLinkRecordScreen DebugLinkReseedRandom DebugLinkShowText DebugLinkEraseSdCard DebugLinkWatchLayout \ GetOwnershipProof OwnershipProof GetOwnershipId OwnershipId AuthorizeCoinJoin DoPreauthorized \ - CancelAuthorization DebugLinkLayout \ - TxAckInput TxAckOutput TxAckPrev \ + CancelAuthorization DebugLinkLayout GetNonce \ + TxAckInput TxAckOutput TxAckPrev TxAckPaymentRequest \ EthereumSignTypedData EthereumTypedDataStructRequest EthereumTypedDataStructAck \ EthereumTypedDataValueRequest EthereumTypedDataValueAck diff --git a/legacy/firmware/protob/messages-bitcoin.options b/legacy/firmware/protob/messages-bitcoin.options index 9cfc470a0..c7c7a5d04 100644 --- a/legacy/firmware/protob/messages-bitcoin.options +++ b/legacy/firmware/protob/messages-bitcoin.options @@ -83,3 +83,5 @@ GetOwnershipId skip_message:true OwnershipId skip_message:true GetOwnershipProof skip_message:true OwnershipProof skip_message:true +TxAckPaymentRequest skip_message:true +PaymentRequestMemo skip_message:true diff --git a/legacy/firmware/protob/messages-management.options b/legacy/firmware/protob/messages-management.options index 221137a82..2ae1aa081 100644 --- a/legacy/firmware/protob/messages-management.options +++ b/legacy/firmware/protob/messages-management.options @@ -33,3 +33,5 @@ RecoveryDevice.language max_size:17 RecoveryDevice.label max_size:33 WordAck.word max_size:12 + +Nonce.nonce max_size:32 diff --git a/python/src/trezorlib/messages.py b/python/src/trezorlib/messages.py index 36d9e99e7..75067b9ef 100644 --- a/python/src/trezorlib/messages.py +++ b/python/src/trezorlib/messages.py @@ -48,6 +48,8 @@ class MessageType(IntEnum): ButtonRequest = 26 ButtonAck = 27 ApplyFlags = 28 + GetNonce = 31 + Nonce = 33 BackupDevice = 34 EntropyRequest = 35 EntropyAck = 36 @@ -80,6 +82,7 @@ class MessageType(IntEnum): TxAck = 22 GetAddress = 29 Address = 30 + TxAckPaymentRequest = 37 SignMessage = 38 VerifyMessage = 39 MessageSignature = 40 @@ -340,6 +343,7 @@ class RequestType(IntEnum): TXEXTRADATA = 4 TXORIGINPUT = 5 TXORIGOUTPUT = 6 + TXPAYMENTREQ = 7 class CardanoDerivationType(IntEnum): @@ -1284,6 +1288,7 @@ class TxOutput(protobuf.MessageType): 6: protobuf.Field("op_return_data", "bytes", repeated=False, required=False), 10: protobuf.Field("orig_hash", "bytes", repeated=False, required=False), 11: protobuf.Field("orig_index", "uint32", repeated=False, required=False), + 12: protobuf.Field("payment_req_index", "uint32", repeated=False, required=False), } def __init__( @@ -1297,6 +1302,7 @@ class TxOutput(protobuf.MessageType): op_return_data: Optional["bytes"] = None, orig_hash: Optional["bytes"] = None, orig_index: Optional["int"] = None, + payment_req_index: Optional["int"] = None, ) -> None: self.address_n: Sequence["int"] = address_n if address_n is not None else [] self.amount = amount @@ -1306,6 +1312,7 @@ class TxOutput(protobuf.MessageType): self.op_return_data = op_return_data self.orig_hash = orig_hash self.orig_index = orig_index + self.payment_req_index = payment_req_index class PrevTx(protobuf.MessageType): @@ -1392,6 +1399,32 @@ class PrevOutput(protobuf.MessageType): self.decred_script_version = decred_script_version +class TxAckPaymentRequest(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 37 + FIELDS = { + 1: protobuf.Field("nonce", "bytes", repeated=False, required=False), + 2: protobuf.Field("recipient_name", "string", repeated=False, required=True), + 3: protobuf.Field("memos", "PaymentRequestMemo", repeated=True, required=False), + 4: protobuf.Field("amount", "uint64", repeated=False, required=False), + 5: protobuf.Field("signature", "bytes", repeated=False, required=True), + } + + def __init__( + self, + *, + recipient_name: "str", + signature: "bytes", + memos: Optional[Sequence["PaymentRequestMemo"]] = None, + nonce: Optional["bytes"] = None, + amount: Optional["int"] = None, + ) -> None: + self.memos: Sequence["PaymentRequestMemo"] = memos if memos is not None else [] + self.recipient_name = recipient_name + self.signature = signature + self.nonce = nonce + self.amount = amount + + class TxAckInput(protobuf.MessageType): MESSAGE_WIRE_TYPE = 22 FIELDS = { @@ -1760,6 +1793,7 @@ class TxOutputType(protobuf.MessageType): 6: protobuf.Field("op_return_data", "bytes", repeated=False, required=False), 10: protobuf.Field("orig_hash", "bytes", repeated=False, required=False), 11: protobuf.Field("orig_index", "uint32", repeated=False, required=False), + 12: protobuf.Field("payment_req_index", "uint32", repeated=False, required=False), } def __init__( @@ -1773,6 +1807,7 @@ class TxOutputType(protobuf.MessageType): op_return_data: Optional["bytes"] = None, orig_hash: Optional["bytes"] = None, orig_index: Optional["int"] = None, + payment_req_index: Optional["int"] = None, ) -> None: self.address_n: Sequence["int"] = address_n if address_n is not None else [] self.amount = amount @@ -1782,6 +1817,81 @@ class TxOutputType(protobuf.MessageType): self.op_return_data = op_return_data self.orig_hash = orig_hash self.orig_index = orig_index + self.payment_req_index = payment_req_index + + +class PaymentRequestMemo(protobuf.MessageType): + MESSAGE_WIRE_TYPE = None + FIELDS = { + 1: protobuf.Field("text_memo", "TextMemo", repeated=False, required=False), + 2: protobuf.Field("refund_memo", "RefundMemo", repeated=False, required=False), + 3: protobuf.Field("coin_purchase_memo", "CoinPurchaseMemo", repeated=False, required=False), + } + + def __init__( + self, + *, + text_memo: Optional["TextMemo"] = None, + refund_memo: Optional["RefundMemo"] = None, + coin_purchase_memo: Optional["CoinPurchaseMemo"] = None, + ) -> None: + self.text_memo = text_memo + self.refund_memo = refund_memo + self.coin_purchase_memo = coin_purchase_memo + + +class TextMemo(protobuf.MessageType): + MESSAGE_WIRE_TYPE = None + FIELDS = { + 1: protobuf.Field("text", "string", repeated=False, required=True), + } + + def __init__( + self, + *, + text: "str", + ) -> None: + self.text = text + + +class RefundMemo(protobuf.MessageType): + MESSAGE_WIRE_TYPE = None + FIELDS = { + 1: protobuf.Field("address", "string", repeated=False, required=True), + 2: protobuf.Field("mac", "bytes", repeated=False, required=True), + } + + def __init__( + self, + *, + address: "str", + mac: "bytes", + ) -> None: + self.address = address + self.mac = mac + + +class CoinPurchaseMemo(protobuf.MessageType): + MESSAGE_WIRE_TYPE = None + FIELDS = { + 1: protobuf.Field("coin_type", "uint32", repeated=False, required=True), + 2: protobuf.Field("amount", "string", repeated=False, required=True), + 3: protobuf.Field("address", "string", repeated=False, required=True), + 4: protobuf.Field("mac", "bytes", repeated=False, required=True), + } + + def __init__( + self, + *, + coin_type: "int", + amount: "str", + address: "str", + mac: "bytes", + ) -> None: + self.coin_type = coin_type + self.amount = amount + self.address = address + self.mac = mac class TxAckInputWrapper(protobuf.MessageType): @@ -3532,6 +3642,24 @@ class RebootToBootloader(protobuf.MessageType): MESSAGE_WIRE_TYPE = 87 +class GetNonce(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 31 + + +class Nonce(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 33 + FIELDS = { + 1: protobuf.Field("nonce", "bytes", repeated=False, required=True), + } + + def __init__( + self, + *, + nonce: "bytes", + ) -> None: + self.nonce = nonce + + class DebugLinkDecision(protobuf.MessageType): MESSAGE_WIRE_TYPE = 100 FIELDS = {