Number: SLIP-0024
Title: Trezor payment request format
Type: Standard
Status: Draft
Authors: Andrew Kozlik <[email protected]>
Created: 2021-12-09
A Trezor payment request is a message that is signed by a trusted party requesting payment of a specified amount to one or more addresses, similar in principle to BIP-0070. This message can be processed by a payer's wallet in such a way that the payer does not have to inspect the destination address and only needs to confirm the payment of the requested amount to the recipient that is named in the payment request.
Before a cryptocurrency payment can be made, the recipient of the payment needs to securely communicate their receiving address to the payer. If the communication channel between the two parties is not well secured or if one of the endpoints is compromised by malware, then an attacker may modify the address, changing it to the attacker's own address in order to receive the payment instead of the legitimate recipient. This type of attack is known as address spoofing.
Hardware wallets are known to be the most secure solution to cryptocurrency payments. They are generally very resilient to malware, but they do not protect the payer from address spoofing outside of the wallet. The task of authenticating the address is up to the payer who should ideally verify the correctness of the address by means of a second channel. Using multiple channels to communicate the address reduces the likelihood of an attacker being able to compromise all of them at once, but at the same time it hinders the user experience.
This specification defines a new format of payment requests which aim to make cryptocurrency payments in hardware wallets safer and more user-friendly by allowing automated authentication of merchant's addresses using digital signatures.
The Trezor payment request format is heavily inspired by the earlier BIP-0070 specification. The main ways in which the present specification differs over BIP-0070 are summarized below.
- Adapted to the needs of hardware wallets.
- A nonce challenge is used to guarantee freshness instead of creation/expiry time, since the current time is not reliably available in hardware wallets.
- The way in which the payment request data is hashed is optimized for memory-constrained environments.
- PKI is considered out of scope of this specification, thus X.509 support is not required.
- Designed for use with multiple cryptocurrencies.
- Defines new types of memo fields, which allow for new use-cases.
- The signature is required.
The recipient name is shown to the payer instead of the address or addresses which the payment request represents. The payer must however have the option of inspecting the addresses on the wallet's screen if they wish.
Memos are used to provide additional information to the payer about the purpose of the payment request. A memo may also contain information that needs to be verified by the payer or their wallet. By making the payment to the specified address the payer confirms that the information in the memo is correct. Two types of memos are defined.
A text memo is a UTF-8 encoded, plain-text note explaining the purpose of the payment request. The note MUST be displayed to the payer in full.
A refund memo specifies the address where the payment should be refunded to the customer if necessary. The customer's wallet MUST verify that it controls this address, see Verifying address ownership. The wallet does not have to display this information on the screen.
A coin purchase memo can be used by a cryptocurrency exchange service to inform their customer about the cryptocurrency and amount that the customer will receive in exchange for the payment they make. The customer's wallet MUST display this information on the screen. The coin purchase memo also specifies the address to which the exchange will send the cryptocurrency purchased by the customer. The customer's wallet MUST verify that it controls this address, see Verifying address ownership.
A unique challenge generated by the payer's wallet. The nonce guarantees freshness of the payment request and prevents replaying the payment request to the wallet or to another wallet. A nonce SHOULD be present whenever one or more memos are present in the payment request.
A list of destination addresses with requested amounts. The payer is requested to pay the specified amount to each of the listed addresses.
A digital signature of the payment request issued by a party which is trusted by the payer.
The definition of a trusted party is out of scope of this specification. It is up to the wallet vendor to choose how trusted parties are defined. For example, they may be defined by one or more pinned public keys in the wallet's firmware or the trusted public keys may be user-defined. Trust can also be derived from a PKI scheme, such as X.509 in the BIP-0070 specification. However, it should be noted that X.509 is not very well suited for use in hardware wallets due to its excessive complexity, e.g. certificate extensions, expiry dates or certificate validation and revocation rules. Each trusted party may be restricted to signing payment requests only for particular recipient names.
The following describes several scenarios involving a customer paying a merchant using cryptocurrency. The customer has a hardware wallet and a wallet application running on their computer which controls the hardware wallet. The merchant has access to a signing server whose signatures are trusted by the customer's wallet.
- The customer creates an order with the merchant and chooses to pay using Bitcoin.
- The merchant generates a unique receiving address for the customer's order.
- The merchant authenticates itself to the signing server and sends it the receiving address and amount to be paid.
- The signing server returns a signed payment request to the merchant containing the merchant's name, receiving address and amount to be paid.
- The merchant verifies that the signature matches the provided data and generates a BIP-0021 URI with the payment request signature in
slip24sig
as described below. - The customer clicks the BIP-0021 URI which opens their wallet application.
- The wallet application creates a transaction transferring the requested amount to the merchant's address.
- The wallet application requests a transaction signature from the hardware wallet, providing the payment request data.
- The hardware wallet verifies that the payment request signature was issued by a trusted party and displays a confirmation dialog to the user stating the merchant's name and amount to be sent.
- The customer confirms the dialog and the hardware wallet generates the transaction signature.
- The wallet application broadcasts the signed transaction.
- The merchant monitors the blockchain for a transaction transferring the requested amount to its receiving address.
If the trusted signing server is operated by a hardware wallet vendor, then in step 1 the customer may need to select the vendor of their wallet. The server may also be operated by the merchant itself, in which case the customer will have had to load the merchant's public key into their hardware wallet in advance.
- The customer's wallet application selects an address which belongs to the customer and is suitable for receiving the refund of a payment.
- The wallet application obtains two items from the hardware wallet:
- an address authentication code for the selected refund address and
- a nonce challenge, which the hardware wallet also records in a nonce cache in its volatile memory.
- The wallet application provides the customer's refund address and nonce challenge to the merchant who will be receiving the payment.
- The merchant generates a unique receiving address for the payment request.
- The merchant authenticates itself to the signing server and sends it its receiving address, the amount to be paid, the customer's refund address and nonce challenge.
- The signing server returns a signed payment request to the merchant.
- The merchant verifies that the signature matches the provided data and returns the payment request to the customer's wallet application.
- The wallet application creates a transaction transferring the requested amount to the merchant's address.
- The wallet application requests a transaction signature from the hardware wallet, providing the payment request data together with the address authentication code of the refund address.
- The hardware wallet takes the following steps:
- Verifies that it issued the nonce challenge by checking its nonce cache and removes it from the cache so that the payment request cannot be reused in the future.
- Verifies that it controls the refund address by validating the address authentication code.
- Verifies that the payment request signature was issued by a trusted party.
- Displays a confirmation dialog to the user stating the merchant's name and amount to be sent.
- The customer confirms the dialog and the hardware wallet generates the transaction signature.
- The wallet application broadcasts the signed transaction.
- The merchant monitors the blockchain for a transaction transferring the requested amount to its receiving address.
- If the merchant needs to refund the payment, then it may send it to the refund address that was specified in the payment request without any further interaction with the customer.
The payment request signature is generated by signing the paymentRequestDigest using the private key of the trusted party. The default signature scheme is ECDSA with SHA-256 and the curve secp256k1, but any other suitable signature scheme may be chosen by the trusted party instead. In case of the default scheme the signature is encoded in 64 bytes. The first 32 bytes encode the r component of the signature and the second 32 bytes encode the s component, both in big-endian byte order.
The paymentRequestDigest is computed by hashing the concatenation of the following fields:
- versionMagic (4 bytes): b"\x53\x4c\x00\x24" (this is "SL" followed by 0024 in compressed numeric form as an abbreviation for "SLIP-0024").
- nonce (length-prefixed binary string): an optional nonce challenge from the wallet. If a nonce is not used, then a zero-length string should be provided.
- recipientName (length-prefixed UTF-8 string): a human-readable string identifying the recipient of the payment.
- n (CompactSize integer): the number of memos which follow. The CompactSize integer MUST be encoded in the fewest possible number of bytes.
- memo1 || memo2 || ... || memon (variable length): concatenation of the binary encoding of the memos, see below.
- coinType (4 bytes): 32-bit encoding of the SLIP-0044 coin type of the outputs in little-endian byte order.
- outputsHash (32 bytes): the hash of the binary encodings of all requested outputs, see below.
A text memo is encoded as the concatenation of the following fields:
- memoType (4 bytes): b"\x01\x00\x00\x00" (32-bit encoding of the integer 1 in little-endian byte order).
- text (length-prefixed UTF-8 string): a human-readable string providing information about the purpose of the payment.
A refund memo is encoded as the concatenation of the following fields:
- memoType (4 bytes): b"\x02\x00\x00\x00" (32-bit encoding of the integer 2 in little-endian byte order).
- address (length-prefixed string): the address where the payment should be refunded if necessary.
A coin purchase memo is encoded as follows:
- memoType (4 bytes): b"\x03\x00\x00\x00" (32-bit encoding of the integer 3 in little-endian byte order).
- coinType (4 bytes): 32-bit encoding of the SLIP-0044 coin type of the address where the coin purchase will be delivered in little-endian byte order.
- amount (length-prefixed UTF-8 string): the human-readable amount the address will receive including units, e.g. "0.025 BTC".
- address (length-prefixed string): the address where the coin purchase will be delivered.
The value of outputsHash is computed as the hash of the concatenation of the binary encodings of the requested outputs. The binary encoding of an output is the concatenation of the following fields:
- amount (8 bytes): 64-bit encoding of the amount of the requested output in little-endian byte order, expressed in the smallest unit of the given cryptocurrency (satoshis).
- address (length-prefixed string): the address of the requested output.
All variable-length fields are encoded the same way as in Bitcoin transactions, as a length-prefixed string, where the length is encoded as a variable-length CompactSize integer.
BIP-0021 specifies a URI scheme for encoding Bitcoin payment requests. The present specification defines a new query key slip24sig
for BIP-0021 URIs, allowing the URI to encode a basic Trezor payment request. If the slip24sig
field is specified in the URI the the amount
and label
fields MUST also be specified.
The value of slip24sig
is the base64 encoding of the SLIP-0024 payment request signature. Note that any =
characters in the base64 encoding must be percent-encoded as %3D
. When computing the hash of the payment request the recipientName is taken from thelabel
field of the URI. If the message
field is specified in the URI, then the message is processed as a text memo. The outputsHash value is the hash of exactly one output specified by the address
and amount
fields of the URI. A nonce is not used and the coinType is 0.
Use-cases involving multiple outputs or nonces require a more complex protocol than BIP-0021. For this purpose a JSON schema will be defined to document the data interchange format between the merchant and customer's wallet application. The same format may also be used for communicating payment requests between the merchant and the signing server.
TODO
This section is non-normative. One of the requirements in processing a refund memo or a coin purchase memo is that the customer's wallet must verify that the specified address is controlled by the wallet. The most common way of verifying address ownership by a wallet is to provide the BIP-0032 derivation path leading from the wallet's seed to the address. In cases where the address uses a script such as multisig, specifying the address derivation can be more complicated.
In order to simplify the communication protocol with the wallet, it is more convenient to verify address ownership by means of an address authentication code. When getting an address from the hardware wallet, the wallet can return an authentication code together with the address. This authentication code is saved by the calling application and used at a later point in time to prove to a stateless hardware wallet that the address was derived from its seed.
Let k be a secret address authentication key derived from the wallet's master secret using the SLIP-0021 method for hierarchical derivation of symmetric keys as:
k = Key(m/"SLIP-0024"/"Address MAC key")
The address authentication code is computed as:
mac = HMAC-SHA256(key = k, msg = coinType || address)
where coinType || address
is the concatenation of the following fields:
- coinType (4 bytes): 32-bit encoding of the SLIP-0044 coin type of the address in little-endian byte order.
- address (length-prefixed string): the address being authenticated.
TODO