This commit is contained in:
Michael de Hoog 2025-11-19 22:08:10 -05:00 committed by GitHub
commit 75d6ab19e8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 8093 additions and 3697 deletions

View file

@ -0,0 +1,106 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package usbwallet
import (
"fmt"
"regexp"
"strconv"
"strings"
"github.com/ethereum/go-ethereum/signer/core/apitypes"
)
type dataType byte
const (
CustomType dataType = iota
IntType
UintType
AddressType
BoolType
StringType
FixedBytesType
BytesType
)
var nameToType = map[string]dataType{
"int": IntType,
"uint": UintType,
"address": AddressType,
"bool": BoolType,
"string": StringType,
"bytes": BytesType,
}
func parseType(data apitypes.TypedData, field apitypes.Type) (dt dataType, name string, byteLength int, arrayLevels []*int, err error) {
name = strings.TrimSpace(field.Type)
arrayLengths := regexp.MustCompile(`\[(\d*)]`).FindAllStringSubmatch(name, -1)
if len(arrayLengths) > 0 {
arrayLevels = make([]*int, len(arrayLengths))
for i, arrayLength := range arrayLengths {
if len(arrayLength[1]) == 0 {
arrayLevels[i] = nil // nil means dynamic length
} else {
length, _ := strconv.Atoi(arrayLength[1]) // guaranteed to be a digit, ignore error
arrayLevels[i] = &length
}
}
name = name[0:strings.Index(name, "[")]
}
if data.Types[name] != nil {
dt = CustomType
return
}
matches := regexp.MustCompile(`^(.+?)(\d*)$`).FindStringSubmatch(name)
name = matches[1]
lengthStr := matches[2]
var ok bool
if dt, ok = nameToType[name]; !ok {
err = fmt.Errorf("unknown type: %s", field.Type)
return
}
byteLength, _ = strconv.Atoi(lengthStr)
if dt == UintType || dt == IntType {
if lengthStr == "" {
byteLength = 32
} else if byteLength%8 != 0 {
err = fmt.Errorf("invalid length for %s: %s", field.Type, lengthStr)
return
} else {
byteLength /= 8
}
} else if lengthStr != "" {
if dt == BytesType {
dt = FixedBytesType
} else {
err = fmt.Errorf("invalid type: %s", field.Type)
return
}
} else if dt == AddressType {
byteLength = 20 // address is always 20 bytes
}
if lengthStr != "" && (byteLength < 1 || byteLength > 32) {
err = fmt.Errorf("invalid length for %s: %s", field.Type, lengthStr)
return
}
return
}

View file

@ -26,7 +26,7 @@ import (
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
"github.com/karalabe/hid"
"github.com/karalabe/usb"
)
// LedgerScheme is the protocol scheme prefixing account and wallet URLs.
@ -105,7 +105,7 @@ func NewTrezorHubWithWebUSB() (*Hub, error) {
// newHub creates a new hardware wallet manager for generic USB devices.
func newHub(scheme string, vendorID uint16, productIDs []uint16, usageID uint16, endpointID int, makeDriver func(log.Logger) driver) (*Hub, error) {
if !hid.Supported() {
if !usb.Supported() {
return nil, errors.New("unsupported platform")
}
hub := &Hub{
@ -151,7 +151,7 @@ func (hub *Hub) refreshWallets() {
return
}
// Retrieve the current list of USB wallet devices
var devices []hid.DeviceInfo
var devices []usb.DeviceInfo
if runtime.GOOS == "linux" {
// hidapi on Linux opens the device during enumeration to retrieve some infos,
@ -166,7 +166,7 @@ func (hub *Hub) refreshWallets() {
return
}
}
infos, err := hid.Enumerate(hub.vendorID, 0)
infos, err := usb.Enumerate(hub.vendorID, 0)
if err != nil {
failcount := hub.enumFails.Add(1)
if runtime.GOOS == "linux" {

View file

@ -27,14 +27,18 @@ import (
"fmt"
"io"
"math/big"
"strconv"
"strings"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/signer/core/apitypes"
)
// ledgerOpcode is an enumeration encoding the supported Ledger opcodes.
@ -48,21 +52,60 @@ type ledgerParam1 byte
// specific opcodes. The same parameter values may be reused between opcodes.
type ledgerParam2 byte
// ledgerStatus is an enumeration encoding ledger status words.
type ledgerStatus uint16
const (
ledgerOpRetrieveAddress ledgerOpcode = 0x02 // Returns the public key and Ethereum address for a given BIP 32 path
ledgerOpSignTransaction ledgerOpcode = 0x04 // Signs an Ethereum transaction after having the user validate the parameters
ledgerOpGetConfiguration ledgerOpcode = 0x06 // Returns specific wallet application configuration
ledgerOpSignTypedMessage ledgerOpcode = 0x0c // Signs an Ethereum message following the EIP 712 specification
ledgerOpRetrieveAddress ledgerOpcode = 0x02 // Returns the public key and Ethereum address for a given BIP 32 path
ledgerOpSignTransaction ledgerOpcode = 0x04 // Signs an Ethereum transaction after having the user validate the parameters
ledgerOpGetConfiguration ledgerOpcode = 0x06 // Returns specific wallet application configuration
ledgerOpSignPersonalMessage ledgerOpcode = 0x08 // Signs a personal message following the EIP 712 specification
ledgerOpSignTypedMessage ledgerOpcode = 0x0c // Signs an Ethereum message following the EIP 712 specification
ledgerOpEip712SendStructDef ledgerOpcode = 0x1a // Sends EIP-712 struct types to the ledger
ledgerOpEip712SendStructImpl ledgerOpcode = 0x1c // Sends EIP-712 struct values to the ledger
ledgerP1DirectlyFetchAddress ledgerParam1 = 0x00 // Return address directly from the wallet
ledgerP1InitTypedMessageData ledgerParam1 = 0x00 // First chunk of Typed Message data
ledgerP1InitTransactionData ledgerParam1 = 0x00 // First transaction data block for signing
ledgerP1ContTransactionData ledgerParam1 = 0x80 // Subsequent transaction data block for signing
ledgerP1CompleteSend ledgerParam1 = 0x00 // Complete the value (in one go, or after partial)
ledgerP1PartialSend ledgerParam1 = 0x01 // Send partial data
ledgerP2DiscardAddressChainCode ledgerParam2 = 0x00 // Do not return the chain code along with the address
ledgerP2ProcessAndStartFlow ledgerParam2 = 0x00 // Process and start transaction signing flow
ledgerP2V0Implementation ledgerParam2 = 0x00 // EIP-712 V0 implementation (hashes only)
ledgerP2StructName ledgerParam2 = 0x00 // Send EIP-712 struct name
ledgerP2RootStruct ledgerParam2 = 0x00 // Send EIP-712 root struct
ledgerP2Array ledgerParam2 = 0x0f // Send EIP-712 array
ledgerP2StructField ledgerParam2 = 0xff // Send EIP-712 struct field
ledgerP2FullImplementation ledgerParam2 = 0x01 // EIP-712 full implementation (typed data)
ledgerEip155Size int = 3 // Size of the EIP-155 chain_id,r,s in unsigned transactions
ledgerStatusNormalEnd ledgerStatus = 0x9000
ledgerEip155Size int = 3 // Size of the EIP-155 chain_id,r,s in unsigned transactions
)
var ledgerStatuses = map[ledgerStatus]string{
0x5515: "Device is locked",
0x6001: "Mode check fail",
0x6501: "TransactionType not supported",
0x6502: "Output buffer too small for chainId conversion",
0x6511: "Ethereum app not open",
//0x68xx: "Internal error (Please report)",
0x6982: "Security status not satisfied (Canceled by user)",
0x6983: "Wrong Data length",
0x6984: "Plugin not installed",
0x6985: "Condition not satisfied",
0x6A00: "Error without info",
0x6A80: "Invalid data",
0x6A84: "Insufficient memory",
0x6A88: "Data not found",
0x6B00: "Incorrect parameter P1 or P2",
0x6D00: "Incorrect parameter INS",
0x6E00: "Incorrect parameter CLA",
0x6F00: "Incorrect parameter CLA",
0x6F01: "Technical problem (Internal error, please report)",
0x911C: "Command code not supported (i.e. Ledger-PKI not yet available)",
}
// errLedgerReplyInvalidHeader is the error message returned by a Ledger data exchange
// if the device replies with a mismatching header. This usually means the device
// is in browser mode.
@ -72,6 +115,10 @@ var errLedgerReplyInvalidHeader = errors.New("ledger: invalid reply header")
// when a response does arrive, but it does not contain the expected data.
var errLedgerInvalidVersionReply = errors.New("ledger: invalid version reply")
// errLedgerInvalidStatus is the error message returned if the ledger doesn't respond with a
// 0x9000 status.
var errLedgerInvalidStatus = errors.New("ledger: invalid status")
// ledgerDriver implements the communication with a Ledger hardware wallet.
type ledgerDriver struct {
device io.ReadWriter // USB device connection to communicate through
@ -119,7 +166,7 @@ func (w *ledgerDriver) Open(device io.ReadWriter, passphrase string) error {
_, err := w.ledgerDerive(accounts.DefaultBaseDerivationPath)
if err != nil {
// Ethereum app is not running or in browser mode, nothing more to do, return
if err == errLedgerReplyInvalidHeader {
if errors.Is(err, errLedgerReplyInvalidHeader) {
w.browser = true
}
return nil
@ -141,7 +188,7 @@ func (w *ledgerDriver) Close() error {
// Heartbeat implements usbwallet.driver, performing a sanity check against the
// Ledger to see if it's still online.
func (w *ledgerDriver) Heartbeat() error {
if _, err := w.ledgerVersion(); err != nil && err != errLedgerInvalidVersionReply {
if _, err := w.ledgerVersion(); err != nil && !errors.Is(err, errLedgerInvalidVersionReply) {
w.failure = err
return err
}
@ -166,7 +213,7 @@ func (w *ledgerDriver) SignTx(path accounts.DerivationPath, tx *types.Transactio
return common.Address{}, nil, accounts.ErrWalletClosed
}
// Ensure the wallet is capable of signing the given transaction
if chainID != nil && (w.version[0] < 1 || (w.version[0] == 1 && w.version[1] == 0 && w.version[2] < 3)) {
if chainID != nil && w.version[0] <= 1 && w.version[1] <= 0 && w.version[2] <= 2 {
//lint:ignore ST1005 brand name displayed on the console
return common.Address{}, nil, fmt.Errorf("Ledger v%d.%d.%d doesn't support signing this transaction, please update to v1.0.3 at least", w.version[0], w.version[1], w.version[2])
}
@ -174,11 +221,11 @@ func (w *ledgerDriver) SignTx(path accounts.DerivationPath, tx *types.Transactio
return w.ledgerSign(path, tx, chainID)
}
// SignTypedMessage implements usbwallet.driver, sending the message to the Ledger and
// SignTypedHash implements usbwallet.driver, sending the message to the Ledger and
// waiting for the user to sign or deny the transaction.
//
// Note: this was introduced in the ledger 1.5.0 firmware
func (w *ledgerDriver) SignTypedMessage(path accounts.DerivationPath, domainHash []byte, messageHash []byte) ([]byte, error) {
func (w *ledgerDriver) SignTypedHash(path accounts.DerivationPath, domainHash []byte, messageHash []byte) ([]byte, error) {
// If the Ethereum app doesn't run, abort
if w.offline() {
return nil, accounts.ErrWalletClosed
@ -189,7 +236,39 @@ func (w *ledgerDriver) SignTypedMessage(path accounts.DerivationPath, domainHash
return nil, fmt.Errorf("Ledger version >= 1.5.0 required for EIP-712 signing (found version v%d.%d.%d)", w.version[0], w.version[1], w.version[2])
}
// All infos gathered and metadata checks out, request signing
return w.ledgerSignTypedMessage(path, domainHash, messageHash)
return w.ledgerSignTypedHash(path, domainHash, messageHash)
}
// SignText implements usbwallet.driver, sending the message to the Ledger and
// waiting for the user to confirm or deny the signature.
func (w *ledgerDriver) SignText(path accounts.DerivationPath, text []byte) ([]byte, error) {
// If the Ethereum app doesn't run, abort
if w.offline() {
return nil, accounts.ErrWalletClosed
}
// Ensure the wallet is capable of signing the given transaction
if w.version[0] < 1 && w.version[1] < 5 {
//lint:ignore ST1005 brand name displayed on the console
return nil, fmt.Errorf("Ledger version >= 1.5.0 required for EIP-712 signing (found version v%d.%d.%d)", w.version[0], w.version[1], w.version[2])
}
// All infos gathered and metadata checks out, request signing
return w.ledgerSignPersonalMessage(path, text)
}
// SignedTypedData implements usbwallet.driver, sending the message to the Ledger and
// waiting for the user to sign or deny signing an EIP-712 typed data struct.
func (w *ledgerDriver) SignedTypedData(path accounts.DerivationPath, data apitypes.TypedData) ([]byte, error) {
// If the Ethereum app doesn't run, abort
if w.offline() {
return nil, accounts.ErrWalletClosed
}
// Ensure the wallet is capable of signing the given transaction
if w.version[0] < 1 && w.version[1] < 5 {
//lint:ignore ST1005 brand name displayed on the console
return nil, fmt.Errorf("Ledger version >= 1.5.0 required for EIP-712 signing (found version v%d.%d.%d)", w.version[0], w.version[1], w.version[2])
}
// All infos gathered and metadata checks out, request signing
return w.ledgerSignTypedData(path, data)
}
// ledgerVersion retrieves the current version of the Ethereum wallet app running
@ -360,7 +439,7 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
// Send the request and wait for the response
var (
op = ledgerP1InitTransactionData
p1 = ledgerP1InitTransactionData
reply []byte
)
@ -378,13 +457,13 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
chunk = len(payload)
}
// Send the chunk over, ensuring it's processed correctly
reply, err = w.ledgerExchange(ledgerOpSignTransaction, op, 0, payload[:chunk])
reply, err = w.ledgerExchange(ledgerOpSignTransaction, p1, ledgerP2ProcessAndStartFlow, payload[:chunk])
if err != nil {
return common.Address{}, nil, err
}
// Shift the payload and ensure subsequent chunks are marked as such
payload = payload[chunk:]
op = ledgerP1ContTransactionData
p1 = ledgerP1ContTransactionData
}
// Extract the Ethereum signature and do a sanity validation
if len(reply) != crypto.SignatureLength {
@ -414,7 +493,7 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
return sender, signed, nil
}
// ledgerSignTypedMessage sends the transaction to the Ledger wallet, and waits for the user
// ledgerSignTypedHash sends the transaction to the Ledger wallet, and waits for the user
// to confirm or deny the transaction.
//
// The signing protocol is defined as follows:
@ -441,7 +520,7 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
// signature V | 1 byte
// signature R | 32 bytes
// signature S | 32 bytes
func (w *ledgerDriver) ledgerSignTypedMessage(derivationPath []uint32, domainHash []byte, messageHash []byte) ([]byte, error) {
func (w *ledgerDriver) ledgerSignTypedHash(derivationPath []uint32, domainHash []byte, messageHash []byte) ([]byte, error) {
// Flatten the derivation path into the Ledger request
path := make([]byte, 1+4*len(derivationPath))
path[0] = byte(len(derivationPath))
@ -454,13 +533,12 @@ func (w *ledgerDriver) ledgerSignTypedMessage(derivationPath []uint32, domainHas
// Send the request and wait for the response
var (
op = ledgerP1InitTypedMessageData
reply []byte
err error
)
// Send the message over, ensuring it's processed correctly
reply, err = w.ledgerExchange(ledgerOpSignTypedMessage, op, 0, payload)
reply, err = w.ledgerExchange(ledgerOpSignTypedMessage, ledgerP1InitTypedMessageData, ledgerP2V0Implementation, payload)
if err != nil {
return nil, err
@ -474,6 +552,281 @@ func (w *ledgerDriver) ledgerSignTypedMessage(derivationPath []uint32, domainHas
return signature, nil
}
// ledgerSignPersonalMessage sends the transaction to the Ledger wallet, and waits for the user
// to confirm or deny the transaction.
//
// The signing protocol is defined as follows:
//
// CLA | INS | P1 | P2 | Lc | Le
// ----+-----+----+-----------------------------+-----+---
// E0 | 08 | 00 | implementation version : 00 | variable | variable
//
// Where the input is:
//
// Description | Length
// -------------------------------------------------+----------
// Number of BIP 32 derivations to perform (max 10) | 1 byte
// First derivation index (big endian) | 4 bytes
// ... | 4 bytes
// Last derivation index (big endian) | 4 bytes
// text | arbitrary
//
// And the output data is:
//
// Description | Length
// ------------+---------
// signature V | 1 byte
// signature R | 32 bytes
// signature S | 32 bytes
func (w *ledgerDriver) ledgerSignPersonalMessage(derivationPath []uint32, text []byte) ([]byte, error) {
// Flatten the derivation path into the Ledger request
path := make([]byte, 5+4*len(derivationPath))
path[0] = byte(len(derivationPath))
for i, component := range derivationPath {
binary.BigEndian.PutUint32(path[1+4*i:], component)
}
binary.BigEndian.PutUint32(path[1+4*len(derivationPath):], uint32(len(text)))
// Create the 712 message
payload := append(path, text...)
// Send the request and wait for the response
var (
reply []byte
err error
)
// Send the message over, ensuring it's processed correctly
reply, err = w.ledgerExchange(ledgerOpSignPersonalMessage, ledgerP1InitTransactionData, 0, payload)
if err != nil {
return nil, err
}
// Extract the Ethereum signature and do a sanity validation
if len(reply) != crypto.SignatureLength {
return nil, errors.New("reply lacks signature")
}
signature := append(reply[1:], reply[0])
return signature, nil
}
// ledgerSignTypedData sends the transaction to the Ledger wallet, and waits for the user
// to confirm or deny the transaction.
//
// The typed data struct fields and values need to be sent to the Ledger first.
// See https://github.com/LedgerHQ/app-ethereum/blob/develop/doc/eip712.md.
//
// After the data is sent, the signing protocol is defined as follows:
//
// CLA | INS | P1 | P2 | Lc | Le
// ----+-----+----+-----------------------------+-----+---
// E0 | 0C | 00 | implementation version : 00 | variable | variable
//
// Where the input is:
//
// Description | Length
// -------------------------------------------------+----------
// Number of BIP 32 derivations to perform (max 10) | 1 byte
// First derivation index (big endian) | 4 bytes
// ... | 4 bytes
// Last derivation index (big endian) | 4 bytes
//
// And the output data is:
//
// Description | Length
// ------------+---------
// signature V | 1 byte
// signature R | 32 bytes
// signature S | 32 bytes
func (w *ledgerDriver) ledgerSignTypedData(derivationPath []uint32, data apitypes.TypedData) ([]byte, error) {
// Check if the EIP712Domain and primary type are present in the data
domainStruct := data.Types["EIP712Domain"]
if domainStruct == nil {
return nil, fmt.Errorf("EIP712Domain type is required")
}
primaryType := data.Types[data.PrimaryType]
if primaryType == nil {
return nil, fmt.Errorf("primary type %s not found in types", data.PrimaryType)
}
// sendField is a function for sending an EIP-712 struct field name + type
sendField := func(field apitypes.Type) error {
dt, name, byteLength, arrays, err := parseType(data, field)
if err != nil {
return err
}
typeDesc := byte(dt)
var typeName []byte
var typeSize []byte
switch dt {
case CustomType:
typeName = append([]byte{byte(len(name))}, []byte(name)...)
case IntType, UintType, FixedBytesType:
typeSize = []byte{byte(byteLength)}
typeDesc |= 0x40
default:
}
var arrayLevels []byte
if len(arrays) > 0 {
typeDesc |= 0x80
arrayLevels = []byte{byte(len(arrays))}
for _, length := range arrays {
if length == nil {
arrayLevels = append(arrayLevels, 0)
} else {
arrayLevels = append(arrayLevels, 1, byte(*length))
}
}
}
payload := []byte{typeDesc}
payload = append(payload, typeName...)
payload = append(payload, typeSize...)
payload = append(payload, arrayLevels...)
payload = append(payload, byte(len(field.Name)))
payload = append(payload, []byte(field.Name)...)
_, err = w.ledgerExchange(ledgerOpEip712SendStructDef, 0, ledgerP2StructField, payload)
return err
}
// sendValue is a recursive function that sends the value of a field
var sendValue func(t, name string, value interface{}) error
sendValue = func(t, name string, value interface{}) error {
if value == nil {
return fmt.Errorf("nil value for field %s", name)
}
if strings.HasSuffix(t, "]") {
a, ok := value.([]interface{})
if !ok {
return fmt.Errorf("expected array for field %s, got %T", name, value)
}
if _, err := w.ledgerExchange(ledgerOpEip712SendStructImpl, ledgerP1CompleteSend, ledgerP2Array, []byte{byte(len(a))}); err != nil {
return fmt.Errorf("failed to send array length: %w", err)
}
t = t[:strings.LastIndex(t, "[")]
for _, item := range a {
if err := sendValue(t, name, item); err != nil {
return fmt.Errorf("failed to send array item: %w", err)
}
}
return nil
}
s := data.Types[t]
if s != nil {
m, ok := value.(map[string]interface{})
if !ok {
return fmt.Errorf("expected struct for field %s, got %T", name, value)
}
for _, field := range s {
if err := sendValue(field.Type, field.Name, m[field.Name]); err != nil {
return fmt.Errorf("failed to send struct field %s: %w", field.Name, err)
}
}
return nil
}
var enc []byte
var err error
switch v := value.(type) {
case string:
if t == "string" {
enc = []byte(v)
} else if strings.HasPrefix(v, "0x") {
enc, err = hex.DecodeString(v[2:])
if err != nil {
return fmt.Errorf("failed to decode hex string for field %s: %w", name, err)
}
} else {
return fmt.Errorf("invalid string value for field %s: %s", name, v)
}
case bool:
if v {
enc = []byte{1}
} else {
enc = []byte{0}
}
case float64:
enc = new(big.Int).SetInt64(int64(v)).Bytes()
case *math.HexOrDecimal256:
if v == nil {
return fmt.Errorf("nil value for field %s", name)
}
h := big.Int(*v)
enc = (&h).Bytes()
default:
return fmt.Errorf("unsupported type for field %s: %T", name, value)
}
chunk := 255
payload := binary.BigEndian.AppendUint16([]byte{}, uint16(len(enc)))
payload = append(payload, enc...)
for len(payload) > 0 {
p1 := ledgerP1PartialSend
if chunk >= len(payload) {
chunk = len(payload)
p1 = ledgerP1CompleteSend
}
if _, err := w.ledgerExchange(ledgerOpEip712SendStructImpl, p1, ledgerP2StructField, payload[:chunk]); err != nil {
return fmt.Errorf("failed to send field %s: %w", name, err)
}
payload = payload[chunk:]
}
return nil
}
// first send all the EIP-712 struct definitions
for name, fields := range data.Types {
_, err := w.ledgerExchange(ledgerOpEip712SendStructDef, 0, ledgerP2StructName, []byte(name))
if err != nil {
return nil, fmt.Errorf("failed to send type name %s: %w", name, err)
}
for _, field := range fields {
if err := sendField(field); err != nil {
return nil, fmt.Errorf("failed to send field %s: %w", field.Name, err)
}
}
}
// send the EIP-712 domain field values
if _, err := w.ledgerExchange(ledgerOpEip712SendStructImpl, ledgerP1CompleteSend, ledgerP2RootStruct, []byte("EIP712Domain")); err != nil {
return nil, fmt.Errorf("failed to send domain type name: %w", err)
}
if err := sendValue("EIP712Domain", "domain", data.Domain.Map()); err != nil {
return nil, fmt.Errorf("failed to send domain fields: %w", err)
}
// send the message field values
if _, err := w.ledgerExchange(ledgerOpEip712SendStructImpl, ledgerP1CompleteSend, ledgerP2RootStruct, []byte(data.PrimaryType)); err != nil {
return nil, fmt.Errorf("failed to send primary type name: %w", err)
}
if err := sendValue(data.PrimaryType, "message", data.Message); err != nil {
return nil, fmt.Errorf("failed to send primary type fields: %w", err)
}
// Flatten the derivation path into the Ledger request
path := make([]byte, 1+4*len(derivationPath))
path[0] = byte(len(derivationPath))
for i, component := range derivationPath {
binary.BigEndian.PutUint32(path[1+4*i:], component)
}
// Send the message over, ensuring it's processed correctly
reply, err := w.ledgerExchange(ledgerOpSignTypedMessage, 0, ledgerP2FullImplementation, path)
if err != nil {
return nil, err
}
// Extract the Ethereum signature and do a sanity validation
if len(reply) != crypto.SignatureLength {
return nil, fmt.Errorf("invalid signature length: %d", len(reply))
}
signature := append(reply[1:], reply[0])
return signature, nil
}
// ledgerExchange performs a data exchange with the Ledger wallet, sending it a
// message and retrieving the response.
//
@ -508,6 +861,16 @@ func (w *ledgerDriver) ledgerSignTypedMessage(derivationPath []uint32, domainHas
// APDU length | 1 byte
// Optional APDU data | arbitrary
func (w *ledgerDriver) ledgerExchange(opcode ledgerOpcode, p1 ledgerParam1, p2 ledgerParam2, data []byte) ([]byte, error) {
for i := 1; ; i++ {
res, err := w._ledgerExchange(opcode, p1, p2, data)
// on failure, try the exchange 3 times in total
if err == nil || i == 3 {
return res, err
}
}
}
func (w *ledgerDriver) _ledgerExchange(opcode ledgerOpcode, p1 ledgerParam1, p2 ledgerParam2, data []byte) ([]byte, error) {
// Construct the message payload, possibly split into multiple chunks
apdu := make([]byte, 2, 7+len(data))
@ -569,5 +932,16 @@ func (w *ledgerDriver) ledgerExchange(opcode ledgerOpcode, p1 ledgerParam1, p2 l
break
}
}
if len(reply) < 2 {
return nil, errLedgerInvalidStatus
}
status := ledgerStatus(binary.BigEndian.Uint16(reply[len(reply)-2:]))
if status != ledgerStatusNormalEnd {
s := ledgerStatuses[status]
if s == "" {
s = "unknown error"
}
return nil, fmt.Errorf("%w: 0x%s (%s)", errLedgerInvalidStatus, strconv.FormatUint(uint64(status), 16), s)
}
return reply[:len(reply)-2], nil
}

View file

@ -27,6 +27,7 @@ import (
"io"
"math"
"math/big"
"reflect"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/usbwallet/trezor"
@ -34,31 +35,34 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/signer/core/apitypes"
pin "github.com/reserve-protocol/trezor"
"google.golang.org/protobuf/proto"
)
// ErrTrezorPINNeeded is returned if opening the trezor requires a PIN code. In
// this case, the calling application should display a pinpad and send back the
// encoded passphrase.
var ErrTrezorPINNeeded = errors.New("trezor: pin needed")
// ErrTrezorPassphraseNeeded is returned if opening the trezor requires a passphrase
var ErrTrezorPassphraseNeeded = errors.New("trezor: passphrase needed")
// errTrezorReplyInvalidHeader is the error message returned by a Trezor data exchange
// if the device replies with a mismatching header. This usually means the device
// is in browser mode.
var errTrezorReplyInvalidHeader = errors.New("trezor: invalid reply header")
type TrezorFailure struct {
*trezor.Failure
}
// Error implements the error interface for the TrezorFailure type, returning
// a formatted error message containing the failure reason.
func (f *TrezorFailure) Error() string {
return fmt.Sprintf("trezor: %s", f.GetMessage())
}
// trezorDriver implements the communication with a Trezor hardware wallet.
type trezorDriver struct {
device io.ReadWriter // USB device connection to communicate through
version [3]uint32 // Current version of the Trezor firmware
label string // Current textual label of the Trezor device
pinwait bool // Flags whether the device is waiting for PIN entry
passphrasewait bool // Flags whether the device is waiting for passphrase entry
failure error // Any failure that would make the device unusable
log log.Logger // Contextual logger to tag the trezor with its id
device io.ReadWriter // USB device connection to communicate through
version [3]uint32 // Current version of the Trezor firmware
label string // Current textual label of the Trezor device
passphrase string
failure error // Any failure that would make the device unusable
log log.Logger // Contextual logger to tag the trezor with its id
}
// newTrezorDriver creates a new instance of a Trezor USB protocol driver.
@ -77,87 +81,32 @@ func (w *trezorDriver) Status() (string, error) {
if w.device == nil {
return "Closed", w.failure
}
if w.pinwait {
return fmt.Sprintf("Trezor v%d.%d.%d '%s' waiting for PIN", w.version[0], w.version[1], w.version[2], w.label), w.failure
}
return fmt.Sprintf("Trezor v%d.%d.%d '%s' online", w.version[0], w.version[1], w.version[2], w.label), w.failure
}
// Open implements usbwallet.driver, attempting to initialize the connection to
// the Trezor hardware wallet. Initializing the Trezor is a two or three phase operation:
// - The first phase is to initialize the connection and read the wallet's
// features. This phase is invoked if the provided passphrase is empty. The
// device will display the pinpad as a result and will return an appropriate
// error to notify the user that a second open phase is needed.
// - The second phase is to unlock access to the Trezor, which is done by the
// user actually providing a passphrase mapping a keyboard keypad to the pin
// number of the user (shuffled according to the pinpad displayed).
// - If needed the device will ask for passphrase which will require calling
// open again with the actual passphrase (3rd phase)
// the Trezor hardware wallet.
func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error {
w.device, w.failure = device, nil
w.device, w.passphrase, w.failure = device, passphrase, nil
// If phase 1 is requested, init the connection and wait for user callback
if passphrase == "" && !w.passphrasewait {
// If we're already waiting for a PIN entry, insta-return
if w.pinwait {
return ErrTrezorPINNeeded
}
// Initialize a connection to the device
features := new(trezor.Features)
if _, err := w.trezorExchange(&trezor.Initialize{}, features); err != nil {
return err
}
w.version = [3]uint32{features.GetMajorVersion(), features.GetMinorVersion(), features.GetPatchVersion()}
w.label = features.GetLabel()
// Do a manual ping, forcing the device to ask for its PIN and Passphrase
askPin := true
askPassphrase := true
res, err := w.trezorExchange(&trezor.Ping{PinProtection: &askPin, PassphraseProtection: &askPassphrase}, new(trezor.PinMatrixRequest), new(trezor.PassphraseRequest), new(trezor.Success))
if err != nil {
return err
}
// Only return the PIN request if the device wasn't unlocked until now
switch res {
case 0:
w.pinwait = true
return ErrTrezorPINNeeded
case 1:
w.pinwait = false
w.passphrasewait = true
return ErrTrezorPassphraseNeeded
case 2:
return nil // responded with trezor.Success
}
}
// Phase 2 requested with actual PIN entry
if w.pinwait {
w.pinwait = false
res, err := w.trezorExchange(&trezor.PinMatrixAck{Pin: &passphrase}, new(trezor.Success), new(trezor.PassphraseRequest))
if err != nil {
w.failure = err
return err
}
if res == 1 {
w.passphrasewait = true
return ErrTrezorPassphraseNeeded
}
} else if w.passphrasewait {
w.passphrasewait = false
if _, err := w.trezorExchange(&trezor.PassphraseAck{Passphrase: &passphrase}, new(trezor.Success)); err != nil {
w.failure = err
return err
}
if _, err := w.trezorExchange(&trezor.EndSession{}, new(trezor.Success)); err != nil {
return err
}
return nil
features := new(trezor.Features)
if _, err := w.trezorExchange(&trezor.Initialize{}, features); err != nil {
return err
}
w.version = [3]uint32{features.GetMajorVersion(), features.GetMinorVersion(), features.GetPatchVersion()}
w.label = features.GetLabel()
return w.Heartbeat()
}
// Close implements usbwallet.driver, cleaning up and metadata maintained within
// the Trezor driver.
func (w *trezorDriver) Close() error {
w.version, w.label, w.pinwait = [3]uint32{}, "", false
w.version, w.label = [3]uint32{}, ""
return nil
}
@ -186,8 +135,236 @@ func (w *trezorDriver) SignTx(path accounts.DerivationPath, tx *types.Transactio
return w.trezorSign(path, tx, chainID)
}
func (w *trezorDriver) SignTypedMessage(path accounts.DerivationPath, domainHash []byte, messageHash []byte) ([]byte, error) {
return nil, accounts.ErrNotSupported
func (w *trezorDriver) SignTypedHash(path accounts.DerivationPath, domainHash []byte, messageHash []byte) ([]byte, error) {
if w.device == nil {
return nil, accounts.ErrWalletClosed
}
response := new(trezor.EthereumTypedDataSignature)
_, err := w.trezorExchange(&trezor.EthereumSignTypedHash{
AddressN: path,
DomainSeparatorHash: domainHash,
MessageHash: messageHash,
}, response)
if err != nil {
return nil, err
}
return response.Signature, nil
}
func (w *trezorDriver) SignText(path accounts.DerivationPath, text []byte) ([]byte, error) {
if w.device == nil {
return nil, accounts.ErrWalletClosed
}
response := new(trezor.EthereumMessageSignature)
_, err := w.trezorExchange(&trezor.EthereumSignMessage{
AddressN: path,
Message: text,
}, response)
if err != nil {
return nil, err
}
return response.Signature, nil
}
func (w *trezorDriver) SignedTypedData(path accounts.DerivationPath, data apitypes.TypedData) ([]byte, error) {
if w.device == nil {
return nil, accounts.ErrWalletClosed
}
_, hashes, err := apitypes.TypedDataAndHash(data)
if err != nil {
return nil, fmt.Errorf("trezor: error hashing typed data: %w", err)
}
domainHash, messageHash := hashes[2:34], hashes[34:66]
if w.version[0] == 1 {
// legacy Trezor devices don't support typed data; fallback to hash signing:
return w.SignTypedHash(path, []byte(domainHash), []byte(messageHash))
}
if w.version[0] == 2 && (w.version[1] < 9 || (w.version[1] == 9 && w.version[2] == 0)) {
// ShowMessageHash was introduced in Trezor firmware v2.9.1
return nil, fmt.Errorf("trezor: typed data signing requires firmware v2.9.1 or newer")
}
signature := new(trezor.EthereumTypedDataSignature)
structRequest := new(trezor.EthereumTypedDataStructRequest)
valueRequest := new(trezor.EthereumTypedDataValueRequest)
var req proto.Message = &trezor.EthereumSignTypedData{
AddressN: path,
PrimaryType: &data.PrimaryType,
ShowMessageHash: []byte(messageHash),
}
nestedArray := false
for {
n, err := w.trezorExchange(req, signature, structRequest, valueRequest)
if err != nil {
var trezorFailure *TrezorFailure
if nestedArray && errors.As(err, &trezorFailure) &&
trezorFailure.Code != nil && *trezorFailure.Code == trezor.Failure_Failure_FirmwareError {
return nil, fmt.Errorf("trezor: nested arrays are not supported by this firmware version: %w", err)
}
return nil, err
}
nestedArray = false
switch n {
case 0:
// No additional data needed, return the signature
return signature.Signature, nil
case 1:
fields := data.Types[structRequest.GetName()]
if len(fields) == 0 {
return nil, fmt.Errorf("trezor: no fields for struct %s", structRequest.GetName())
}
ack := &trezor.EthereumTypedDataStructAck{
Members: make([]*trezor.EthereumTypedDataStructAck_EthereumStructMember, len(fields)),
}
for i, field := range fields {
dt, name, byteLength, arrays, err := parseType(data, field)
if err != nil {
return nil, err
}
ubyteLength := uint32(byteLength)
t := &trezor.EthereumTypedDataStructAck_EthereumFieldType{}
inner := t
for i := len(arrays) - 1; i >= 0; i-- {
dataType := trezor.EthereumTypedDataStructAck_ARRAY
inner.DataType = &dataType
if arrays[i] != nil {
length := uint32(*arrays[i])
inner.Size = &length
}
inner.EntryType = &trezor.EthereumTypedDataStructAck_EthereumFieldType{}
inner = inner.EntryType
}
var dataType trezor.EthereumTypedDataStructAck_EthereumDataType
switch dt {
case CustomType:
inner.StructName = &name
dataType = trezor.EthereumTypedDataStructAck_STRUCT
members := uint32(len(data.Types[name]))
inner.Size = &members
case IntType:
dataType = trezor.EthereumTypedDataStructAck_INT
inner.Size = &ubyteLength
case UintType:
dataType = trezor.EthereumTypedDataStructAck_UINT
inner.Size = &ubyteLength
case AddressType:
dataType = trezor.EthereumTypedDataStructAck_ADDRESS
case BoolType:
dataType = trezor.EthereumTypedDataStructAck_BOOL
case StringType:
dataType = trezor.EthereumTypedDataStructAck_STRING
case FixedBytesType:
dataType = trezor.EthereumTypedDataStructAck_BYTES
inner.Size = &ubyteLength
case BytesType:
dataType = trezor.EthereumTypedDataStructAck_BYTES
}
inner.DataType = &dataType
ack.Members[i] = &trezor.EthereumTypedDataStructAck_EthereumStructMember{
Name: &field.Name,
Type: t,
}
}
req = ack
case 2:
structType := data.Types[data.PrimaryType]
structValue := data.Message
if valueRequest.MemberPath[0] == 0 {
// populate with domain info
structType = data.Types["EIP712Domain"]
structValue = data.Domain.Map()
}
var value []byte
for i := 1; i < len(valueRequest.MemberPath); i++ {
p := valueRequest.MemberPath[i]
if structType == nil {
return nil, fmt.Errorf("trezor: no struct type for path %v", path)
}
if int(p) >= len(structType) {
return nil, fmt.Errorf("trezor: invalid field index %d for struct %s", p, structRequest.GetName())
}
field := structType[p]
nextValue := structValue[field.Name]
dt, name, byteLength, arrays, err := parseType(data, field)
if err != nil {
return nil, err
}
if len(arrays) > 1 {
nestedArray = true
}
for j := 0; j < len(arrays) && i < len(valueRequest.MemberPath)-1; i, j = i+1, j+1 {
k := reflect.TypeOf(nextValue).Kind()
if !(k == reflect.Array || k == reflect.Slice) {
return nil, fmt.Errorf("trezor: expected array at path %v, got %T", valueRequest.MemberPath[:i+1], nextValue)
}
a := reflect.ValueOf(nextValue)
p = valueRequest.MemberPath[i+1]
if int(p) >= a.Len() {
return nil, fmt.Errorf("trezor: invalid array index %d for path %v", p, valueRequest.MemberPath[:i+1])
}
nextValue = a.Index(int(p)).Interface()
}
k := reflect.TypeOf(nextValue).Kind()
if i < len(valueRequest.MemberPath)-1 {
if reflect.TypeOf(nextValue).Kind() != reflect.Map {
return nil, fmt.Errorf("trezor: expected map at path %v, got %T", valueRequest.MemberPath[:i+1], nextValue)
}
structType = data.Types[name]
structValue = nextValue.(apitypes.TypedDataMessage)
} else if k == reflect.Array || k == reflect.Slice {
// Array value, return length as uint16
value = binary.BigEndian.AppendUint16([]byte{}, uint16(reflect.ValueOf(nextValue).Len()))
} else {
// Last value, encode it as a primitive value
switch dt {
case CustomType:
return nil, fmt.Errorf("trezor: cannot encode custom type %s at path %v", name, valueRequest.MemberPath[:i+1])
case IntType, UintType, AddressType, FixedBytesType:
if str, ok := nextValue.(string); ok {
value = common.FromHex(str)
} else if f, ok := nextValue.(float64); ok {
value = new(big.Int).SetInt64(int64(f)).Bytes()
}
if len(value) > byteLength {
return nil, fmt.Errorf("trezor: value at path %v is too long (%d bytes, expected %d)", valueRequest.MemberPath[:i+1], len(value), byteLength)
}
for len(value) < byteLength {
value = append([]byte{0}, value...)
}
case BoolType:
if b, ok := nextValue.(bool); ok {
if b {
value = []byte{1}
} else {
value = []byte{0}
}
} else {
return nil, fmt.Errorf("trezor: expected bool at path %v, got %T", valueRequest.MemberPath[:i+1], nextValue)
}
case StringType:
if str, ok := nextValue.(string); ok {
value = []byte(str)
} else {
return nil, fmt.Errorf("trezor: expected string at path %v, got %T", valueRequest.MemberPath[:i+1], nextValue)
}
case BytesType:
if str, ok := nextValue.(string); ok {
value = common.FromHex(str)
} else {
return nil, fmt.Errorf("trezor: expected bytes at path %v, got %T", valueRequest.MemberPath[:i+1], nextValue)
}
}
}
}
req = &trezor.EthereumTypedDataValueAck{
Value: value,
}
default:
return nil, fmt.Errorf("trezor: unexpected reply index %d", n)
}
}
}
// trezorDerive sends a derivation request to the Trezor device and returns the
@ -197,10 +374,7 @@ func (w *trezorDriver) trezorDerive(derivationPath []uint32) (common.Address, er
if _, err := w.trezorExchange(&trezor.EthereumGetAddress{AddressN: derivationPath}, address); err != nil {
return common.Address{}, err
}
if addr := address.GetAddressBin(); len(addr) > 0 { // Older firmwares use binary formats
return common.BytesToAddress(addr), nil
}
if addr := address.GetAddressHex(); len(addr) > 0 { // Newer firmwares use hexadecimal formats
if addr := address.GetAddress(); len(addr) > 0 {
return common.HexToAddress(addr), nil
}
return common.Address{}, errors.New("missing derived address")
@ -224,8 +398,7 @@ func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction
if to := tx.To(); to != nil {
// Non contract deploy, set recipient explicitly
hex := to.Hex()
request.ToHex = &hex // Newer firmwares (old will ignore)
request.ToBin = (*to)[:] // Older firmwares (new will ignore)
request.To = &hex
}
if length > 1024 { // Send the data chunked if that was requested
request.DataInitialChunk, data = data[:1024], data[1024:]
@ -233,7 +406,7 @@ func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction
request.DataInitialChunk, data = data, nil
}
if chainID != nil { // EIP-155 transaction, set chain ID explicitly (only 32 bit is supported!?)
id := uint32(chainID.Int64())
id := chainID.Uint64()
request.ChainId = &id
}
// Send the initiation message and stream content until a signature is returned
@ -250,11 +423,7 @@ func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction
}
}
// Extract the Ethereum signature and do a sanity validation
if len(response.GetSignatureR()) == 0 || len(response.GetSignatureS()) == 0 {
return common.Address{}, nil, errors.New("reply lacks signature")
} else if response.GetSignatureV() == 0 && int(chainID.Int64()) <= (math.MaxUint32-36)/2 {
// for chainId >= (MaxUint32-36)/2, Trezor returns signature bit only
// https://github.com/trezor/trezor-mcu/pull/399
if len(response.GetSignatureR()) == 0 || len(response.GetSignatureS()) == 0 || response.GetSignatureV() == 0 {
return common.Address{}, nil, errors.New("reply lacks signature")
}
signature := append(append(response.GetSignatureR(), response.GetSignatureS()...), byte(response.GetSignatureV()))
@ -361,12 +530,22 @@ func (w *trezorDriver) trezorExchange(req proto.Message, results ...proto.Messag
if err := proto.Unmarshal(reply, failure); err != nil {
return 0, err
}
return 0, errors.New("trezor: " + failure.GetMessage())
return 0, &TrezorFailure{Failure: failure}
}
if kind == uint16(trezor.MessageType_MessageType_ButtonRequest) {
// Trezor is waiting for user confirmation, ack and wait for the next message
return w.trezorExchange(&trezor.ButtonAck{}, results...)
}
if kind == uint16(trezor.MessageType_MessageType_PinMatrixRequest) {
p, err := pin.GetPIN("Please enter your Trezor PIN")
if err != nil {
return 0, err
}
return w.trezorExchange(&trezor.PinMatrixAck{Pin: &p}, results...)
}
if kind == uint16(trezor.MessageType_MessageType_PassphraseRequest) {
return w.trezorExchange(&trezor.PassphraseAck{Passphrase: &w.passphrase}, results...)
}
for i, res := range results {
if trezor.Type(res) == kind {
return i, proto.Unmarshal(reply, res)

File diff suppressed because it is too large Load diff

View file

@ -1,18 +1,26 @@
// This file originates from the SatoshiLabs Trezor `common` repository at:
// https://github.com/trezor/trezor-common/blob/master/protob/messages-common.proto
// dated 28.05.2019, commit 893fd219d4a01bcffa0cd9cfa631856371ec5aa9.
// dated 30.06.2025, commit 421b45d4677f2499234692b0f54010bc45b3ae5f.
syntax = "proto2";
package hw.trezor.messages.common;
option go_package = "github.com/ethereum/go-ethereum/accounts/usbwallet/trezor";
// Sugar for easier handling in Java
option java_package = "com.satoshilabs.trezor.lib.protobuf";
option java_outer_classname = "TrezorMessageCommon";
import "options.proto";
option (include_in_bitcoin_only) = true;
/**
* Response: Success of the previous request
* @end
*/
message Success {
optional string message = 1; // human readable description of action or request-specific payload
optional string message = 1 [default=""]; // human readable description of action or request-specific payload
}
/**
@ -20,23 +28,29 @@ message Success {
* @end
*/
message Failure {
optional FailureType code = 1; // computer-readable definition of the error state
optional string message = 2; // human-readable message of the error state
enum FailureType {
Failure_UnexpectedMessage = 1;
Failure_ButtonExpected = 2;
Failure_DataError = 3;
Failure_ActionCancelled = 4;
Failure_PinExpected = 5;
Failure_PinCancelled = 6;
Failure_PinInvalid = 7;
Failure_InvalidSignature = 8;
Failure_ProcessError = 9;
Failure_NotEnoughFunds = 10;
Failure_NotInitialized = 11;
Failure_PinMismatch = 12;
Failure_FirmwareError = 99;
}
optional FailureType code = 1; // computer-readable definition of the error state
optional string message = 2; // human-readable message of the error state
enum FailureType {
Failure_UnexpectedMessage = 1;
Failure_ButtonExpected = 2;
Failure_DataError = 3;
Failure_ActionCancelled = 4;
Failure_PinExpected = 5;
Failure_PinCancelled = 6;
Failure_PinInvalid = 7;
Failure_InvalidSignature = 8;
Failure_ProcessError = 9;
Failure_NotEnoughFunds = 10;
Failure_NotInitialized = 11;
Failure_PinMismatch = 12;
Failure_WipeCodeMismatch = 13;
Failure_InvalidSession = 14;
Failure_Busy = 15;
Failure_ThpUnallocatedSession = 16;
Failure_InvalidProtocol = 17;
Failure_BufferError = 18;
Failure_FirmwareError = 99;
}
}
/**
@ -45,28 +59,39 @@ message Failure {
* @next ButtonAck
*/
message ButtonRequest {
optional ButtonRequestType code = 1;
optional string data = 2;
/**
* Type of button request
*/
enum ButtonRequestType {
ButtonRequest_Other = 1;
ButtonRequest_FeeOverThreshold = 2;
ButtonRequest_ConfirmOutput = 3;
ButtonRequest_ResetDevice = 4;
ButtonRequest_ConfirmWord = 5;
ButtonRequest_WipeDevice = 6;
ButtonRequest_ProtectCall = 7;
ButtonRequest_SignTx = 8;
ButtonRequest_FirmwareCheck = 9;
ButtonRequest_Address = 10;
ButtonRequest_PublicKey = 11;
ButtonRequest_MnemonicWordCount = 12;
ButtonRequest_MnemonicInput = 13;
ButtonRequest_PassphraseType = 14;
ButtonRequest_UnknownDerivationPath = 15;
}
optional ButtonRequestType code = 1; // enum identifier of the screen (deprecated)
optional uint32 pages = 2; // if the screen is paginated, number of pages
// this existed briefly: https://github.com/trezor/trezor-firmware/commit/1012ee8497b241e8ca559e386d936fa549bc0357
reserved 3;
optional string name = 4; // name of the screen
/**
* Type of button request
*/
enum ButtonRequestType {
ButtonRequest_Other = 1;
ButtonRequest_FeeOverThreshold = 2;
ButtonRequest_ConfirmOutput = 3;
ButtonRequest_ResetDevice = 4;
ButtonRequest_ConfirmWord = 5;
ButtonRequest_WipeDevice = 6;
ButtonRequest_ProtectCall = 7;
ButtonRequest_SignTx = 8;
ButtonRequest_FirmwareCheck = 9;
ButtonRequest_Address = 10;
ButtonRequest_PublicKey = 11;
ButtonRequest_MnemonicWordCount = 12;
ButtonRequest_MnemonicInput = 13;
_Deprecated_ButtonRequest_PassphraseType = 14 [deprecated=true];
ButtonRequest_UnknownDerivationPath = 15;
ButtonRequest_RecoveryHomepage = 16;
ButtonRequest_Success = 17;
ButtonRequest_Warning = 18;
ButtonRequest_PassphraseEntry = 19;
ButtonRequest_PinEntry = 20;
}
}
/**
@ -82,15 +107,17 @@ message ButtonAck {
* @next PinMatrixAck
*/
message PinMatrixRequest {
optional PinMatrixRequestType type = 1;
/**
* Type of PIN request
*/
enum PinMatrixRequestType {
PinMatrixRequestType_Current = 1;
PinMatrixRequestType_NewFirst = 2;
PinMatrixRequestType_NewSecond = 3;
}
optional PinMatrixRequestType type = 1;
/**
* Type of PIN request
*/
enum PinMatrixRequestType {
PinMatrixRequestType_Current = 1;
PinMatrixRequestType_NewFirst = 2;
PinMatrixRequestType_NewSecond = 3;
PinMatrixRequestType_WipeCodeFirst = 4;
PinMatrixRequestType_WipeCodeSecond = 5;
}
}
/**
@ -98,7 +125,7 @@ message PinMatrixRequest {
* @auxend
*/
message PinMatrixAck {
required string pin = 1; // matrix encoded PIN entered by user
required string pin = 1; // matrix encoded PIN entered by user
}
/**
@ -107,31 +134,36 @@ message PinMatrixAck {
* @next PassphraseAck
*/
message PassphraseRequest {
optional bool on_device = 1; // passphrase is being entered on the device
optional bool _on_device = 1 [deprecated=true]; // <2.3.0
}
/**
* Request: Send passphrase back
* @next PassphraseStateRequest
* @auxend
*/
message PassphraseAck {
optional string passphrase = 1;
optional bytes state = 2; // expected device state
optional string passphrase = 1;
optional bytes _state = 2 [deprecated=true]; // <2.3.0
optional bool on_device = 3; // user wants to enter passphrase on the device
}
/**
* Response: Device awaits passphrase state
* @next PassphraseStateAck
* Deprecated in 2.3.0
* @next Deprecated_PassphraseStateAck
*/
message PassphraseStateRequest {
optional bytes state = 1; // actual device state
message Deprecated_PassphraseStateRequest {
option deprecated = true;
optional bytes state = 1; // actual device state
}
/**
* Request: Send passphrase state back
* Deprecated in 2.3.0
* @auxend
*/
message PassphraseStateAck {
message Deprecated_PassphraseStateAck {
option deprecated = true;
}
/**
@ -140,10 +172,10 @@ message PassphraseStateAck {
* @embed
*/
message HDNodeType {
required uint32 depth = 1;
required uint32 fingerprint = 2;
required uint32 child_num = 3;
required bytes chain_code = 4;
optional bytes private_key = 5;
optional bytes public_key = 6;
required uint32 depth = 1;
required uint32 fingerprint = 2;
required uint32 child_num = 3;
required bytes chain_code = 4;
optional bytes private_key = 5;
required bytes public_key = 6;
}

View file

@ -0,0 +1,608 @@
// This file originates from the SatoshiLabs Trezor `common` repository at:
// https://github.com/trezor/trezor-common/blob/master/protob/messages-ethereum-eip712.proto
// dated 30.06.2025, commit 421b45d4677f2499234692b0f54010bc45b3ae5f.
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.6
// protoc v4.25.3
// source: messages-ethereum-eip712.proto
package trezor
import (
reflect "reflect"
sync "sync"
unsafe "unsafe"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type EthereumTypedDataStructAck_EthereumDataType int32
const (
EthereumTypedDataStructAck_UINT EthereumTypedDataStructAck_EthereumDataType = 1
EthereumTypedDataStructAck_INT EthereumTypedDataStructAck_EthereumDataType = 2
EthereumTypedDataStructAck_BYTES EthereumTypedDataStructAck_EthereumDataType = 3
EthereumTypedDataStructAck_STRING EthereumTypedDataStructAck_EthereumDataType = 4
EthereumTypedDataStructAck_BOOL EthereumTypedDataStructAck_EthereumDataType = 5
EthereumTypedDataStructAck_ADDRESS EthereumTypedDataStructAck_EthereumDataType = 6
EthereumTypedDataStructAck_ARRAY EthereumTypedDataStructAck_EthereumDataType = 7
EthereumTypedDataStructAck_STRUCT EthereumTypedDataStructAck_EthereumDataType = 8
)
// Enum value maps for EthereumTypedDataStructAck_EthereumDataType.
var (
EthereumTypedDataStructAck_EthereumDataType_name = map[int32]string{
1: "UINT",
2: "INT",
3: "BYTES",
4: "STRING",
5: "BOOL",
6: "ADDRESS",
7: "ARRAY",
8: "STRUCT",
}
EthereumTypedDataStructAck_EthereumDataType_value = map[string]int32{
"UINT": 1,
"INT": 2,
"BYTES": 3,
"STRING": 4,
"BOOL": 5,
"ADDRESS": 6,
"ARRAY": 7,
"STRUCT": 8,
}
)
func (x EthereumTypedDataStructAck_EthereumDataType) Enum() *EthereumTypedDataStructAck_EthereumDataType {
p := new(EthereumTypedDataStructAck_EthereumDataType)
*p = x
return p
}
func (x EthereumTypedDataStructAck_EthereumDataType) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (EthereumTypedDataStructAck_EthereumDataType) Descriptor() protoreflect.EnumDescriptor {
return file_messages_ethereum_eip712_proto_enumTypes[0].Descriptor()
}
func (EthereumTypedDataStructAck_EthereumDataType) Type() protoreflect.EnumType {
return &file_messages_ethereum_eip712_proto_enumTypes[0]
}
func (x EthereumTypedDataStructAck_EthereumDataType) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Do not use.
func (x *EthereumTypedDataStructAck_EthereumDataType) UnmarshalJSON(b []byte) error {
num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
if err != nil {
return err
}
*x = EthereumTypedDataStructAck_EthereumDataType(num)
return nil
}
// Deprecated: Use EthereumTypedDataStructAck_EthereumDataType.Descriptor instead.
func (EthereumTypedDataStructAck_EthereumDataType) EnumDescriptor() ([]byte, []int) {
return file_messages_ethereum_eip712_proto_rawDescGZIP(), []int{2, 0}
}
// *
// Request: Ask device to sign typed data
// @start
// @next EthereumTypedDataStructRequest
// @next EthereumTypedDataValueRequest
// @next EthereumTypedDataSignature
// @next Failure
type EthereumSignTypedData struct {
state protoimpl.MessageState `protogen:"open.v1"`
AddressN []uint32 `protobuf:"varint,1,rep,name=address_n,json=addressN" json:"address_n,omitempty"` // BIP-32 path to derive the key from master node
PrimaryType *string `protobuf:"bytes,2,req,name=primary_type,json=primaryType" json:"primary_type,omitempty"` // name of the root message struct
MetamaskV4Compat *bool `protobuf:"varint,3,opt,name=metamask_v4_compat,json=metamaskV4Compat,def=1" json:"metamask_v4_compat,omitempty"` // use MetaMask v4 (see https://github.com/MetaMask/eth-sig-util/issues/106)
Definitions *EthereumDefinitions `protobuf:"bytes,4,opt,name=definitions" json:"definitions,omitempty"` // network and/or token definitions
ShowMessageHash []byte `protobuf:"bytes,5,opt,name=show_message_hash,json=showMessageHash" json:"show_message_hash,omitempty"` // hash of the typed data to be signed (if set, user will be asked to confirm before signing)
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
// Default values for EthereumSignTypedData fields.
const (
Default_EthereumSignTypedData_MetamaskV4Compat = bool(true)
)
func (x *EthereumSignTypedData) Reset() {
*x = EthereumSignTypedData{}
mi := &file_messages_ethereum_eip712_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *EthereumSignTypedData) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*EthereumSignTypedData) ProtoMessage() {}
func (x *EthereumSignTypedData) ProtoReflect() protoreflect.Message {
mi := &file_messages_ethereum_eip712_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use EthereumSignTypedData.ProtoReflect.Descriptor instead.
func (*EthereumSignTypedData) Descriptor() ([]byte, []int) {
return file_messages_ethereum_eip712_proto_rawDescGZIP(), []int{0}
}
func (x *EthereumSignTypedData) GetAddressN() []uint32 {
if x != nil {
return x.AddressN
}
return nil
}
func (x *EthereumSignTypedData) GetPrimaryType() string {
if x != nil && x.PrimaryType != nil {
return *x.PrimaryType
}
return ""
}
func (x *EthereumSignTypedData) GetMetamaskV4Compat() bool {
if x != nil && x.MetamaskV4Compat != nil {
return *x.MetamaskV4Compat
}
return Default_EthereumSignTypedData_MetamaskV4Compat
}
func (x *EthereumSignTypedData) GetDefinitions() *EthereumDefinitions {
if x != nil {
return x.Definitions
}
return nil
}
func (x *EthereumSignTypedData) GetShowMessageHash() []byte {
if x != nil {
return x.ShowMessageHash
}
return nil
}
// *
// Response: Device asks for type information about a struct.
// @next EthereumTypedDataStructAck
type EthereumTypedDataStructRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` // name of the requested struct
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *EthereumTypedDataStructRequest) Reset() {
*x = EthereumTypedDataStructRequest{}
mi := &file_messages_ethereum_eip712_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *EthereumTypedDataStructRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*EthereumTypedDataStructRequest) ProtoMessage() {}
func (x *EthereumTypedDataStructRequest) ProtoReflect() protoreflect.Message {
mi := &file_messages_ethereum_eip712_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use EthereumTypedDataStructRequest.ProtoReflect.Descriptor instead.
func (*EthereumTypedDataStructRequest) Descriptor() ([]byte, []int) {
return file_messages_ethereum_eip712_proto_rawDescGZIP(), []int{1}
}
func (x *EthereumTypedDataStructRequest) GetName() string {
if x != nil && x.Name != nil {
return *x.Name
}
return ""
}
// *
// Request: Type information about a struct.
// @next EthereumTypedDataStructRequest
type EthereumTypedDataStructAck struct {
state protoimpl.MessageState `protogen:"open.v1"`
Members []*EthereumTypedDataStructAck_EthereumStructMember `protobuf:"bytes,1,rep,name=members" json:"members,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *EthereumTypedDataStructAck) Reset() {
*x = EthereumTypedDataStructAck{}
mi := &file_messages_ethereum_eip712_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *EthereumTypedDataStructAck) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*EthereumTypedDataStructAck) ProtoMessage() {}
func (x *EthereumTypedDataStructAck) ProtoReflect() protoreflect.Message {
mi := &file_messages_ethereum_eip712_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use EthereumTypedDataStructAck.ProtoReflect.Descriptor instead.
func (*EthereumTypedDataStructAck) Descriptor() ([]byte, []int) {
return file_messages_ethereum_eip712_proto_rawDescGZIP(), []int{2}
}
func (x *EthereumTypedDataStructAck) GetMembers() []*EthereumTypedDataStructAck_EthereumStructMember {
if x != nil {
return x.Members
}
return nil
}
// *
// Response: Device asks for data at the specific member path.
// @next EthereumTypedDataValueAck
type EthereumTypedDataValueRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
MemberPath []uint32 `protobuf:"varint,1,rep,name=member_path,json=memberPath" json:"member_path,omitempty"` // member path requested by device
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *EthereumTypedDataValueRequest) Reset() {
*x = EthereumTypedDataValueRequest{}
mi := &file_messages_ethereum_eip712_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *EthereumTypedDataValueRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*EthereumTypedDataValueRequest) ProtoMessage() {}
func (x *EthereumTypedDataValueRequest) ProtoReflect() protoreflect.Message {
mi := &file_messages_ethereum_eip712_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use EthereumTypedDataValueRequest.ProtoReflect.Descriptor instead.
func (*EthereumTypedDataValueRequest) Descriptor() ([]byte, []int) {
return file_messages_ethereum_eip712_proto_rawDescGZIP(), []int{3}
}
func (x *EthereumTypedDataValueRequest) GetMemberPath() []uint32 {
if x != nil {
return x.MemberPath
}
return nil
}
// *
// Request: Single value of a specific atomic field.
// @next EthereumTypedDataValueRequest
type EthereumTypedDataValueAck struct {
state protoimpl.MessageState `protogen:"open.v1"`
Value []byte `protobuf:"bytes,1,req,name=value" json:"value,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *EthereumTypedDataValueAck) Reset() {
*x = EthereumTypedDataValueAck{}
mi := &file_messages_ethereum_eip712_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *EthereumTypedDataValueAck) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*EthereumTypedDataValueAck) ProtoMessage() {}
func (x *EthereumTypedDataValueAck) ProtoReflect() protoreflect.Message {
mi := &file_messages_ethereum_eip712_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use EthereumTypedDataValueAck.ProtoReflect.Descriptor instead.
func (*EthereumTypedDataValueAck) Descriptor() ([]byte, []int) {
return file_messages_ethereum_eip712_proto_rawDescGZIP(), []int{4}
}
func (x *EthereumTypedDataValueAck) GetValue() []byte {
if x != nil {
return x.Value
}
return nil
}
type EthereumTypedDataStructAck_EthereumStructMember struct {
state protoimpl.MessageState `protogen:"open.v1"`
Type *EthereumTypedDataStructAck_EthereumFieldType `protobuf:"bytes,1,req,name=type" json:"type,omitempty"`
Name *string `protobuf:"bytes,2,req,name=name" json:"name,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *EthereumTypedDataStructAck_EthereumStructMember) Reset() {
*x = EthereumTypedDataStructAck_EthereumStructMember{}
mi := &file_messages_ethereum_eip712_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *EthereumTypedDataStructAck_EthereumStructMember) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*EthereumTypedDataStructAck_EthereumStructMember) ProtoMessage() {}
func (x *EthereumTypedDataStructAck_EthereumStructMember) ProtoReflect() protoreflect.Message {
mi := &file_messages_ethereum_eip712_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use EthereumTypedDataStructAck_EthereumStructMember.ProtoReflect.Descriptor instead.
func (*EthereumTypedDataStructAck_EthereumStructMember) Descriptor() ([]byte, []int) {
return file_messages_ethereum_eip712_proto_rawDescGZIP(), []int{2, 0}
}
func (x *EthereumTypedDataStructAck_EthereumStructMember) GetType() *EthereumTypedDataStructAck_EthereumFieldType {
if x != nil {
return x.Type
}
return nil
}
func (x *EthereumTypedDataStructAck_EthereumStructMember) GetName() string {
if x != nil && x.Name != nil {
return *x.Name
}
return ""
}
type EthereumTypedDataStructAck_EthereumFieldType struct {
state protoimpl.MessageState `protogen:"open.v1"`
DataType *EthereumTypedDataStructAck_EthereumDataType `protobuf:"varint,1,req,name=data_type,json=dataType,enum=hw.trezor.messages.ethereum_eip712.EthereumTypedDataStructAck_EthereumDataType" json:"data_type,omitempty"`
Size *uint32 `protobuf:"varint,2,opt,name=size" json:"size,omitempty"` // for integer types: size in bytes (uint8 has size 1, uint256 has size 32)
// for bytes types: size in bytes, or unset for dynamic
// for arrays: size in elements, or unset for dynamic
// for structs: number of members
// for string, bool and address: unset
EntryType *EthereumTypedDataStructAck_EthereumFieldType `protobuf:"bytes,3,opt,name=entry_type,json=entryType" json:"entry_type,omitempty"` // for array types, type of single entry
StructName *string `protobuf:"bytes,4,opt,name=struct_name,json=structName" json:"struct_name,omitempty"` // for structs: its name
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *EthereumTypedDataStructAck_EthereumFieldType) Reset() {
*x = EthereumTypedDataStructAck_EthereumFieldType{}
mi := &file_messages_ethereum_eip712_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *EthereumTypedDataStructAck_EthereumFieldType) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*EthereumTypedDataStructAck_EthereumFieldType) ProtoMessage() {}
func (x *EthereumTypedDataStructAck_EthereumFieldType) ProtoReflect() protoreflect.Message {
mi := &file_messages_ethereum_eip712_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use EthereumTypedDataStructAck_EthereumFieldType.ProtoReflect.Descriptor instead.
func (*EthereumTypedDataStructAck_EthereumFieldType) Descriptor() ([]byte, []int) {
return file_messages_ethereum_eip712_proto_rawDescGZIP(), []int{2, 1}
}
func (x *EthereumTypedDataStructAck_EthereumFieldType) GetDataType() EthereumTypedDataStructAck_EthereumDataType {
if x != nil && x.DataType != nil {
return *x.DataType
}
return EthereumTypedDataStructAck_UINT
}
func (x *EthereumTypedDataStructAck_EthereumFieldType) GetSize() uint32 {
if x != nil && x.Size != nil {
return *x.Size
}
return 0
}
func (x *EthereumTypedDataStructAck_EthereumFieldType) GetEntryType() *EthereumTypedDataStructAck_EthereumFieldType {
if x != nil {
return x.EntryType
}
return nil
}
func (x *EthereumTypedDataStructAck_EthereumFieldType) GetStructName() string {
if x != nil && x.StructName != nil {
return *x.StructName
}
return ""
}
var File_messages_ethereum_eip712_proto protoreflect.FileDescriptor
const file_messages_ethereum_eip712_proto_rawDesc = "" +
"\n" +
"\x1emessages-ethereum-eip712.proto\x12\"hw.trezor.messages.ethereum_eip712\x1a\x17messages-ethereum.proto\"\x8b\x02\n" +
"\x15EthereumSignTypedData\x12\x1b\n" +
"\taddress_n\x18\x01 \x03(\rR\baddressN\x12!\n" +
"\fprimary_type\x18\x02 \x02(\tR\vprimaryType\x122\n" +
"\x12metamask_v4_compat\x18\x03 \x01(\b:\x04trueR\x10metamaskV4Compat\x12R\n" +
"\vdefinitions\x18\x04 \x01(\v20.hw.trezor.messages.ethereum.EthereumDefinitionsR\vdefinitions\x12*\n" +
"\x11show_message_hash\x18\x05 \x01(\fR\x0fshowMessageHash\"4\n" +
"\x1eEthereumTypedDataStructRequest\x12\x12\n" +
"\x04name\x18\x01 \x02(\tR\x04name\"\xb4\x05\n" +
"\x1aEthereumTypedDataStructAck\x12m\n" +
"\amembers\x18\x01 \x03(\v2S.hw.trezor.messages.ethereum_eip712.EthereumTypedDataStructAck.EthereumStructMemberR\amembers\x1a\x90\x01\n" +
"\x14EthereumStructMember\x12d\n" +
"\x04type\x18\x01 \x02(\v2P.hw.trezor.messages.ethereum_eip712.EthereumTypedDataStructAck.EthereumFieldTypeR\x04type\x12\x12\n" +
"\x04name\x18\x02 \x02(\tR\x04name\x1a\xa7\x02\n" +
"\x11EthereumFieldType\x12l\n" +
"\tdata_type\x18\x01 \x02(\x0e2O.hw.trezor.messages.ethereum_eip712.EthereumTypedDataStructAck.EthereumDataTypeR\bdataType\x12\x12\n" +
"\x04size\x18\x02 \x01(\rR\x04size\x12o\n" +
"\n" +
"entry_type\x18\x03 \x01(\v2P.hw.trezor.messages.ethereum_eip712.EthereumTypedDataStructAck.EthereumFieldTypeR\tentryType\x12\x1f\n" +
"\vstruct_name\x18\x04 \x01(\tR\n" +
"structName\"j\n" +
"\x10EthereumDataType\x12\b\n" +
"\x04UINT\x10\x01\x12\a\n" +
"\x03INT\x10\x02\x12\t\n" +
"\x05BYTES\x10\x03\x12\n" +
"\n" +
"\x06STRING\x10\x04\x12\b\n" +
"\x04BOOL\x10\x05\x12\v\n" +
"\aADDRESS\x10\x06\x12\t\n" +
"\x05ARRAY\x10\a\x12\n" +
"\n" +
"\x06STRUCT\x10\b\"@\n" +
"\x1dEthereumTypedDataValueRequest\x12\x1f\n" +
"\vmember_path\x18\x01 \x03(\rR\n" +
"memberPath\"1\n" +
"\x19EthereumTypedDataValueAck\x12\x14\n" +
"\x05value\x18\x01 \x02(\fR\x05valueB}\n" +
"#com.satoshilabs.trezor.lib.protobufB\x1bTrezorMessageEthereumEIP712Z9github.com/ethereum/go-ethereum/accounts/usbwallet/trezor"
var (
file_messages_ethereum_eip712_proto_rawDescOnce sync.Once
file_messages_ethereum_eip712_proto_rawDescData []byte
)
func file_messages_ethereum_eip712_proto_rawDescGZIP() []byte {
file_messages_ethereum_eip712_proto_rawDescOnce.Do(func() {
file_messages_ethereum_eip712_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_messages_ethereum_eip712_proto_rawDesc), len(file_messages_ethereum_eip712_proto_rawDesc)))
})
return file_messages_ethereum_eip712_proto_rawDescData
}
var file_messages_ethereum_eip712_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_messages_ethereum_eip712_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_messages_ethereum_eip712_proto_goTypes = []any{
(EthereumTypedDataStructAck_EthereumDataType)(0), // 0: hw.trezor.messages.ethereum_eip712.EthereumTypedDataStructAck.EthereumDataType
(*EthereumSignTypedData)(nil), // 1: hw.trezor.messages.ethereum_eip712.EthereumSignTypedData
(*EthereumTypedDataStructRequest)(nil), // 2: hw.trezor.messages.ethereum_eip712.EthereumTypedDataStructRequest
(*EthereumTypedDataStructAck)(nil), // 3: hw.trezor.messages.ethereum_eip712.EthereumTypedDataStructAck
(*EthereumTypedDataValueRequest)(nil), // 4: hw.trezor.messages.ethereum_eip712.EthereumTypedDataValueRequest
(*EthereumTypedDataValueAck)(nil), // 5: hw.trezor.messages.ethereum_eip712.EthereumTypedDataValueAck
(*EthereumTypedDataStructAck_EthereumStructMember)(nil), // 6: hw.trezor.messages.ethereum_eip712.EthereumTypedDataStructAck.EthereumStructMember
(*EthereumTypedDataStructAck_EthereumFieldType)(nil), // 7: hw.trezor.messages.ethereum_eip712.EthereumTypedDataStructAck.EthereumFieldType
(*EthereumDefinitions)(nil), // 8: hw.trezor.messages.ethereum.EthereumDefinitions
}
var file_messages_ethereum_eip712_proto_depIdxs = []int32{
8, // 0: hw.trezor.messages.ethereum_eip712.EthereumSignTypedData.definitions:type_name -> hw.trezor.messages.ethereum.EthereumDefinitions
6, // 1: hw.trezor.messages.ethereum_eip712.EthereumTypedDataStructAck.members:type_name -> hw.trezor.messages.ethereum_eip712.EthereumTypedDataStructAck.EthereumStructMember
7, // 2: hw.trezor.messages.ethereum_eip712.EthereumTypedDataStructAck.EthereumStructMember.type:type_name -> hw.trezor.messages.ethereum_eip712.EthereumTypedDataStructAck.EthereumFieldType
0, // 3: hw.trezor.messages.ethereum_eip712.EthereumTypedDataStructAck.EthereumFieldType.data_type:type_name -> hw.trezor.messages.ethereum_eip712.EthereumTypedDataStructAck.EthereumDataType
7, // 4: hw.trezor.messages.ethereum_eip712.EthereumTypedDataStructAck.EthereumFieldType.entry_type:type_name -> hw.trezor.messages.ethereum_eip712.EthereumTypedDataStructAck.EthereumFieldType
5, // [5:5] is the sub-list for method output_type
5, // [5:5] is the sub-list for method input_type
5, // [5:5] is the sub-list for extension type_name
5, // [5:5] is the sub-list for extension extendee
0, // [0:5] is the sub-list for field type_name
}
func init() { file_messages_ethereum_eip712_proto_init() }
func file_messages_ethereum_eip712_proto_init() {
if File_messages_ethereum_eip712_proto != nil {
return
}
file_messages_ethereum_proto_init()
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_messages_ethereum_eip712_proto_rawDesc), len(file_messages_ethereum_eip712_proto_rawDesc)),
NumEnums: 1,
NumMessages: 7,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_messages_ethereum_eip712_proto_goTypes,
DependencyIndexes: file_messages_ethereum_eip712_proto_depIdxs,
EnumInfos: file_messages_ethereum_eip712_proto_enumTypes,
MessageInfos: file_messages_ethereum_eip712_proto_msgTypes,
}.Build()
File_messages_ethereum_eip712_proto = out.File
file_messages_ethereum_eip712_proto_goTypes = nil
file_messages_ethereum_eip712_proto_depIdxs = nil
}

View file

@ -0,0 +1,99 @@
// This file originates from the SatoshiLabs Trezor `common` repository at:
// https://github.com/trezor/trezor-common/blob/master/protob/messages-ethereum-eip712.proto
// dated 30.06.2025, commit 421b45d4677f2499234692b0f54010bc45b3ae5f.
syntax = "proto2";
package hw.trezor.messages.ethereum_eip712;
option go_package = "github.com/ethereum/go-ethereum/accounts/usbwallet/trezor";
// Sugar for easier handling in Java
option java_package = "com.satoshilabs.trezor.lib.protobuf";
option java_outer_classname = "TrezorMessageEthereumEIP712";
import "messages-ethereum.proto";
// Separated from messages-ethereum.proto as it is not implemented on T1 side
// and defining all the messages and fields could be even impossible as recursive
// messages are used here
/**
* Request: Ask device to sign typed data
* @start
* @next EthereumTypedDataStructRequest
* @next EthereumTypedDataValueRequest
* @next EthereumTypedDataSignature
* @next Failure
*/
message EthereumSignTypedData {
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
required string primary_type = 2; // name of the root message struct
optional bool metamask_v4_compat = 3 [default=true]; // use MetaMask v4 (see https://github.com/MetaMask/eth-sig-util/issues/106)
optional ethereum.EthereumDefinitions definitions = 4; // network and/or token definitions
optional bytes show_message_hash = 5; // hash of the typed data to be signed (if set, user will be asked to confirm before signing)
}
/**
* Response: Device asks for type information about a struct.
* @next EthereumTypedDataStructAck
*/
message EthereumTypedDataStructRequest {
required string name = 1; // name of the requested struct
}
/**
* Request: Type information about a struct.
* @next EthereumTypedDataStructRequest
*/
message EthereumTypedDataStructAck {
repeated EthereumStructMember members = 1;
message EthereumStructMember {
required EthereumFieldType type = 1;
required string name = 2;
}
message EthereumFieldType {
required EthereumDataType data_type = 1;
optional uint32 size = 2; // for integer types: size in bytes (uint8 has size 1, uint256 has size 32)
// for bytes types: size in bytes, or unset for dynamic
// for arrays: size in elements, or unset for dynamic
// for structs: number of members
// for string, bool and address: unset
optional EthereumFieldType entry_type = 3; // for array types, type of single entry
optional string struct_name = 4; // for structs: its name
}
enum EthereumDataType {
UINT = 1;
INT = 2;
BYTES = 3;
STRING = 4;
BOOL = 5;
ADDRESS = 6;
ARRAY = 7;
STRUCT = 8;
}
}
/**
* Response: Device asks for data at the specific member path.
* @next EthereumTypedDataValueAck
*/
message EthereumTypedDataValueRequest {
repeated uint32 member_path = 1; // member path requested by device
}
/**
* Request: Single value of a specific atomic field.
* @next EthereumTypedDataValueRequest
*/
message EthereumTypedDataValueAck {
required bytes value = 1;
// * atomic types: value of the member.
// Length must match the `size` of the corresponding field type, unless the size is dynamic.
// * array types: number of elements, encoded as uint16.
// * struct types: undefined, Trezor will not query a struct field.
}

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
// This file originates from the SatoshiLabs Trezor `common` repository at:
// https://github.com/trezor/trezor-common/blob/master/protob/messages-ethereum.proto
// dated 28.05.2019, commit 893fd219d4a01bcffa0cd9cfa631856371ec5aa9.
// dated 30.06.2025, commit 421b45d4677f2499234692b0f54010bc45b3ae5f.
syntax = "proto2";
package hw.trezor.messages.ethereum;
@ -13,7 +13,6 @@ option java_outer_classname = "TrezorMessageEthereum";
import "messages-common.proto";
/**
* Request: Ask device for public key corresponding to address_n path
* @start
@ -21,8 +20,8 @@ import "messages-common.proto";
* @next Failure
*/
message EthereumGetPublicKey {
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
optional bool show_display = 2; // optionally show on display before sending the result
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
optional bool show_display = 2; // optionally show on display before sending the result
}
/**
@ -30,8 +29,8 @@ message EthereumGetPublicKey {
* @end
*/
message EthereumPublicKey {
optional hw.trezor.messages.common.HDNodeType node = 1; // BIP32 public node
optional string xpub = 2; // serialized form of public node
required hw.trezor.messages.common.HDNodeType node = 1; // BIP32 public node
required string xpub = 2; // serialized form of public node
}
/**
@ -41,8 +40,10 @@ message EthereumPublicKey {
* @next Failure
*/
message EthereumGetAddress {
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
optional bool show_display = 2; // optionally show on display before sending the result
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
optional bool show_display = 2; // optionally show on display before sending the result
optional bytes encoded_network = 3; // encoded Ethereum network, see external-definitions.md for details
optional bool chunkify = 4; // display the address in chunks of 4 characters
}
/**
@ -50,30 +51,60 @@ message EthereumGetAddress {
* @end
*/
message EthereumAddress {
optional bytes addressBin = 1; // Ethereum address as 20 bytes (legacy firmwares)
optional string addressHex = 2; // Ethereum address as hex string (newer firmwares)
optional bytes _old_address = 1 [deprecated=true]; // trezor <1.8.0, <2.1.0 - raw bytes of Ethereum address
optional string address = 2; // Ethereum address as hex-encoded string
}
/**
* Request: Ask device to sign transaction
* All fields are optional from the protocol's point of view. Each field defaults to value `0` if missing.
* gas_price, gas_limit and chain_id must be provided and non-zero.
* All other fields are optional and default to value `0` if missing.
* Note: the first at most 1024 bytes of data MUST be transmitted as part of this message.
* @start
* @next EthereumTxRequest
* @next Failure
*/
message EthereumSignTx {
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
optional bytes nonce = 2; // <=256 bit unsigned big endian
optional bytes gas_price = 3; // <=256 bit unsigned big endian (in wei)
optional bytes gas_limit = 4; // <=256 bit unsigned big endian
optional bytes toBin = 5; // recipient address (20 bytes, legacy firmware)
optional string toHex = 11; // recipient address (hex string, newer firmware)
optional bytes value = 6; // <=256 bit unsigned big endian (in wei)
optional bytes data_initial_chunk = 7; // The initial data chunk (<= 1024 bytes)
optional uint32 data_length = 8; // Length of transaction payload
optional uint32 chain_id = 9; // Chain Id for EIP 155
optional uint32 tx_type = 10; // (only for Wanchain)
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
optional bytes nonce = 2 [default='']; // <=256 bit unsigned big endian
required bytes gas_price = 3; // <=256 bit unsigned big endian (in wei)
required bytes gas_limit = 4; // <=256 bit unsigned big endian
optional string to = 11 [default='']; // recipient address
optional bytes value = 6 [default='']; // <=256 bit unsigned big endian (in wei)
optional bytes data_initial_chunk = 7 [default='']; // The initial data chunk (<= 1024 bytes)
optional uint32 data_length = 8 [default=0]; // Length of transaction payload
required uint64 chain_id = 9; // Chain Id for EIP 155
optional uint32 tx_type = 10; // Used for Wanchain
optional EthereumDefinitions definitions = 12; // network and/or token definitions for tx
optional bool chunkify = 13; // display the address in chunks of 4 characters
}
/**
* Request: Ask device to sign EIP1559 transaction
* Note: the first at most 1024 bytes of data MUST be transmitted as part of this message.
* @start
* @next EthereumTxRequest
* @next Failure
*/
message EthereumSignTxEIP1559 {
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
required bytes nonce = 2; // <=256 bit unsigned big endian
required bytes max_gas_fee = 3; // <=256 bit unsigned big endian (in wei)
required bytes max_priority_fee = 4; // <=256 bit unsigned big endian (in wei)
required bytes gas_limit = 5; // <=256 bit unsigned big endian
optional string to = 6 [default='']; // recipient address
required bytes value = 7; // <=256 bit unsigned big endian (in wei)
optional bytes data_initial_chunk = 8 [default='']; // The initial data chunk (<= 1024 bytes)
required uint32 data_length = 9; // Length of transaction payload
required uint64 chain_id = 10; // Chain Id for EIP 155
repeated EthereumAccessList access_list = 11; // Access List
optional EthereumDefinitions definitions = 12; // network and/or token definitions for tx
optional bool chunkify = 13; // display the address in chunks of 4 characters
message EthereumAccessList {
required string address = 1;
repeated bytes storage_keys = 2;
}
}
/**
@ -84,10 +115,10 @@ message EthereumSignTx {
* @next EthereumTxAck
*/
message EthereumTxRequest {
optional uint32 data_length = 1; // Number of bytes being requested (<= 1024)
optional uint32 signature_v = 2; // Computed signature (recovery parameter, limited to 27 or 28)
optional bytes signature_r = 3; // Computed signature R component (256 bit)
optional bytes signature_s = 4; // Computed signature S component (256 bit)
optional uint32 data_length = 1; // Number of bytes being requested (<= 1024)
optional uint32 signature_v = 2; // Computed signature (recovery parameter, limited to 27 or 28)
optional bytes signature_r = 3; // Computed signature R component (256 bit)
optional bytes signature_s = 4; // Computed signature S component (256 bit)
}
/**
@ -95,7 +126,7 @@ message EthereumTxRequest {
* @next EthereumTxRequest
*/
message EthereumTxAck {
optional bytes data_chunk = 1; // Bytes from transaction payload (<= 1024 bytes)
required bytes data_chunk = 1; // Bytes from transaction payload (<= 1024 bytes)
}
/**
@ -105,8 +136,10 @@ message EthereumTxAck {
* @next Failure
*/
message EthereumSignMessage {
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
optional bytes message = 2; // message to be signed
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
required bytes message = 2; // message to be signed
optional bytes encoded_network = 3; // encoded Ethereum network, see external-definitions.md for details
optional bool chunkify = 4; // display the address in chunks of 4 characters
}
/**
@ -114,9 +147,8 @@ message EthereumSignMessage {
* @end
*/
message EthereumMessageSignature {
optional bytes addressBin = 1; // address used to sign the message (20 bytes, legacy firmware)
optional bytes signature = 2; // signature of the message
optional string addressHex = 3; // address used to sign the message (hex string, newer firmware)
required bytes signature = 2; // signature of the message
required string address = 3; // address used to sign the message
}
/**
@ -126,8 +158,39 @@ message EthereumMessageSignature {
* @next Failure
*/
message EthereumVerifyMessage {
optional bytes addressBin = 1; // address to verify (20 bytes, legacy firmware)
optional bytes signature = 2; // signature to verify
optional bytes message = 3; // message to verify
optional string addressHex = 4; // address to verify (hex string, newer firmware)
required bytes signature = 2; // signature to verify
required bytes message = 3; // message to verify
required string address = 4; // address to verify
optional bool chunkify = 5; // display the address in chunks of 4 characters
}
/**
* Request: Ask device to sign hash of typed data
* @start
* @next EthereumTypedDataSignature
* @next Failure
*/
message EthereumSignTypedHash {
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
required bytes domain_separator_hash = 2; // Hash of domainSeparator of typed data to be signed
optional bytes message_hash = 3; // Hash of the data of typed data to be signed (empty if domain-only data)
optional bytes encoded_network = 4; // encoded Ethereum network, see external-definitions.md for details
}
/**
* Response: Signed typed data
* @end
*/
message EthereumTypedDataSignature {
required bytes signature = 1; // signature of the typed data
required string address = 2; // address used to sign the typed data
}
/**
* Contains an encoded network and/or token definition. See external-definitions.md for details.
* @embed
*/
message EthereumDefinitions {
optional bytes encoded_network = 1; // encoded ethereum network
optional bytes encoded_token = 2; // encoded ethereum token
}

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
// This file originates from the SatoshiLabs Trezor `common` repository at:
// https://github.com/trezor/trezor-common/blob/master/protob/messages-management.proto
// dated 28.05.2019, commit 893fd219d4a01bcffa0cd9cfa631856371ec5aa9.
// dated 30.06.2025, commit 421b45d4677f2499234692b0f54010bc45b3ae5f.
syntax = "proto2";
package hw.trezor.messages.management;
@ -11,7 +11,49 @@ option go_package = "github.com/ethereum/go-ethereum/accounts/usbwallet/trezor"
option java_package = "com.satoshilabs.trezor.lib.protobuf";
option java_outer_classname = "TrezorMessageManagement";
import "messages-common.proto";
import "options.proto";
option (include_in_bitcoin_only) = true;
/**
* Type of the mnemonic backup given/received by the device during reset/recovery.
*/
enum BackupType {
Bip39 = 0; // also called "Single Backup", see BIP-0039
Slip39_Basic = 1; // also called "Shamir Backup", see SLIP-0039
Slip39_Advanced = 2; // also called "Super Shamir" or "Shamir with Groups", see SLIP-0039#two-level-scheme
Slip39_Single_Extendable = 3; // extendable single-share Shamir backup
Slip39_Basic_Extendable = 4; // extendable multi-share Shamir backup
Slip39_Advanced_Extendable = 5; // extendable multi-share Shamir backup with groups
}
/**
* Level of safety checks for unsafe actions like spending from invalid path namespace or setting high transaction fee.
*/
enum SafetyCheckLevel {
Strict = 0; // disallow unsafe actions, this is the default
PromptAlways = 1; // ask user before unsafe action
PromptTemporarily = 2; // like PromptAlways but reverts to Strict after reboot
}
/**
* Allowed display rotation angles (in degrees from North)
*/
enum DisplayRotation {
North = 0;
East = 90;
South = 180;
West = 270;
}
/**
* Format of the homescreen image
*/
enum HomescreenFormat {
Toif = 1; // full-color toif
Jpeg = 2; // jpeg
ToiG = 3; // greyscale toif
}
/**
* Request: Reset device to default state and ask for device details
@ -19,8 +61,9 @@ import "messages-common.proto";
* @next Features
*/
message Initialize {
optional bytes state = 1; // assumed device state, clear session if set and different
optional bool skip_passphrase = 2; // this session should always assume empty passphrase
optional bytes session_id = 1; // assumed device session id; Trezor clears caches if it is different or empty
optional bool _skip_passphrase = 2 [deprecated=true]; // removed as part of passphrase redesign
optional bool derive_cardano = 3; // whether to derive Cardano Icarus root keys in this session
}
/**
@ -36,65 +79,193 @@ message GetFeatures {
* @end
*/
message Features {
optional string vendor = 1; // name of the manufacturer, e.g. "trezor.io"
optional uint32 major_version = 2; // major version of the firmware/bootloader, e.g. 1
optional uint32 minor_version = 3; // minor version of the firmware/bootloader, e.g. 0
optional uint32 patch_version = 4; // patch version of the firmware/bootloader, e.g. 0
optional bool bootloader_mode = 5; // is device in bootloader mode?
optional string device_id = 6; // device's unique identifier
optional bool pin_protection = 7; // is device protected by PIN?
optional bool passphrase_protection = 8; // is node/mnemonic encrypted using passphrase?
optional string language = 9; // device language
optional string label = 10; // device description label
optional bool initialized = 12; // does device contain seed?
optional bytes revision = 13; // SCM revision of firmware
optional bytes bootloader_hash = 14; // hash of the bootloader
optional bool imported = 15; // was storage imported from an external source?
optional bool pin_cached = 16; // is PIN already cached in session?
optional bool passphrase_cached = 17; // is passphrase already cached in session?
optional bool firmware_present = 18; // is valid firmware loaded?
optional bool needs_backup = 19; // does storage need backup? (equals to Storage.needs_backup)
optional uint32 flags = 20; // device flags (equals to Storage.flags)
optional string model = 21; // device hardware model
optional uint32 fw_major = 22; // reported firmware version if in bootloader mode
optional uint32 fw_minor = 23; // reported firmware version if in bootloader mode
optional uint32 fw_patch = 24; // reported firmware version if in bootloader mode
optional string fw_vendor = 25; // reported firmware vendor if in bootloader mode
optional bytes fw_vendor_keys = 26; // reported firmware vendor keys (their hash)
optional bool unfinished_backup = 27; // report unfinished backup (equals to Storage.unfinished_backup)
optional bool no_backup = 28; // report no backup (equals to Storage.no_backup)
optional string vendor = 1; // name of the manufacturer, e.g. "trezor.io"
required uint32 major_version = 2; // major version of the firmware/bootloader, e.g. 1
required uint32 minor_version = 3; // minor version of the firmware/bootloader, e.g. 0
required uint32 patch_version = 4; // patch version of the firmware/bootloader, e.g. 0
optional bool bootloader_mode = 5; // is device in bootloader mode?
optional string device_id = 6; // device's unique identifier
optional bool pin_protection = 7; // is device protected by PIN?
optional bool passphrase_protection = 8; // is node/mnemonic encrypted using passphrase?
optional string language = 9; // device language
optional string label = 10; // device description label
optional bool initialized = 12; // does device contain seed?
optional bytes revision = 13; // SCM revision of firmware
optional bytes bootloader_hash = 14; // hash of the bootloader
optional bool imported = 15; // was storage imported from an external source?
optional bool unlocked = 16; // is the device unlocked? called "pin_cached" previously
optional bool _passphrase_cached = 17 [deprecated=true]; // is passphrase already cached in session?
optional bool firmware_present = 18; // is valid firmware loaded?
optional BackupAvailability backup_availability = 19; // does storage need backup? is repeated backup unlocked?
optional uint32 flags = 20; // device flags (equals to Storage.flags)
optional string model = 21; // device hardware model
optional uint32 fw_major = 22; // reported firmware version if in bootloader mode
optional uint32 fw_minor = 23; // reported firmware version if in bootloader mode
optional uint32 fw_patch = 24; // reported firmware version if in bootloader mode
optional string fw_vendor = 25; // reported firmware vendor if in bootloader mode
// optional bytes fw_vendor_keys = 26; // obsoleted, use fw_vendor
optional bool unfinished_backup = 27; // report unfinished backup (equals to Storage.unfinished_backup)
optional bool no_backup = 28; // report no backup (equals to Storage.no_backup)
optional RecoveryStatus recovery_status = 29; // whether or not we are in recovery mode and of what kind
repeated Capability capabilities = 30; // list of supported capabilities
optional BackupType backup_type = 31; // type of device backup (BIP-39 / SLIP-39 basic / SLIP-39 advanced)
optional bool sd_card_present = 32; // is SD card present
optional bool sd_protection = 33; // is SD Protect enabled
optional bool wipe_code_protection = 34; // is wipe code protection enabled
optional bytes session_id = 35;
optional bool passphrase_always_on_device = 36; // device enforces passphrase entry on Trezor
optional SafetyCheckLevel safety_checks = 37; // safety check level, set to Prompt to limit path namespace enforcement
optional uint32 auto_lock_delay_ms = 38; // number of milliseconds after which the device locks itself
optional DisplayRotation display_rotation = 39; // rotation of display (in degrees from North)
optional bool experimental_features = 40; // are experimental message types enabled?
optional bool busy = 41; // is the device busy, showing "Do not disconnect"?
optional HomescreenFormat homescreen_format = 42; // format of the homescreen, 1 = TOIf, 2 = jpg, 3 = TOIG
optional bool hide_passphrase_from_host = 43; // should we hide the passphrase when it comes from host?
optional string internal_model = 44; // internal model name
optional uint32 unit_color = 45; // color of the unit/device
optional bool unit_btconly = 46; // unit/device is intended as bitcoin only
optional uint32 homescreen_width = 47; // homescreen width in pixels
optional uint32 homescreen_height = 48; // homescreen height in pixels
optional bool bootloader_locked = 49; // bootloader is locked
optional bool language_version_matches = 50 [default=true]; // translation blob version matches firmware version
optional uint32 unit_packaging = 51; // unit/device packaging version
optional bool haptic_feedback = 52; // haptic feedback is enabled
optional RecoveryType recovery_type = 53; // what type of recovery we are in. NB: this works in conjunction with recovery_status
optional uint32 optiga_sec = 54; // Optiga's security event counter.
enum BackupAvailability {
/// Device is already backed up, or a previous backup has failed.
NotAvailable = 0;
/// Device is not backed up. Backup is required.
Required = 1;
/// Device is already backed up and can be backed up again.
Available = 2;
}
enum RecoveryStatus {
Nothing = 0; // we are not in recovery mode
Recovery = 1; // we are in "Normal" or "DryRun" recovery
Backup = 2; // we are in repeated backup mode
}
enum Capability {
option (has_bitcoin_only_values) = true;
Capability_Bitcoin = 1 [(bitcoin_only) = true];
Capability_Bitcoin_like = 2; // Altcoins based on the Bitcoin source code
Capability_Binance = 3; // BNB Smart Chain
Capability_Cardano = 4;
Capability_Crypto = 5 [(bitcoin_only) = true]; // generic crypto operations for GPG, SSH, etc.
Capability_EOS = 6;
Capability_Ethereum = 7;
Capability_Lisk = 8 [deprecated = true];
Capability_Monero = 9;
Capability_NEM = 10;
Capability_Ripple = 11;
Capability_Stellar = 12;
Capability_Tezos = 13;
Capability_U2F = 14;
Capability_Shamir = 15 [(bitcoin_only) = true];
Capability_ShamirGroups = 16 [(bitcoin_only) = true];
Capability_PassphraseEntry = 17 [(bitcoin_only) = true]; // the device is capable of passphrase entry directly on the device
Capability_Solana = 18;
Capability_Translations = 19 [(bitcoin_only) = true];
Capability_Brightness = 20 [(bitcoin_only) = true];
Capability_Haptic = 21 [(bitcoin_only) = true];
Capability_BLE = 22 [(bitcoin_only) = true]; // Bluetooth Low Energy
Capability_NFC = 23 [(bitcoin_only) = true]; // Near Field Communications
}
}
/**
* Request: clear session (removes cached PIN, passphrase, etc).
* Request: soft-lock the device. Following actions will require PIN. Passphrases remain cached.
* @start
* @next Success
*/
message ClearSession {
message LockDevice {
}
/**
* Request: change language and/or label of the device
* Request: Show a "Do not disconnect" dialog instead of the standard homescreen.
* @start
* @next Success
*/
message SetBusy {
optional uint32 expiry_ms = 1; // The time in milliseconds after which the dialog will automatically disappear. Overrides any previously set expiry. If not set, then the dialog is hidden.
}
/**
* Request: end the current sesson. Following actions must call Initialize again.
* Cache for the current session is discarded, other sessions remain intact.
* Device is not PIN-locked.
* @start
* @next Success
*/
message EndSession {
}
/**
* Request: change some property of the device, e.g. label or homescreen
* @start
* @next Success
* @next DataChunkRequest
* @next Failure
*/
message ApplySettings {
optional string language = 1;
optional string label = 2;
optional bool use_passphrase = 3;
optional bytes homescreen = 4;
optional PassphraseSourceType passphrase_source = 5;
optional uint32 auto_lock_delay_ms = 6;
optional uint32 display_rotation = 7; // in degrees from North
/**
* Structure representing passphrase source
*/
enum PassphraseSourceType {
ASK = 0;
DEVICE = 1;
HOST = 2;
}
optional string language = 1 [deprecated=true];
optional string label = 2;
optional bool use_passphrase = 3;
optional bytes homescreen = 4; // homescreen image in single array, deprecated for 14
optional uint32 _passphrase_source = 5 [deprecated=true]; // ASK = 0; DEVICE = 1; HOST = 2;
optional uint32 auto_lock_delay_ms = 6;
optional DisplayRotation display_rotation = 7; // rotation of display (in degrees from North)
optional bool passphrase_always_on_device = 8; // do not prompt for passphrase, enforce device entry
optional SafetyCheckLevel safety_checks = 9; // Safety check level, set to Prompt to limit path namespace enforcement
optional bool experimental_features = 10; // enable experimental message types
optional bool hide_passphrase_from_host = 11; // do not show passphrase coming from host
optional bool haptic_feedback = 13; // enable haptic feedback
optional uint32 homescreen_length = 14; // byte length of new homescreen, device will request chunks
}
/**
* Request: change the device language via translation data.
* Does not send the translation data itself, as they are too large for one message.
* Device will request the translation data in chunks.
* @start
* @next DataChunkRequest
* @next Failure
*/
message ChangeLanguage {
// byte length of the whole translation blob (set to 0 for default language - english)
required uint32 data_length = 1;
// Prompt the user on screen.
// In certain conditions (such as freshly installed device), the confirmation prompt
// is not mandatory. Setting show_display=false will skip the prompt if that's
// the case. If the device does not allow skipping the prompt, a request with
// show_display=false will return a failure. (This way the host can safely try
// to change the language without invoking a prompt.)
// Setting show_display to true will always show the prompt.
// Leaving the option unset will show the prompt only when necessary.
optional bool show_display = 2;
}
/**
* Response: Device asks for more data from translation/homescreen image.
* @end
* @next DataChunkAck
*/
message DataChunkRequest {
required uint32 data_length = 1; // Number of bytes being requested
required uint32 data_offset = 2; // Offset of the first byte being requested
}
/**
* Request: Translation/homescreen payload data.
* @next DataChunkRequest
* @next Success
*/
message DataChunkAck {
required bytes data_chunk = 1; // Bytes from translation/homescreen payload
}
/**
@ -104,7 +275,7 @@ message ApplySettings {
* @next Failure
*/
message ApplyFlags {
optional uint32 flags = 1; // bitmask, can only set bits, not unset
required uint32 flags = 1; // bitmask, can only set bits, not unset
}
/**
@ -114,7 +285,35 @@ message ApplyFlags {
* @next Failure
*/
message ChangePin {
optional bool remove = 1; // is PIN removal requested?
optional bool remove = 1; // is PIN removal requested?
}
/**
* Request: Starts workflow for setting/removing the wipe code
* @start
* @next Success
* @next Failure
*/
message ChangeWipeCode {
optional bool remove = 1; // is wipe code removal requested?
}
/**
* Request: Starts workflow for enabling/regenerating/disabling SD card protection
* @start
* @next Success
* @next Failure
*/
message SdProtect {
required SdProtectOperationType operation = 1;
/**
* Structure representing SD card protection operation
*/
enum SdProtectOperationType {
DISABLE = 0;
ENABLE = 1;
REFRESH = 2;
}
}
/**
@ -123,10 +322,8 @@ message ChangePin {
* @next Success
*/
message Ping {
optional string message = 1; // message to send back in Success message
optional bool button_protection = 2; // ask for button press
optional bool pin_protection = 3; // ask for PIN if set in device
optional bool passphrase_protection = 4; // ask for passphrase if set in device
optional string message = 1 [default=""]; // message to send back in Success message
optional bool button_protection = 2; // ask for button press
}
/**
@ -144,7 +341,7 @@ message Cancel {
* @next Failure
*/
message GetEntropy {
required uint32 size = 1; // size of requested entropy
required uint32 size = 1; // size of requested entropy
}
/**
@ -152,7 +349,44 @@ message GetEntropy {
* @end
*/
message Entropy {
required bytes entropy = 1; // chunk of random generated bytes
required bytes entropy = 1; // chunk of random generated bytes
}
/**
* Request: Get a hash of the installed firmware combined with an optional challenge.
* @start
* @next FirmwareHash
* @next Failure
*/
message GetFirmwareHash {
optional bytes challenge = 1; // Blake2s key up to 32 bytes in length.
}
/**
* Response: Hash of the installed firmware combined with the optional challenge.
* @end
*/
message FirmwareHash {
required bytes hash = 1;
}
/**
* Request: Request a signature of the provided challenge.
* @start
* @next AuthenticityProof
* @next Failure
*/
message AuthenticateDevice {
required bytes challenge = 1; // A random challenge to sign.
}
/**
* Response: Signature of the provided challenge along with a certificate issued by the Trezor company.
* @end
*/
message AuthenticityProof {
repeated bytes certificates = 1; // A certificate chain starting with the device certificate, followed by intermediate CA certificates, the last of which is signed by Trezor company's root CA.
required bytes signature = 2; // A DER-encoded signature of "\0x13AuthenticateDevice:" + length-prefixed challenge that should be verified using the device certificate.
}
/**
@ -171,14 +405,15 @@ message WipeDevice {
* @next Failure
*/
message LoadDevice {
optional string mnemonic = 1; // seed encoded as BIP-39 mnemonic (12, 18 or 24 words)
optional hw.trezor.messages.common.HDNodeType node = 2; // BIP-32 node
optional string pin = 3; // set PIN protection
optional bool passphrase_protection = 4; // enable master node encryption using passphrase
optional string language = 5 [default='english']; // device language
optional string label = 6; // device label
optional bool skip_checksum = 7; // do not test mnemonic for valid BIP-39 checksum
optional uint32 u2f_counter = 8; // U2F counter
repeated string mnemonics = 1; // seed encoded as mnemonic (12, 18 or 24 words for BIP39, 20 or 33 for SLIP39)
optional string pin = 3; // set PIN protection
optional bool passphrase_protection = 4; // enable master node encryption using passphrase
optional string language = 5 [deprecated=true]; // deprecated (use ChangeLanguage)
optional string label = 6; // device label
optional bool skip_checksum = 7; // do not test mnemonic for valid BIP-39 checksum
optional uint32 u2f_counter = 8; // U2F counter
optional bool needs_backup = 9; // set "needs backup" flag
optional bool no_backup = 10; // indicate that no backup is going to be made
}
/**
@ -188,15 +423,17 @@ message LoadDevice {
* @next Failure
*/
message ResetDevice {
optional bool display_random = 1; // display entropy generated by the device before asking for additional entropy
optional uint32 strength = 2 [default=256]; // strength of seed in bits
optional bool passphrase_protection = 3; // enable master node encryption using passphrase
optional bool pin_protection = 4; // enable PIN protection
optional string language = 5 [default='english']; // device language
optional string label = 6; // device label
optional uint32 u2f_counter = 7; // U2F counter
optional bool skip_backup = 8; // postpone seed backup to BackupDevice workflow
optional bool no_backup = 9; // indicate that no backup is going to be made
reserved 1; // unused display_random
optional uint32 strength = 2 [default=256]; // strength of seed in bits
optional bool passphrase_protection = 3; // enable master node encryption using passphrase
optional bool pin_protection = 4; // enable PIN protection
optional string language = 5 [deprecated=true]; // deprecated (use ChangeLanguage)
optional string label = 6; // device label
optional uint32 u2f_counter = 7; // U2F counter
optional bool skip_backup = 8; // postpone seed backup to BackupDevice workflow
optional bool no_backup = 9; // indicate that no backup is going to be made
optional BackupType backup_type = 10 [default=Bip39]; // type of the mnemonic backup
optional bool entropy_check = 11; // run with entropy check protocol
}
/**
@ -205,6 +442,12 @@ message ResetDevice {
* @next Success
*/
message BackupDevice {
optional uint32 group_threshold = 1;
message Slip39Group {
required uint32 member_threshold = 1;
required uint32 member_count = 2;
}
repeated Slip39Group groups = 2;
}
/**
@ -212,14 +455,34 @@ message BackupDevice {
* @next EntropyAck
*/
message EntropyRequest {
optional bytes entropy_commitment = 1; // HMAC-SHA256 of Trezor's internal entropy used in entropy check.
optional bytes prev_entropy = 2; // Trezor's internal entropy from the previous round of entropy check.
}
/**
* Request: Provide additional entropy for seed generation function
* @next Success
* @next EntropyCheckReady
*/
message EntropyAck {
optional bytes entropy = 1; // 256 bits (32 bytes) of random data
required bytes entropy = 1; // 256 bits (32 bytes) of the host's random data
}
/**
* Response: Trezor is ready for the next phase of the entropy check protocol.
* @next EntropyCheckContinue
* @next GetPublicKey
*/
message EntropyCheckReady {
}
/**
* Request: Proceed with the next phase of the entropy check protocol, asking Trezor to either reveal its internal entropy or to finish and store the seed.
* @next Success
* @next EntropyRequest
*/
message EntropyCheckContinue {
optional bool finish = 1 [default=false]; // finish the entropy check protocol, store the seed
}
/**
@ -229,29 +492,35 @@ message EntropyAck {
* @next WordRequest
*/
message RecoveryDevice {
optional uint32 word_count = 1; // number of words in BIP-39 mnemonic
optional bool passphrase_protection = 2; // enable master node encryption using passphrase
optional bool pin_protection = 3; // enable PIN protection
optional string language = 4 [default='english']; // device language
optional string label = 5; // device label
optional bool enforce_wordlist = 6; // enforce BIP-39 wordlist during the process
// 7 reserved for unused recovery method
optional RecoveryDeviceType type = 8; // supported recovery type
optional uint32 u2f_counter = 9; // U2F counter
optional bool dry_run = 10; // perform dry-run recovery workflow (for safe mnemonic validation)
/**
* Type of recovery procedure. These should be used as bitmask, e.g.,
* `RecoveryDeviceType_ScrambledWords | RecoveryDeviceType_Matrix`
* listing every method supported by the host computer.
*
* Note that ScrambledWords must be supported by every implementation
* for backward compatibility; there is no way to not support it.
*/
enum RecoveryDeviceType {
// use powers of two when extending this field
RecoveryDeviceType_ScrambledWords = 0; // words in scrambled order
RecoveryDeviceType_Matrix = 1; // matrix recovery type
}
optional uint32 word_count = 1; // number of words in BIP-39 mnemonic (T1 only)
optional bool passphrase_protection = 2; // enable master node encryption using passphrase
optional bool pin_protection = 3; // enable PIN protection
optional string language = 4 [deprecated=true]; // deprecated (use ChangeLanguage)
optional string label = 5; // device label
optional bool enforce_wordlist = 6; // enforce BIP-39 wordlist during the process (T1 only)
reserved 7; // unused recovery method
optional RecoveryDeviceInputMethod input_method = 8; // supported recovery input method (T1 only)
optional uint32 u2f_counter = 9; // U2F counter
optional RecoveryType type = 10 [default=NormalRecovery]; // the type of recovery to perform
/**
* Type of recovery procedure. These should be used as bitmask, e.g.,
* `RecoveryDeviceInputMethod_ScrambledWords | RecoveryDeviceInputMethod_Matrix`
* listing every method supported by the host computer.
*
* Note that ScrambledWords must be supported by every implementation
* for backward compatibility; there is no way to not support it.
*/
enum RecoveryDeviceInputMethod {
// use powers of two when extending this field
ScrambledWords = 0; // words in scrambled order
Matrix = 1; // matrix recovery type
}
}
enum RecoveryType {
NormalRecovery = 0; // recovery from seedphrase on an uninitialized device
DryRun = 1; // mnemonic validation
UnlockRepeatedBackup = 2; // unlock SLIP-39 repeated backup
}
/**
@ -260,15 +529,15 @@ message RecoveryDevice {
* @next WordAck
*/
message WordRequest {
optional WordRequestType type = 1;
/**
* Type of Recovery Word request
*/
enum WordRequestType {
WordRequestType_Plain = 0;
WordRequestType_Matrix9 = 1;
WordRequestType_Matrix6 = 2;
}
required WordRequestType type = 1;
/**
* Type of Recovery Word request
*/
enum WordRequestType {
WordRequestType_Plain = 0;
WordRequestType_Matrix9 = 1;
WordRequestType_Matrix6 = 2;
}
}
/**
@ -278,7 +547,7 @@ message WordRequest {
* @next Failure
*/
message WordAck {
required string word = 1; // one word of mnemonic on asked position
required string word = 1; // one word of mnemonic on asked position
}
/**
@ -287,5 +556,137 @@ message WordAck {
* @next Success
*/
message SetU2FCounter {
optional uint32 u2f_counter = 1; // counter
required uint32 u2f_counter = 1;
}
/**
* Request: Set U2F counter
* @start
* @next NextU2FCounter
*/
message GetNextU2FCounter {
}
/**
* Request: Set U2F counter
* @end
*/
message NextU2FCounter {
required uint32 u2f_counter = 1;
}
/**
* Request: Ask device to prepare for a preauthorized operation.
* @start
* @next PreauthorizedRequest
* @next Failure
*/
message DoPreauthorized {
}
/**
* Request: Device awaits a preauthorized operation.
* @start
* @next SignTx
* @next GetOwnershipProof
*/
message PreauthorizedRequest {
}
/**
* Request: Cancel any outstanding authorization in the current session.
* @start
* @next Success
* @next Failure
*/
message CancelAuthorization {
}
/**
* Request: Reboot firmware to bootloader
* @start
* @next Success
* @next DataChunkRequest
*/
message RebootToBootloader {
// Action to be performed after rebooting to bootloader
optional BootCommand boot_command = 1 [default=STOP_AND_WAIT];
// Firmware header to be flashed after rebooting to bootloader
optional bytes firmware_header = 2;
// Length of language blob to be installed before upgrading firmware
optional uint32 language_data_length = 3 [default=0];
enum BootCommand {
// Go to bootloader menu
STOP_AND_WAIT = 0;
// Connect to host and wait for firmware update
INSTALL_UPGRADE = 1;
}
}
/**
* Request: Ask device to generate a random nonce and store it in the session's cache
* @start
* @next Nonce
*/
message GetNonce {
option (experimental_message) = true;
}
/**
* Response: Contains a random nonce
* @end
*/
message Nonce {
option (experimental_message) = true;
required bytes nonce = 1; // a 32-byte random value generated by Trezor
}
/**
* Request: Ask device to unlock a subtree of the keychain.
* @start
* @next UnlockedPathRequest
* @next Failure
*/
message UnlockPath {
repeated uint32 address_n = 1; // prefix of the BIP-32 path leading to the account (m / purpose')
optional bytes mac = 2; // the MAC returned by UnlockedPathRequest
}
/**
* Request: Device awaits an operation.
* @start
* @next SignTx
* @next GetPublicKey
* @next GetAddress
*/
message UnlockedPathRequest {
required bytes mac = 1; // authentication code for future UnlockPath calls
}
/**
* Request: Show tutorial screens on the device
* @start
* @next Success
*/
message ShowDeviceTutorial {
}
/**
* Request: Unlocks bootloader, !irreversible!
* @start
* @next Success
* @next Failure
*/
message UnlockBootloader {
}
/**
* Request: Set device brightness
* @start
* @next Success
*/
message SetBrightness {
optional uint32 value = 1; // if not specified, let the user choose
}

File diff suppressed because it is too large Load diff

View file

@ -1,12 +1,12 @@
// This file originates from the SatoshiLabs Trezor `common` repository at:
// https://github.com/trezor/trezor-common/blob/master/protob/messages.proto
// dated 28.05.2019, commit 893fd219d4a01bcffa0cd9cfa631856371ec5aa9.
// dated 30.06.2025, commit 421b45d4677f2499234692b0f54010bc45b3ae5f.
syntax = "proto2";
package hw.trezor.messages;
/**
* Messages for TREZOR communication
* Messages for Trezor communication
*/
option go_package = "github.com/ethereum/go-ethereum/accounts/usbwallet/trezor";
@ -15,253 +15,348 @@ option go_package = "github.com/ethereum/go-ethereum/accounts/usbwallet/trezor"
option java_package = "com.satoshilabs.trezor.lib.protobuf";
option java_outer_classname = "TrezorMessage";
import "options.proto";
import "google/protobuf/descriptor.proto";
option (include_in_bitcoin_only) = true;
/**
* Options for specifying message direction and type of wire (normal/debug)
*/
extend google.protobuf.EnumValueOptions {
optional bool wire_in = 50002; // message can be transmitted via wire from PC to TREZOR
optional bool wire_out = 50003; // message can be transmitted via wire from TREZOR to PC
optional bool wire_debug_in = 50004; // message can be transmitted via debug wire from PC to TREZOR
optional bool wire_debug_out = 50005; // message can be transmitted via debug wire from TREZOR to PC
optional bool wire_tiny = 50006; // message is handled by TREZOR when the USB stack is in tiny mode
optional bool wire_bootloader = 50007; // message is only handled by TREZOR Bootloader
optional bool wire_no_fsm = 50008; // message is not handled by TREZOR unless the USB stack is in tiny mode
}
/**
* Mapping between TREZOR wire identifier (uint) and a protobuf message
* Mapping between Trezor wire identifier (uint) and a protobuf message
*/
enum MessageType {
option (has_bitcoin_only_values) = true;
// Management
MessageType_Initialize = 0 [(wire_in) = true, (wire_tiny) = true];
MessageType_Ping = 1 [(wire_in) = true];
MessageType_Success = 2 [(wire_out) = true];
MessageType_Failure = 3 [(wire_out) = true];
MessageType_ChangePin = 4 [(wire_in) = true];
MessageType_WipeDevice = 5 [(wire_in) = true];
MessageType_GetEntropy = 9 [(wire_in) = true];
MessageType_Entropy = 10 [(wire_out) = true];
MessageType_LoadDevice = 13 [(wire_in) = true];
MessageType_ResetDevice = 14 [(wire_in) = true];
MessageType_Features = 17 [(wire_out) = true];
MessageType_PinMatrixRequest = 18 [(wire_out) = true];
MessageType_PinMatrixAck = 19 [(wire_in) = true, (wire_tiny) = true, (wire_no_fsm) = true];
MessageType_Cancel = 20 [(wire_in) = true, (wire_tiny) = true];
MessageType_ClearSession = 24 [(wire_in) = true];
MessageType_ApplySettings = 25 [(wire_in) = true];
MessageType_ButtonRequest = 26 [(wire_out) = true];
MessageType_ButtonAck = 27 [(wire_in) = true, (wire_tiny) = true, (wire_no_fsm) = true];
MessageType_ApplyFlags = 28 [(wire_in) = true];
MessageType_BackupDevice = 34 [(wire_in) = true];
MessageType_EntropyRequest = 35 [(wire_out) = true];
MessageType_EntropyAck = 36 [(wire_in) = true];
MessageType_PassphraseRequest = 41 [(wire_out) = true];
MessageType_PassphraseAck = 42 [(wire_in) = true, (wire_tiny) = true, (wire_no_fsm) = true];
MessageType_PassphraseStateRequest = 77 [(wire_out) = true];
MessageType_PassphraseStateAck = 78 [(wire_in) = true, (wire_tiny) = true, (wire_no_fsm) = true];
MessageType_RecoveryDevice = 45 [(wire_in) = true];
MessageType_WordRequest = 46 [(wire_out) = true];
MessageType_WordAck = 47 [(wire_in) = true];
MessageType_GetFeatures = 55 [(wire_in) = true];
MessageType_SetU2FCounter = 63 [(wire_in) = true];
// Management
MessageType_Initialize = 0 [(bitcoin_only) = true, (wire_in) = true, (wire_tiny) = true];
MessageType_Ping = 1 [(bitcoin_only) = true, (wire_in) = true];
MessageType_Success = 2 [(bitcoin_only) = true, (wire_out) = true, (wire_debug_out) = true];
MessageType_Failure = 3 [(bitcoin_only) = true, (wire_out) = true, (wire_debug_out) = true];
MessageType_ChangePin = 4 [(bitcoin_only) = true, (wire_in) = true];
MessageType_WipeDevice = 5 [(bitcoin_only) = true, (wire_in) = true];
MessageType_GetEntropy = 9 [(bitcoin_only) = true, (wire_in) = true];
MessageType_Entropy = 10 [(bitcoin_only) = true, (wire_out) = true];
MessageType_LoadDevice = 13 [(bitcoin_only) = true, (wire_in) = true];
MessageType_ResetDevice = 14 [(bitcoin_only) = true, (wire_in) = true];
MessageType_SetBusy = 16 [(bitcoin_only) = true, (wire_in) = true];
MessageType_Features = 17 [(bitcoin_only) = true, (wire_out) = true];
MessageType_PinMatrixRequest = 18 [(bitcoin_only) = true, (wire_out) = true];
MessageType_PinMatrixAck = 19 [(bitcoin_only) = true, (wire_in) = true, (wire_tiny) = true, (wire_no_fsm) = true];
MessageType_Cancel = 20 [(bitcoin_only) = true, (wire_in) = true, (wire_tiny) = true];
MessageType_LockDevice = 24 [(bitcoin_only) = true, (wire_in) = true];
MessageType_ApplySettings = 25 [(bitcoin_only) = true, (wire_in) = true];
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];
MessageType_EntropyCheckReady = 994 [(bitcoin_only) = true, (wire_out) = true];
MessageType_EntropyCheckContinue = 995 [(bitcoin_only) = true, (wire_in) = true];
MessageType_PassphraseRequest = 41 [(bitcoin_only) = true, (wire_out) = true];
MessageType_PassphraseAck = 42 [(bitcoin_only) = true, (wire_in) = true, (wire_tiny) = true, (wire_no_fsm) = true];
MessageType_RecoveryDevice = 45 [(bitcoin_only) = true, (wire_in) = true];
MessageType_WordRequest = 46 [(bitcoin_only) = true, (wire_out) = true];
MessageType_WordAck = 47 [(bitcoin_only) = true, (wire_in) = true];
MessageType_GetFeatures = 55 [(bitcoin_only) = true, (wire_in) = true];
MessageType_SdProtect = 79 [(bitcoin_only) = true, (wire_in) = true];
MessageType_ChangeWipeCode = 82 [(bitcoin_only) = true, (wire_in) = true];
MessageType_EndSession = 83 [(bitcoin_only) = true, (wire_in) = true];
MessageType_DoPreauthorized = 84 [(bitcoin_only) = true, (wire_in) = true];
MessageType_PreauthorizedRequest = 85 [(bitcoin_only) = true, (wire_out) = true];
MessageType_CancelAuthorization = 86 [(bitcoin_only) = true, (wire_in) = true];
MessageType_RebootToBootloader = 87 [(bitcoin_only) = true, (wire_in) = true];
MessageType_GetFirmwareHash = 88 [(bitcoin_only) = true, (wire_in) = true];
MessageType_FirmwareHash = 89 [(bitcoin_only) = true, (wire_out) = true];
reserved 90 to 92;
MessageType_UnlockPath = 93 [(bitcoin_only) = true, (wire_in) = true];
MessageType_UnlockedPathRequest = 94 [(bitcoin_only) = true, (wire_out) = true];
MessageType_ShowDeviceTutorial = 95 [(bitcoin_only) = true, (wire_in) = true];
MessageType_UnlockBootloader = 96 [(bitcoin_only) = true, (wire_in) = true];
MessageType_AuthenticateDevice = 97 [(bitcoin_only) = true, (wire_out) = true];
MessageType_AuthenticityProof = 98 [(bitcoin_only) = true, (wire_in) = true];
MessageType_ChangeLanguage = 990 [(bitcoin_only) = true, (wire_in) = true];
MessageType_DataChunkRequest = 991 [(bitcoin_only) = true, (wire_out) = true];
MessageType_DataChunkAck = 992 [(bitcoin_only) = true, (wire_in) = true];
MessageType_SetBrightness = 993 [(bitcoin_only) = true, (wire_in) = true];
// Bootloader
MessageType_FirmwareErase = 6 [(wire_in) = true, (wire_bootloader) = true];
MessageType_FirmwareUpload = 7 [(wire_in) = true, (wire_bootloader) = true];
MessageType_FirmwareRequest = 8 [(wire_out) = true, (wire_bootloader) = true];
MessageType_SelfTest = 32 [(wire_in) = true, (wire_bootloader) = true];
MessageType_SetU2FCounter = 63 [(wire_in) = true];
MessageType_GetNextU2FCounter = 80 [(wire_in) = true];
MessageType_NextU2FCounter = 81 [(wire_out) = true];
// Bitcoin
MessageType_GetPublicKey = 11 [(wire_in) = true];
MessageType_PublicKey = 12 [(wire_out) = true];
MessageType_SignTx = 15 [(wire_in) = true];
MessageType_TxRequest = 21 [(wire_out) = true];
MessageType_TxAck = 22 [(wire_in) = true];
MessageType_GetAddress = 29 [(wire_in) = true];
MessageType_Address = 30 [(wire_out) = true];
MessageType_SignMessage = 38 [(wire_in) = true];
MessageType_VerifyMessage = 39 [(wire_in) = true];
MessageType_MessageSignature = 40 [(wire_out) = true];
// Deprecated messages, kept for protobuf compatibility.
MessageType_Deprecated_PassphraseStateRequest = 77 [deprecated = true];
MessageType_Deprecated_PassphraseStateAck = 78 [deprecated = true];
// Crypto
MessageType_CipherKeyValue = 23 [(wire_in) = true];
MessageType_CipheredKeyValue = 48 [(wire_out) = true];
MessageType_SignIdentity = 53 [(wire_in) = true];
MessageType_SignedIdentity = 54 [(wire_out) = true];
MessageType_GetECDHSessionKey = 61 [(wire_in) = true];
MessageType_ECDHSessionKey = 62 [(wire_out) = true];
MessageType_CosiCommit = 71 [(wire_in) = true];
MessageType_CosiCommitment = 72 [(wire_out) = true];
MessageType_CosiSign = 73 [(wire_in) = true];
MessageType_CosiSignature = 74 [(wire_out) = true];
// Bootloader
MessageType_FirmwareErase = 6 [(bitcoin_only) = true, (wire_in) = true, (wire_bootloader) = true];
MessageType_FirmwareUpload = 7 [(bitcoin_only) = true, (wire_in) = true, (wire_bootloader) = true];
MessageType_FirmwareRequest = 8 [(bitcoin_only) = true, (wire_out) = true, (wire_bootloader) = true];
MessageType_ProdTestT1 = 32 [(bitcoin_only) = true, (wire_in) = true, (wire_bootloader) = true];
// Debug
MessageType_DebugLinkDecision = 100 [(wire_debug_in) = true, (wire_tiny) = true, (wire_no_fsm) = true];
MessageType_DebugLinkGetState = 101 [(wire_debug_in) = true, (wire_tiny) = true];
MessageType_DebugLinkState = 102 [(wire_debug_out) = true];
MessageType_DebugLinkStop = 103 [(wire_debug_in) = true];
MessageType_DebugLinkLog = 104 [(wire_debug_out) = true];
MessageType_DebugLinkMemoryRead = 110 [(wire_debug_in) = true];
MessageType_DebugLinkMemory = 111 [(wire_debug_out) = true];
MessageType_DebugLinkMemoryWrite = 112 [(wire_debug_in) = true];
MessageType_DebugLinkFlashErase = 113 [(wire_debug_in) = true];
// BLE
MessageType_BleUnpair = 8001 [(bitcoin_only) = true, (wire_in) = true];
// Ethereum
MessageType_EthereumGetPublicKey = 450 [(wire_in) = true];
MessageType_EthereumPublicKey = 451 [(wire_out) = true];
MessageType_EthereumGetAddress = 56 [(wire_in) = true];
MessageType_EthereumAddress = 57 [(wire_out) = true];
MessageType_EthereumSignTx = 58 [(wire_in) = true];
MessageType_EthereumTxRequest = 59 [(wire_out) = true];
MessageType_EthereumTxAck = 60 [(wire_in) = true];
MessageType_EthereumSignMessage = 64 [(wire_in) = true];
MessageType_EthereumVerifyMessage = 65 [(wire_in) = true];
MessageType_EthereumMessageSignature = 66 [(wire_out) = true];
// Bitcoin
MessageType_GetPublicKey = 11 [(bitcoin_only) = true, (wire_in) = true];
MessageType_PublicKey = 12 [(bitcoin_only) = true, (wire_out) = true];
MessageType_SignTx = 15 [(bitcoin_only) = true, (wire_in) = true];
MessageType_TxRequest = 21 [(bitcoin_only) = true, (wire_out) = true];
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];
MessageType_GetOwnershipId = 43 [(bitcoin_only) = true, (wire_in) = true];
MessageType_OwnershipId = 44 [(bitcoin_only) = true, (wire_out) = true];
MessageType_GetOwnershipProof = 49 [(bitcoin_only) = true, (wire_in) = true];
MessageType_OwnershipProof = 50 [(bitcoin_only) = true, (wire_out) = true];
MessageType_AuthorizeCoinJoin = 51 [(bitcoin_only) = true, (wire_in) = true];
// NEM
MessageType_NEMGetAddress = 67 [(wire_in) = true];
MessageType_NEMAddress = 68 [(wire_out) = true];
MessageType_NEMSignTx = 69 [(wire_in) = true];
MessageType_NEMSignedTx = 70 [(wire_out) = true];
MessageType_NEMDecryptMessage = 75 [(wire_in) = true];
MessageType_NEMDecryptedMessage = 76 [(wire_out) = true];
// Crypto
MessageType_CipherKeyValue = 23 [(bitcoin_only) = true, (wire_in) = true];
MessageType_CipheredKeyValue = 48 [(bitcoin_only) = true, (wire_out) = true];
MessageType_SignIdentity = 53 [(bitcoin_only) = true, (wire_in) = true];
MessageType_SignedIdentity = 54 [(bitcoin_only) = true, (wire_out) = true];
MessageType_GetECDHSessionKey = 61 [(bitcoin_only) = true, (wire_in) = true];
MessageType_ECDHSessionKey = 62 [(bitcoin_only) = true, (wire_out) = true];
// dropped: CosiCommit, CosiCommitment, CosiSign, CosiSignature
reserved 71 to 74;
// Lisk
MessageType_LiskGetAddress = 114 [(wire_in) = true];
MessageType_LiskAddress = 115 [(wire_out) = true];
MessageType_LiskSignTx = 116 [(wire_in) = true];
MessageType_LiskSignedTx = 117 [(wire_out) = true];
MessageType_LiskSignMessage = 118 [(wire_in) = true];
MessageType_LiskMessageSignature = 119 [(wire_out) = true];
MessageType_LiskVerifyMessage = 120 [(wire_in) = true];
MessageType_LiskGetPublicKey = 121 [(wire_in) = true];
MessageType_LiskPublicKey = 122 [(wire_out) = true];
// Debug
MessageType_DebugLinkDecision = 100 [(bitcoin_only) = true, (wire_debug_in) = true, (wire_tiny) = true, (wire_no_fsm) = true];
MessageType_DebugLinkGetState = 101 [(bitcoin_only) = true, (wire_debug_in) = true, (wire_tiny) = true];
MessageType_DebugLinkState = 102 [(bitcoin_only) = true, (wire_debug_out) = true];
MessageType_DebugLinkStop = 103 [(bitcoin_only) = true, (wire_debug_in) = true];
MessageType_DebugLinkLog = 104 [(bitcoin_only) = true, (wire_debug_out) = true];
MessageType_DebugLinkMemoryRead = 110 [(bitcoin_only) = true, (wire_debug_in) = true];
MessageType_DebugLinkMemory = 111 [(bitcoin_only) = true, (wire_debug_out) = true];
MessageType_DebugLinkMemoryWrite = 112 [(bitcoin_only) = true, (wire_debug_in) = true];
MessageType_DebugLinkFlashErase = 113 [(bitcoin_only) = true, (wire_debug_in) = true];
MessageType_DebugLinkLayout = 9001 [(bitcoin_only) = true, (wire_debug_out) = true];
MessageType_DebugLinkReseedRandom = 9002 [(bitcoin_only) = true, (wire_debug_in) = true];
MessageType_DebugLinkRecordScreen = 9003 [(bitcoin_only) = true, (wire_debug_in) = true];
MessageType_DebugLinkEraseSdCard = 9005 [(bitcoin_only) = true, (wire_debug_in) = true];
MessageType_DebugLinkWatchLayout = 9006 [(bitcoin_only) = true, (wire_debug_in) = true];
MessageType_DebugLinkResetDebugEvents = 9007 [(bitcoin_only) = true, (wire_debug_in) = true];
MessageType_DebugLinkOptigaSetSecMax = 9008 [(bitcoin_only) = true, (wire_debug_in) = true];
MessageType_DebugLinkGetGcInfo = 9009 [(bitcoin_only) = true, (wire_debug_in) = true];
MessageType_DebugLinkGcInfo = 9010 [(bitcoin_only) = true, (wire_debug_out) = true];
MessageType_DebugLinkGetPairingInfo = 9011 [(bitcoin_only) = true, (wire_debug_in) = true];
MessageType_DebugLinkPairingInfo = 9012 [(bitcoin_only) = true, (wire_debug_out) = true];
// Tezos
MessageType_TezosGetAddress = 150 [(wire_in) = true];
MessageType_TezosAddress = 151 [(wire_out) = true];
MessageType_TezosSignTx = 152 [(wire_in) = true];
MessageType_TezosSignedTx = 153 [(wire_out) = true];
MessageType_TezosGetPublicKey = 154 [(wire_in) = true];
MessageType_TezosPublicKey = 155 [(wire_out) = true];
// Ethereum
MessageType_EthereumGetPublicKey = 450 [(wire_in) = true];
MessageType_EthereumPublicKey = 451 [(wire_out) = true];
MessageType_EthereumGetAddress = 56 [(wire_in) = true];
MessageType_EthereumAddress = 57 [(wire_out) = true];
MessageType_EthereumSignTx = 58 [(wire_in) = true];
MessageType_EthereumSignTxEIP1559 = 452 [(wire_in) = true];
MessageType_EthereumTxRequest = 59 [(wire_out) = true];
MessageType_EthereumTxAck = 60 [(wire_in) = true];
MessageType_EthereumSignMessage = 64 [(wire_in) = true];
MessageType_EthereumVerifyMessage = 65 [(wire_in) = true];
MessageType_EthereumMessageSignature = 66 [(wire_out) = true];
MessageType_EthereumSignTypedData = 464 [(wire_in) = true];
MessageType_EthereumTypedDataStructRequest = 465 [(wire_out) = true];
MessageType_EthereumTypedDataStructAck = 466 [(wire_in) = true];
MessageType_EthereumTypedDataValueRequest = 467 [(wire_out) = true];
MessageType_EthereumTypedDataValueAck = 468 [(wire_in) = true];
MessageType_EthereumTypedDataSignature = 469 [(wire_out) = true];
MessageType_EthereumSignTypedHash = 470 [(wire_in) = true];
// Stellar
MessageType_StellarSignTx = 202 [(wire_in) = true];
MessageType_StellarTxOpRequest = 203 [(wire_out) = true];
MessageType_StellarGetAddress = 207 [(wire_in) = true];
MessageType_StellarAddress = 208 [(wire_out) = true];
MessageType_StellarCreateAccountOp = 210 [(wire_in) = true];
MessageType_StellarPaymentOp = 211 [(wire_in) = true];
MessageType_StellarPathPaymentOp = 212 [(wire_in) = true];
MessageType_StellarManageOfferOp = 213 [(wire_in) = true];
MessageType_StellarCreatePassiveOfferOp = 214 [(wire_in) = true];
MessageType_StellarSetOptionsOp = 215 [(wire_in) = true];
MessageType_StellarChangeTrustOp = 216 [(wire_in) = true];
MessageType_StellarAllowTrustOp = 217 [(wire_in) = true];
MessageType_StellarAccountMergeOp = 218 [(wire_in) = true];
// omitted: StellarInflationOp is not a supported operation, would be 219
MessageType_StellarManageDataOp = 220 [(wire_in) = true];
MessageType_StellarBumpSequenceOp = 221 [(wire_in) = true];
MessageType_StellarSignedTx = 230 [(wire_out) = true];
// NEM
MessageType_NEMGetAddress = 67 [(wire_in) = true];
MessageType_NEMAddress = 68 [(wire_out) = true];
MessageType_NEMSignTx = 69 [(wire_in) = true];
MessageType_NEMSignedTx = 70 [(wire_out) = true];
MessageType_NEMDecryptMessage = 75 [(wire_in) = true];
MessageType_NEMDecryptedMessage = 76 [(wire_out) = true];
// TRON
MessageType_TronGetAddress = 250 [(wire_in) = true];
MessageType_TronAddress = 251 [(wire_out) = true];
MessageType_TronSignTx = 252 [(wire_in) = true];
MessageType_TronSignedTx = 253 [(wire_out) = true];
// Lisk
/*
MessageType_LiskGetAddress = 114 [(wire_in) = true];
MessageType_LiskAddress = 115 [(wire_out) = true];
MessageType_LiskSignTx = 116 [(wire_in) = true];
MessageType_LiskSignedTx = 117 [(wire_out) = true];
MessageType_LiskSignMessage = 118 [(wire_in) = true];
MessageType_LiskMessageSignature = 119 [(wire_out) = true];
MessageType_LiskVerifyMessage = 120 [(wire_in) = true];
MessageType_LiskGetPublicKey = 121 [(wire_in) = true];
MessageType_LiskPublicKey = 122 [(wire_out) = true];
*/
reserved 114 to 122;
// Cardano
// dropped Sign/VerifyMessage ids 300-302
MessageType_CardanoSignTx = 303 [(wire_in) = true];
MessageType_CardanoTxRequest = 304 [(wire_out) = true];
MessageType_CardanoGetPublicKey = 305 [(wire_in) = true];
MessageType_CardanoPublicKey = 306 [(wire_out) = true];
MessageType_CardanoGetAddress = 307 [(wire_in) = true];
MessageType_CardanoAddress = 308 [(wire_out) = true];
MessageType_CardanoTxAck = 309 [(wire_in) = true];
MessageType_CardanoSignedTx = 310 [(wire_out) = true];
// Tezos
MessageType_TezosGetAddress = 150 [(wire_in) = true];
MessageType_TezosAddress = 151 [(wire_out) = true];
MessageType_TezosSignTx = 152 [(wire_in) = true];
MessageType_TezosSignedTx = 153 [(wire_out) = true];
MessageType_TezosGetPublicKey = 154 [(wire_in) = true];
MessageType_TezosPublicKey = 155 [(wire_out) = true];
// Ontology
MessageType_OntologyGetAddress = 350 [(wire_in) = true];
MessageType_OntologyAddress = 351 [(wire_out) = true];
MessageType_OntologyGetPublicKey = 352 [(wire_in) = true];
MessageType_OntologyPublicKey = 353 [(wire_out) = true];
MessageType_OntologySignTransfer = 354 [(wire_in) = true];
MessageType_OntologySignedTransfer = 355 [(wire_out) = true];
MessageType_OntologySignWithdrawOng = 356 [(wire_in) = true];
MessageType_OntologySignedWithdrawOng = 357 [(wire_out) = true];
MessageType_OntologySignOntIdRegister = 358 [(wire_in) = true];
MessageType_OntologySignedOntIdRegister = 359 [(wire_out) = true];
MessageType_OntologySignOntIdAddAttributes = 360 [(wire_in) = true];
MessageType_OntologySignedOntIdAddAttributes = 361 [(wire_out) = true];
// Stellar
MessageType_StellarSignTx = 202 [(wire_in) = true];
MessageType_StellarTxOpRequest = 203 [(wire_out) = true];
MessageType_StellarGetAddress = 207 [(wire_in) = true];
MessageType_StellarAddress = 208 [(wire_out) = true];
MessageType_StellarCreateAccountOp = 210 [(wire_in) = true];
MessageType_StellarPaymentOp = 211 [(wire_in) = true];
MessageType_StellarPathPaymentStrictReceiveOp = 212 [(wire_in) = true];
MessageType_StellarManageSellOfferOp = 213 [(wire_in) = true];
MessageType_StellarCreatePassiveSellOfferOp = 214 [(wire_in) = true];
MessageType_StellarSetOptionsOp = 215 [(wire_in) = true];
MessageType_StellarChangeTrustOp = 216 [(wire_in) = true];
MessageType_StellarAllowTrustOp = 217 [(wire_in) = true];
MessageType_StellarAccountMergeOp = 218 [(wire_in) = true];
reserved 219; // omitted: StellarInflationOp
MessageType_StellarManageDataOp = 220 [(wire_in) = true];
MessageType_StellarBumpSequenceOp = 221 [(wire_in) = true];
MessageType_StellarManageBuyOfferOp = 222 [(wire_in) = true];
MessageType_StellarPathPaymentStrictSendOp = 223 [(wire_in) = true];
reserved 224; // omitted: StellarCreateClaimableBalanceOp
MessageType_StellarClaimClaimableBalanceOp = 225 [(wire_in) = true];
MessageType_StellarSignedTx = 230 [(wire_out) = true];
// Ripple
MessageType_RippleGetAddress = 400 [(wire_in) = true];
MessageType_RippleAddress = 401 [(wire_out) = true];
MessageType_RippleSignTx = 402 [(wire_in) = true];
MessageType_RippleSignedTx = 403 [(wire_in) = true];
// Cardano
// dropped Sign/VerifyMessage ids 300-302
// dropped TxRequest/TxAck ids 304 and 309 (shelley update)
// dropped SignTx/SignedTx/SignedTxChunk/SignedTxChunkAck ids 303, 310, 311 and 312
reserved 300 to 304, 309 to 312;
MessageType_CardanoGetPublicKey = 305 [(wire_in) = true];
MessageType_CardanoPublicKey = 306 [(wire_out) = true];
MessageType_CardanoGetAddress = 307 [(wire_in) = true];
MessageType_CardanoAddress = 308 [(wire_out) = true];
MessageType_CardanoTxItemAck = 313 [(wire_out) = true];
MessageType_CardanoTxAuxiliaryDataSupplement = 314 [(wire_out) = true];
MessageType_CardanoTxWitnessRequest = 315 [(wire_in) = true];
MessageType_CardanoTxWitnessResponse = 316 [(wire_out) = true];
MessageType_CardanoTxHostAck = 317 [(wire_in) = true];
MessageType_CardanoTxBodyHash = 318 [(wire_out) = true];
MessageType_CardanoSignTxFinished = 319 [(wire_out) = true];
MessageType_CardanoSignTxInit = 320 [(wire_in) = true];
MessageType_CardanoTxInput = 321 [(wire_in) = true];
MessageType_CardanoTxOutput = 322 [(wire_in) = true];
MessageType_CardanoAssetGroup = 323 [(wire_in) = true];
MessageType_CardanoToken = 324 [(wire_in) = true];
MessageType_CardanoTxCertificate = 325 [(wire_in) = true];
MessageType_CardanoTxWithdrawal = 326 [(wire_in) = true];
MessageType_CardanoTxAuxiliaryData = 327 [(wire_in) = true];
MessageType_CardanoPoolOwner = 328 [(wire_in) = true];
MessageType_CardanoPoolRelayParameters = 329 [(wire_in) = true];
MessageType_CardanoGetNativeScriptHash = 330 [(wire_in) = true];
MessageType_CardanoNativeScriptHash = 331 [(wire_out) = true];
MessageType_CardanoTxMint = 332 [(wire_in) = true];
MessageType_CardanoTxCollateralInput = 333 [(wire_in) = true];
MessageType_CardanoTxRequiredSigner = 334 [(wire_in) = true];
MessageType_CardanoTxInlineDatumChunk = 335 [(wire_in) = true];
MessageType_CardanoTxReferenceScriptChunk = 336 [(wire_in) = true];
MessageType_CardanoTxReferenceInput = 337 [(wire_in) = true];
// Monero
MessageType_MoneroTransactionInitRequest = 501 [(wire_out) = true];
MessageType_MoneroTransactionInitAck = 502 [(wire_out) = true];
MessageType_MoneroTransactionSetInputRequest = 503 [(wire_out) = true];
MessageType_MoneroTransactionSetInputAck = 504 [(wire_out) = true];
MessageType_MoneroTransactionInputsPermutationRequest = 505 [(wire_out) = true];
MessageType_MoneroTransactionInputsPermutationAck = 506 [(wire_out) = true];
MessageType_MoneroTransactionInputViniRequest = 507 [(wire_out) = true];
MessageType_MoneroTransactionInputViniAck = 508 [(wire_out) = true];
MessageType_MoneroTransactionAllInputsSetRequest = 509 [(wire_out) = true];
MessageType_MoneroTransactionAllInputsSetAck = 510 [(wire_out) = true];
MessageType_MoneroTransactionSetOutputRequest = 511 [(wire_out) = true];
MessageType_MoneroTransactionSetOutputAck = 512 [(wire_out) = true];
MessageType_MoneroTransactionAllOutSetRequest = 513 [(wire_out) = true];
MessageType_MoneroTransactionAllOutSetAck = 514 [(wire_out) = true];
MessageType_MoneroTransactionSignInputRequest = 515 [(wire_out) = true];
MessageType_MoneroTransactionSignInputAck = 516 [(wire_out) = true];
MessageType_MoneroTransactionFinalRequest = 517 [(wire_out) = true];
MessageType_MoneroTransactionFinalAck = 518 [(wire_out) = true];
MessageType_MoneroKeyImageExportInitRequest = 530 [(wire_out) = true];
MessageType_MoneroKeyImageExportInitAck = 531 [(wire_out) = true];
MessageType_MoneroKeyImageSyncStepRequest = 532 [(wire_out) = true];
MessageType_MoneroKeyImageSyncStepAck = 533 [(wire_out) = true];
MessageType_MoneroKeyImageSyncFinalRequest = 534 [(wire_out) = true];
MessageType_MoneroKeyImageSyncFinalAck = 535 [(wire_out) = true];
MessageType_MoneroGetAddress = 540 [(wire_in) = true];
MessageType_MoneroAddress = 541 [(wire_out) = true];
MessageType_MoneroGetWatchKey = 542 [(wire_in) = true];
MessageType_MoneroWatchKey = 543 [(wire_out) = true];
MessageType_DebugMoneroDiagRequest = 546 [(wire_in) = true];
MessageType_DebugMoneroDiagAck = 547 [(wire_out) = true];
MessageType_MoneroGetTxKeyRequest = 550 [(wire_in) = true];
MessageType_MoneroGetTxKeyAck = 551 [(wire_out) = true];
MessageType_MoneroLiveRefreshStartRequest = 552 [(wire_in) = true];
MessageType_MoneroLiveRefreshStartAck = 553 [(wire_out) = true];
MessageType_MoneroLiveRefreshStepRequest = 554 [(wire_in) = true];
MessageType_MoneroLiveRefreshStepAck = 555 [(wire_out) = true];
MessageType_MoneroLiveRefreshFinalRequest = 556 [(wire_in) = true];
MessageType_MoneroLiveRefreshFinalAck = 557 [(wire_out) = true];
// Ripple
MessageType_RippleGetAddress = 400 [(wire_in) = true];
MessageType_RippleAddress = 401 [(wire_out) = true];
MessageType_RippleSignTx = 402 [(wire_in) = true];
MessageType_RippleSignedTx = 403 [(wire_in) = true];
// EOS
MessageType_EosGetPublicKey = 600 [(wire_in) = true];
MessageType_EosPublicKey = 601 [(wire_out) = true];
MessageType_EosSignTx = 602 [(wire_in) = true];
MessageType_EosTxActionRequest = 603 [(wire_out) = true];
MessageType_EosTxActionAck = 604 [(wire_in) = true];
MessageType_EosSignedTx = 605 [(wire_out) = true];
// Monero
MessageType_MoneroTransactionInitRequest = 501 [(wire_out) = true];
MessageType_MoneroTransactionInitAck = 502 [(wire_out) = true];
MessageType_MoneroTransactionSetInputRequest = 503 [(wire_out) = true];
MessageType_MoneroTransactionSetInputAck = 504 [(wire_out) = true];
MessageType_MoneroTransactionInputViniRequest = 507 [(wire_out) = true];
MessageType_MoneroTransactionInputViniAck = 508 [(wire_out) = true];
MessageType_MoneroTransactionAllInputsSetRequest = 509 [(wire_out) = true];
MessageType_MoneroTransactionAllInputsSetAck = 510 [(wire_out) = true];
MessageType_MoneroTransactionSetOutputRequest = 511 [(wire_out) = true];
MessageType_MoneroTransactionSetOutputAck = 512 [(wire_out) = true];
MessageType_MoneroTransactionAllOutSetRequest = 513 [(wire_out) = true];
MessageType_MoneroTransactionAllOutSetAck = 514 [(wire_out) = true];
MessageType_MoneroTransactionSignInputRequest = 515 [(wire_out) = true];
MessageType_MoneroTransactionSignInputAck = 516 [(wire_out) = true];
MessageType_MoneroTransactionFinalRequest = 517 [(wire_out) = true];
MessageType_MoneroTransactionFinalAck = 518 [(wire_out) = true];
MessageType_MoneroKeyImageExportInitRequest = 530 [(wire_out) = true];
MessageType_MoneroKeyImageExportInitAck = 531 [(wire_out) = true];
MessageType_MoneroKeyImageSyncStepRequest = 532 [(wire_out) = true];
MessageType_MoneroKeyImageSyncStepAck = 533 [(wire_out) = true];
MessageType_MoneroKeyImageSyncFinalRequest = 534 [(wire_out) = true];
MessageType_MoneroKeyImageSyncFinalAck = 535 [(wire_out) = true];
MessageType_MoneroGetAddress = 540 [(wire_in) = true];
MessageType_MoneroAddress = 541 [(wire_out) = true];
MessageType_MoneroGetWatchKey = 542 [(wire_in) = true];
MessageType_MoneroWatchKey = 543 [(wire_out) = true];
MessageType_DebugMoneroDiagRequest = 546 [(wire_in) = true];
MessageType_DebugMoneroDiagAck = 547 [(wire_out) = true];
MessageType_MoneroGetTxKeyRequest = 550 [(wire_in) = true];
MessageType_MoneroGetTxKeyAck = 551 [(wire_out) = true];
MessageType_MoneroLiveRefreshStartRequest = 552 [(wire_in) = true];
MessageType_MoneroLiveRefreshStartAck = 553 [(wire_out) = true];
MessageType_MoneroLiveRefreshStepRequest = 554 [(wire_in) = true];
MessageType_MoneroLiveRefreshStepAck = 555 [(wire_out) = true];
MessageType_MoneroLiveRefreshFinalRequest = 556 [(wire_in) = true];
MessageType_MoneroLiveRefreshFinalAck = 557 [(wire_out) = true];
// Binance
MessageType_BinanceGetAddress = 700 [(wire_in) = true];
MessageType_BinanceAddress = 701 [(wire_out) = true];
MessageType_BinanceGetPublicKey = 702 [(wire_in) = true];
MessageType_BinancePublicKey = 703 [(wire_out) = true];
MessageType_BinanceSignTx = 704 [(wire_in) = true];
MessageType_BinanceTxRequest = 705 [(wire_out) = true];
MessageType_BinanceTransferMsg = 706 [(wire_in) = true];
MessageType_BinanceOrderMsg = 707 [(wire_in) = true];
MessageType_BinanceCancelMsg = 708 [(wire_in) = true];
MessageType_BinanceSignedTx = 709 [(wire_out) = true];
// EOS
MessageType_EosGetPublicKey = 600 [(wire_in) = true];
MessageType_EosPublicKey = 601 [(wire_out) = true];
MessageType_EosSignTx = 602 [(wire_in) = true];
MessageType_EosTxActionRequest = 603 [(wire_out) = true];
MessageType_EosTxActionAck = 604 [(wire_in) = true];
MessageType_EosSignedTx = 605 [(wire_out) = true];
// BNB Beacon Chain (deprecated)
reserved 700 to 709;
// WebAuthn
MessageType_WebAuthnListResidentCredentials = 800 [(wire_in) = true];
MessageType_WebAuthnCredentials = 801 [(wire_out) = true];
MessageType_WebAuthnAddResidentCredential = 802 [(wire_in) = true];
MessageType_WebAuthnRemoveResidentCredential = 803 [(wire_in) = true];
// Solana
MessageType_SolanaGetPublicKey = 900 [(wire_in) = true];
MessageType_SolanaPublicKey = 901 [(wire_out) = true];
MessageType_SolanaGetAddress = 902 [(wire_in) = true];
MessageType_SolanaAddress = 903 [(wire_out) = true];
MessageType_SolanaSignTx = 904 [(wire_in) = true];
MessageType_SolanaTxSignature = 905 [(wire_out) = true];
// THP
MessageType_ThpCreateNewSession = 1000 [(bitcoin_only) = true, (wire_in) = true];
reserved 1001 to 1005; // never appeared in a release, reserved for future use
MessageType_ThpPairingRequest = 1006 [(bitcoin_only) = true];
MessageType_ThpPairingRequestApproved = 1007 [(bitcoin_only) = true];
MessageType_ThpSelectMethod = 1008 [(bitcoin_only) = true];
MessageType_ThpPairingPreparationsFinished = 1009 [(bitcoin_only) = true];
MessageType_ThpCredentialRequest = 1010 [(bitcoin_only) = true];
MessageType_ThpCredentialResponse = 1011 [(bitcoin_only) = true];
MessageType_ThpEndRequest = 1012 [(bitcoin_only) = true];
MessageType_ThpEndResponse = 1013 [(bitcoin_only) = true];
reserved 1014 to 1015;
MessageType_ThpCodeEntryCommitment = 1016 [(bitcoin_only) = true];
MessageType_ThpCodeEntryChallenge = 1017 [(bitcoin_only) = true];
MessageType_ThpCodeEntryCpaceTrezor = 1018 [(bitcoin_only) = true];
MessageType_ThpCodeEntryCpaceHostTag = 1019 [(bitcoin_only) = true];
MessageType_ThpCodeEntrySecret = 1020 [(bitcoin_only) = true];
reserved 1021 to 1023;
MessageType_ThpQrCodeTag = 1024 [(bitcoin_only) = true];
MessageType_ThpQrCodeSecret = 1025 [(bitcoin_only) = true];
reserved 1026 to 1031;
MessageType_ThpNfcTagHost = 1032 [(bitcoin_only) = true];
MessageType_ThpNfcTagTrezor = 1033 [(bitcoin_only) = true];
// Nostr
MessageType_NostrGetPubkey = 2001 [(wire_in) = true];
MessageType_NostrPubkey = 2002 [(wire_out) = true];
MessageType_NostrSignEvent = 2003 [(wire_in) = true];
MessageType_NostrEventSignature = 2004 [(wire_out) = true];
// Benchmark
MessageType_BenchmarkListNames = 9100 [(bitcoin_only) = true];
MessageType_BenchmarkNames = 9101 [(bitcoin_only) = true];
MessageType_BenchmarkRun = 9102 [(bitcoin_only) = true];
MessageType_BenchmarkResult = 9103 [(bitcoin_only) = true];
}

View file

@ -0,0 +1,264 @@
// This file originates from the SatoshiLabs Trezor `common` repository at:
// https://github.com/trezor/trezor-common/blob/master/protob/options.proto
// dated 30.06.2025, commit 421b45d4677f2499234692b0f54010bc45b3ae5f.
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.6
// protoc v4.25.3
// source: options.proto
package trezor
import (
reflect "reflect"
unsafe "unsafe"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
descriptorpb "google.golang.org/protobuf/types/descriptorpb"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
var file_options_proto_extTypes = []protoimpl.ExtensionInfo{
{
ExtendedType: (*descriptorpb.EnumValueOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 50002,
Name: "hw.trezor.messages.wire_in",
Tag: "varint,50002,opt,name=wire_in",
Filename: "options.proto",
},
{
ExtendedType: (*descriptorpb.EnumValueOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 50003,
Name: "hw.trezor.messages.wire_out",
Tag: "varint,50003,opt,name=wire_out",
Filename: "options.proto",
},
{
ExtendedType: (*descriptorpb.EnumValueOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 50004,
Name: "hw.trezor.messages.wire_debug_in",
Tag: "varint,50004,opt,name=wire_debug_in",
Filename: "options.proto",
},
{
ExtendedType: (*descriptorpb.EnumValueOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 50005,
Name: "hw.trezor.messages.wire_debug_out",
Tag: "varint,50005,opt,name=wire_debug_out",
Filename: "options.proto",
},
{
ExtendedType: (*descriptorpb.EnumValueOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 50006,
Name: "hw.trezor.messages.wire_tiny",
Tag: "varint,50006,opt,name=wire_tiny",
Filename: "options.proto",
},
{
ExtendedType: (*descriptorpb.EnumValueOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 50007,
Name: "hw.trezor.messages.wire_bootloader",
Tag: "varint,50007,opt,name=wire_bootloader",
Filename: "options.proto",
},
{
ExtendedType: (*descriptorpb.EnumValueOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 50008,
Name: "hw.trezor.messages.wire_no_fsm",
Tag: "varint,50008,opt,name=wire_no_fsm",
Filename: "options.proto",
},
{
ExtendedType: (*descriptorpb.EnumValueOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 60000,
Name: "hw.trezor.messages.bitcoin_only",
Tag: "varint,60000,opt,name=bitcoin_only",
Filename: "options.proto",
},
{
ExtendedType: (*descriptorpb.EnumOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 51001,
Name: "hw.trezor.messages.has_bitcoin_only_values",
Tag: "varint,51001,opt,name=has_bitcoin_only_values",
Filename: "options.proto",
},
{
ExtendedType: (*descriptorpb.MessageOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 52001,
Name: "hw.trezor.messages.experimental_message",
Tag: "varint,52001,opt,name=experimental_message",
Filename: "options.proto",
},
{
ExtendedType: (*descriptorpb.MessageOptions)(nil),
ExtensionType: (*uint32)(nil),
Field: 52002,
Name: "hw.trezor.messages.wire_type",
Tag: "varint,52002,opt,name=wire_type",
Filename: "options.proto",
},
{
ExtendedType: (*descriptorpb.MessageOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 52003,
Name: "hw.trezor.messages.internal_only",
Tag: "varint,52003,opt,name=internal_only",
Filename: "options.proto",
},
{
ExtendedType: (*descriptorpb.FieldOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 53001,
Name: "hw.trezor.messages.experimental_field",
Tag: "varint,53001,opt,name=experimental_field",
Filename: "options.proto",
},
{
ExtendedType: (*descriptorpb.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 60000,
Name: "hw.trezor.messages.include_in_bitcoin_only",
Tag: "varint,60000,opt,name=include_in_bitcoin_only",
Filename: "options.proto",
},
}
// Extension fields to descriptorpb.EnumValueOptions.
var (
// optional bool wire_in = 50002;
E_WireIn = &file_options_proto_extTypes[0] // message can be transmitted via wire from PC to Trezor
// optional bool wire_out = 50003;
E_WireOut = &file_options_proto_extTypes[1] // message can be transmitted via wire from Trezor to PC
// optional bool wire_debug_in = 50004;
E_WireDebugIn = &file_options_proto_extTypes[2] // message can be transmitted via debug wire from PC to Trezor
// optional bool wire_debug_out = 50005;
E_WireDebugOut = &file_options_proto_extTypes[3] // message can be transmitted via debug wire from Trezor to PC
// optional bool wire_tiny = 50006;
E_WireTiny = &file_options_proto_extTypes[4] // message is handled by Trezor when the USB stack is in tiny mode
// optional bool wire_bootloader = 50007;
E_WireBootloader = &file_options_proto_extTypes[5] // message is only handled by Trezor Bootloader
// optional bool wire_no_fsm = 50008;
E_WireNoFsm = &file_options_proto_extTypes[6] // message is not handled by Trezor unless the USB stack is in tiny mode
// optional bool bitcoin_only = 60000;
E_BitcoinOnly = &file_options_proto_extTypes[7] // enum value is available on BITCOIN_ONLY build
)
// Extension fields to descriptorpb.EnumOptions.
var (
// optional bool has_bitcoin_only_values = 51001;
E_HasBitcoinOnlyValues = &file_options_proto_extTypes[8] // indicate that some values should be excluded on BITCOIN_ONLY builds
)
// Extension fields to descriptorpb.MessageOptions.
var (
// optional bool experimental_message = 52001;
E_ExperimentalMessage = &file_options_proto_extTypes[9] // indicate that a message is intended for development and beta testing only and its definition may change at any time
// optional uint32 wire_type = 52002;
E_WireType = &file_options_proto_extTypes[10] // override wire type specified in the MessageType enum
// optional bool internal_only = 52003;
E_InternalOnly = &file_options_proto_extTypes[11] // indicate that a message is intended for internal use only and should not be transmitted via the wire
)
// Extension fields to descriptorpb.FieldOptions.
var (
// optional bool experimental_field = 53001;
E_ExperimentalField = &file_options_proto_extTypes[12] // indicate that a field is intended for development and beta testing only
)
// Extension fields to descriptorpb.FileOptions.
var (
// optional bool include_in_bitcoin_only = 60000;
E_IncludeInBitcoinOnly = &file_options_proto_extTypes[13] // definitions are available on BITCOIN_ONLY build
)
var File_options_proto protoreflect.FileDescriptor
const file_options_proto_rawDesc = "" +
"\n" +
"\roptions.proto\x12\x12hw.trezor.messages\x1a google/protobuf/descriptor.proto:<\n" +
"\awire_in\x12!.google.protobuf.EnumValueOptions\x18҆\x03 \x01(\bR\x06wireIn:>\n" +
"\bwire_out\x12!.google.protobuf.EnumValueOptions\x18ӆ\x03 \x01(\bR\awireOut:G\n" +
"\rwire_debug_in\x12!.google.protobuf.EnumValueOptions\x18Ԇ\x03 \x01(\bR\vwireDebugIn:I\n" +
"\x0ewire_debug_out\x12!.google.protobuf.EnumValueOptions\x18Ն\x03 \x01(\bR\fwireDebugOut:@\n" +
"\twire_tiny\x12!.google.protobuf.EnumValueOptions\x18ֆ\x03 \x01(\bR\bwireTiny:L\n" +
"\x0fwire_bootloader\x12!.google.protobuf.EnumValueOptions\x18׆\x03 \x01(\bR\x0ewireBootloader:C\n" +
"\vwire_no_fsm\x12!.google.protobuf.EnumValueOptions\x18؆\x03 \x01(\bR\twireNoFsm:F\n" +
"\fbitcoin_only\x12!.google.protobuf.EnumValueOptions\x18\xe0\xd4\x03 \x01(\bR\vbitcoinOnly:U\n" +
"\x17has_bitcoin_only_values\x12\x1c.google.protobuf.EnumOptions\x18\xb9\x8e\x03 \x01(\bR\x14hasBitcoinOnlyValues:T\n" +
"\x14experimental_message\x12\x1f.google.protobuf.MessageOptions\x18\xa1\x96\x03 \x01(\bR\x13experimentalMessage:>\n" +
"\twire_type\x12\x1f.google.protobuf.MessageOptions\x18\xa2\x96\x03 \x01(\rR\bwireType:F\n" +
"\rinternal_only\x12\x1f.google.protobuf.MessageOptions\x18\xa3\x96\x03 \x01(\bR\finternalOnly:N\n" +
"\x12experimental_field\x12\x1d.google.protobuf.FieldOptions\x18\x89\x9e\x03 \x01(\bR\x11experimentalField:U\n" +
"\x17include_in_bitcoin_only\x12\x1c.google.protobuf.FileOptions\x18\xe0\xd4\x03 \x01(\bR\x14includeInBitcoinOnlyBo\n" +
"#com.satoshilabs.trezor.lib.protobufB\rTrezorOptionsZ9github.com/ethereum/go-ethereum/accounts/usbwallet/trezor"
var file_options_proto_goTypes = []any{
(*descriptorpb.EnumValueOptions)(nil), // 0: google.protobuf.EnumValueOptions
(*descriptorpb.EnumOptions)(nil), // 1: google.protobuf.EnumOptions
(*descriptorpb.MessageOptions)(nil), // 2: google.protobuf.MessageOptions
(*descriptorpb.FieldOptions)(nil), // 3: google.protobuf.FieldOptions
(*descriptorpb.FileOptions)(nil), // 4: google.protobuf.FileOptions
}
var file_options_proto_depIdxs = []int32{
0, // 0: hw.trezor.messages.wire_in:extendee -> google.protobuf.EnumValueOptions
0, // 1: hw.trezor.messages.wire_out:extendee -> google.protobuf.EnumValueOptions
0, // 2: hw.trezor.messages.wire_debug_in:extendee -> google.protobuf.EnumValueOptions
0, // 3: hw.trezor.messages.wire_debug_out:extendee -> google.protobuf.EnumValueOptions
0, // 4: hw.trezor.messages.wire_tiny:extendee -> google.protobuf.EnumValueOptions
0, // 5: hw.trezor.messages.wire_bootloader:extendee -> google.protobuf.EnumValueOptions
0, // 6: hw.trezor.messages.wire_no_fsm:extendee -> google.protobuf.EnumValueOptions
0, // 7: hw.trezor.messages.bitcoin_only:extendee -> google.protobuf.EnumValueOptions
1, // 8: hw.trezor.messages.has_bitcoin_only_values:extendee -> google.protobuf.EnumOptions
2, // 9: hw.trezor.messages.experimental_message:extendee -> google.protobuf.MessageOptions
2, // 10: hw.trezor.messages.wire_type:extendee -> google.protobuf.MessageOptions
2, // 11: hw.trezor.messages.internal_only:extendee -> google.protobuf.MessageOptions
3, // 12: hw.trezor.messages.experimental_field:extendee -> google.protobuf.FieldOptions
4, // 13: hw.trezor.messages.include_in_bitcoin_only:extendee -> google.protobuf.FileOptions
14, // [14:14] is the sub-list for method output_type
14, // [14:14] is the sub-list for method input_type
14, // [14:14] is the sub-list for extension type_name
0, // [0:14] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_options_proto_init() }
func file_options_proto_init() {
if File_options_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_options_proto_rawDesc), len(file_options_proto_rawDesc)),
NumEnums: 0,
NumMessages: 0,
NumExtensions: 14,
NumServices: 0,
},
GoTypes: file_options_proto_goTypes,
DependencyIndexes: file_options_proto_depIdxs,
ExtensionInfos: file_options_proto_extTypes,
}.Build()
File_options_proto = out.File
file_options_proto_goTypes = nil
file_options_proto_depIdxs = nil
}

View file

@ -0,0 +1,72 @@
// This file originates from the SatoshiLabs Trezor `common` repository at:
// https://github.com/trezor/trezor-common/blob/master/protob/options.proto
// dated 30.06.2025, commit 421b45d4677f2499234692b0f54010bc45b3ae5f.
syntax = "proto2";
package hw.trezor.messages;
option go_package = "github.com/ethereum/go-ethereum/accounts/usbwallet/trezor";
// Sugar for easier handling in Java
option java_package = "com.satoshilabs.trezor.lib.protobuf";
option java_outer_classname = "TrezorOptions";
import "google/protobuf/descriptor.proto";
/************************* WARNING ***********************
Due to the way extensions are accessed in pb2py, there needs to be a globally unique
name-ID mapping for extensions. That means that two different extensions, e.g. for
EnumValueOptions and FieldOptions, MUST NOT have the same ID.
Using the same ID indicates the same purpose (protobuf does not allow multiple
extensions with the same name), such as EnumValueOptions.bitcoin_only and
FileOptions.include_in_bitcoin_only. pb2py can then find the extension under
either name.
The convention to achieve this is as follows:
- extensions specific to a type have the same prefix:
* 50xxx for EnumValueOptions
* 51xxx for EnumOptions
* 52xxx for MessageOptions
* 53xxx for FieldOptions
- extensions that might be used across types have the same "global" prefix 60xxx
*/
/**
* Options for specifying message direction and type of wire (normal/debug)
*/
extend google.protobuf.EnumValueOptions {
optional bool wire_in = 50002; // message can be transmitted via wire from PC to Trezor
optional bool wire_out = 50003; // message can be transmitted via wire from Trezor to PC
optional bool wire_debug_in = 50004; // message can be transmitted via debug wire from PC to Trezor
optional bool wire_debug_out = 50005; // message can be transmitted via debug wire from Trezor to PC
optional bool wire_tiny = 50006; // message is handled by Trezor when the USB stack is in tiny mode
optional bool wire_bootloader = 50007; // message is only handled by Trezor Bootloader
optional bool wire_no_fsm = 50008; // message is not handled by Trezor unless the USB stack is in tiny mode
optional bool bitcoin_only = 60000; // enum value is available on BITCOIN_ONLY build
// (messages not marked bitcoin_only will be EXCLUDED)
}
/** Options for tagging enum types */
extend google.protobuf.EnumOptions {
optional bool has_bitcoin_only_values = 51001; // indicate that some values should be excluded on BITCOIN_ONLY builds
}
/** Options for tagging message types */
extend google.protobuf.MessageOptions {
optional bool experimental_message = 52001; // indicate that a message is intended for development and beta testing only and its definition may change at any time
optional uint32 wire_type = 52002; // override wire type specified in the MessageType enum
optional bool internal_only = 52003; // indicate that a message is intended for internal use only and should not be transmitted via the wire
}
/** Options for tagging field types */
extend google.protobuf.FieldOptions {
optional bool experimental_field = 53001; // indicate that a field is intended for development and beta testing only
}
/** Options for tagging files with protobuf definitions */
extend google.protobuf.FileOptions {
optional bool include_in_bitcoin_only = 60000; // definitions are available on BITCOIN_ONLY build
// intentionally identical to `bitcoin_only` from enum
}

View file

@ -42,7 +42,7 @@
// - Grab the latest Go plugin `go get -u google.golang.org/protobuf/cmd/protoc-gen-go`
// - Vendor in the latest Go plugin `govendor fetch google.golang.org/protobuf/...`
//go:generate protoc -I/usr/local/include:. --go_out=paths=source_relative:. messages.proto messages-common.proto messages-management.proto messages-ethereum.proto
//go:generate protoc -I/usr/local/include:. --go_out=paths=source_relative:. options.proto messages.proto messages-common.proto messages-management.proto messages-ethereum.proto messages-ethereum-eip712.proto
// Package trezor contains the wire protocol.
package trezor

View file

@ -19,6 +19,7 @@ package usbwallet
import (
"context"
"encoding/json"
"fmt"
"io"
"math/big"
@ -31,7 +32,8 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/karalabe/hid"
"github.com/ethereum/go-ethereum/signer/core/apitypes"
"github.com/karalabe/usb"
)
// Maximum time between wallet health checks to detect USB unplugs.
@ -68,7 +70,17 @@ type driver interface {
// or deny the transaction.
SignTx(path accounts.DerivationPath, tx *types.Transaction, chainID *big.Int) (common.Address, *types.Transaction, error)
SignTypedMessage(path accounts.DerivationPath, messageHash []byte, domainHash []byte) ([]byte, error)
// SignText sends a message to sign to the USB device and waits for the user to confirm
// or deny the signature.
SignText(path accounts.DerivationPath, text []byte) ([]byte, error)
// SignTypedHash sends a typed message to sign to the USB device and waits for the user to confirm
// or deny the signature.
SignTypedHash(path accounts.DerivationPath, messageHash []byte, domainHash []byte) ([]byte, error)
// SignedTypedData sends a typed data struct to sign to the USB device and waits for the user to confirm
// or deny the signature.
SignedTypedData(path accounts.DerivationPath, data apitypes.TypedData) ([]byte, error)
}
// wallet represents the common functionality shared by all USB hardware
@ -79,8 +91,8 @@ type wallet struct {
driver driver // Hardware implementation of the low level device operations
url *accounts.URL // Textual URL uniquely identifying this wallet
info hid.DeviceInfo // Known USB device infos about the wallet
device hid.Device // USB device advertising itself as a hardware wallet
info usb.DeviceInfo // Known USB device infos about the wallet
device usb.Device // USB device advertising itself as a hardware wallet
accounts []accounts.Account // List of derive accounts pinned on the hardware wallet
paths map[common.Address]accounts.DerivationPath // Known derivation paths for signing operations
@ -530,45 +542,29 @@ func (w *wallet) signHash(account accounts.Account, hash []byte) ([]byte, error)
// SignData signs keccak256(data). The mimetype parameter describes the type of data being signed
func (w *wallet) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) {
// Unless we are doing 712 signing, simply dispatch to signHash
if !(mimeType == accounts.MimetypeTypedData && len(data) == 66 && data[0] == 0x19 && data[1] == 0x01) {
return w.signHash(account, crypto.Keccak256(data))
}
// dispatch to 712 signing if the mimetype is TypedData and the format matches
w.stateLock.RLock() // Comms have own mutex, this is for the state fields
defer w.stateLock.RUnlock()
// If the wallet is closed, abort
if w.device == nil {
return nil, accounts.ErrWalletClosed
}
// Make sure the requested account is contained within
path, ok := w.paths[account.Address]
if !ok {
return nil, accounts.ErrUnknownAccount
}
// All infos gathered and metadata checks out, request signing
<-w.commsLock
defer func() { w.commsLock <- struct{}{} }()
// Ensure the device isn't screwed with while user confirmation is pending
// TODO(karalabe): remove if hotplug lands on Windows
w.hub.commsLock.Lock()
w.hub.commsPend++
w.hub.commsLock.Unlock()
defer func() {
w.hub.commsLock.Lock()
w.hub.commsPend--
w.hub.commsLock.Unlock()
}()
// Sign the transaction
signature, err := w.driver.SignTypedMessage(path, data[2:34], data[34:66])
path, done, err := w.lockAndDerivePath(account)
if err != nil {
return nil, err
}
return signature, nil
defer done()
if mimeType == accounts.MimetypeTypedData {
// If the data is exactly 66 bytes and starts with the 0x19 0x01 prefix, treat
// it as the raw EIP-712 hash pair (domain and message) to sign.
if len(data) == 66 && data[0] == 0x19 && data[1] == 0x01 {
return w.driver.SignTypedHash(path, data[2:34], data[34:66])
}
// Otherwise attempt to parse the data as a JSON encoded EIP-712 struct
// and sign it as such.
var typedData apitypes.TypedData
if err := json.Unmarshal(data, &typedData); err != nil {
return nil, fmt.Errorf("invalid typed data: %w", err)
}
return w.driver.SignedTypedData(path, typedData)
}
// If we're not doing 712 signing, simply dispatch to signHash
return w.signHash(account, crypto.Keccak256(data))
}
// SignDataWithPassphrase implements accounts.Wallet, attempting to sign the given
@ -578,8 +574,21 @@ func (w *wallet) SignDataWithPassphrase(account accounts.Account, passphrase, mi
return w.SignData(account, mimeType, data)
}
// SignText implements accounts.Wallet, signing the text as an EIP-712 personal
// message.
func (w *wallet) SignText(account accounts.Account, text []byte) ([]byte, error) {
return w.signHash(account, accounts.TextHash(text))
path, done, err := w.lockAndDerivePath(account)
if err != nil {
return nil, err
}
defer done()
// Sign the transaction
signature, err := w.driver.SignText(path, text)
if err != nil {
return nil, err
}
return signature, nil
}
// SignTx implements accounts.Wallet. It sends the transaction over to the Ledger
@ -590,33 +599,12 @@ func (w *wallet) SignText(account accounts.Account, text []byte) ([]byte, error)
// too old to sign EIP-155 transactions, but such is requested nonetheless, an error
// will be returned opposed to silently signing in Homestead mode.
func (w *wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
w.stateLock.RLock() // Comms have own mutex, this is for the state fields
defer w.stateLock.RUnlock()
// If the wallet is closed, abort
if w.device == nil {
return nil, accounts.ErrWalletClosed
path, done, err := w.lockAndDerivePath(account)
if err != nil {
return nil, err
}
// Make sure the requested account is contained within
path, ok := w.paths[account.Address]
if !ok {
return nil, accounts.ErrUnknownAccount
}
// All infos gathered and metadata checks out, request signing
<-w.commsLock
defer func() { w.commsLock <- struct{}{} }()
defer done()
// Ensure the device isn't screwed with while user confirmation is pending
// TODO(karalabe): remove if hotplug lands on Windows
w.hub.commsLock.Lock()
w.hub.commsPend++
w.hub.commsLock.Unlock()
defer func() {
w.hub.commsLock.Lock()
w.hub.commsPend--
w.hub.commsLock.Unlock()
}()
// Sign the transaction and verify the sender to avoid hardware fault surprises
sender, signed, err := w.driver.SignTx(path, tx, chainID)
if err != nil {
@ -628,11 +616,10 @@ func (w *wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID
return signed, nil
}
// SignTextWithPassphrase implements accounts.Wallet, however signing arbitrary
// data is not supported for Ledger wallets, so this method will always return
// an error.
// SignTextWithPassphrase implements accounts.Wallet, signing the text as an EIP-712
// personal message.
func (w *wallet) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) {
return w.SignText(account, accounts.TextHash(text))
return w.SignText(account, text)
}
// SignTxWithPassphrase implements accounts.Wallet, attempting to sign the given
@ -641,3 +628,38 @@ func (w *wallet) SignTextWithPassphrase(account accounts.Account, passphrase str
func (w *wallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
return w.SignTx(account, tx, chainID)
}
func (w *wallet) lockAndDerivePath(account accounts.Account) (accounts.DerivationPath, func(), error) {
w.stateLock.RLock() // Comms have own mutex, this is for the state fields
// If the wallet is closed, abort
if w.device == nil {
w.stateLock.RUnlock()
return nil, nil, accounts.ErrWalletClosed
}
// Make sure the requested account is contained within
path, ok := w.paths[account.Address]
if !ok {
w.stateLock.RUnlock()
return nil, nil, accounts.ErrUnknownAccount
}
// All infos gathered and metadata checks out, request signing
<-w.commsLock
// Ensure the device isn't screwed with while user confirmation is pending
// TODO(karalabe): remove if hotplug lands on Windows
w.hub.commsLock.Lock()
w.hub.commsPend++
w.hub.commsLock.Unlock()
done := func() {
w.stateLock.RUnlock()
w.commsLock <- struct{}{}
w.hub.commsLock.Lock()
w.hub.commsPend--
w.hub.commsLock.Unlock()
}
return path, done, nil
}

4
go.mod
View file

@ -45,7 +45,7 @@ require (
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c
github.com/jackpal/go-nat-pmp v1.0.2
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267
github.com/karalabe/hid v1.0.1-0.20240306101548-573246063e52
github.com/karalabe/usb v0.0.3-0.20231219215548-8627268f6b0a
github.com/kylelemons/godebug v1.1.0
github.com/mattn/go-colorable v0.1.13
github.com/mattn/go-isatty v0.0.20
@ -56,6 +56,7 @@ require (
github.com/protolambda/bls12-381-util v0.1.0
github.com/protolambda/zrnt v0.34.1
github.com/protolambda/ztyp v0.2.2
github.com/reserve-protocol/trezor v0.0.0-20190523030725-9e38328dde28
github.com/rs/cors v1.7.0
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible
github.com/status-im/keycard-go v0.2.0
@ -127,6 +128,7 @@ require (
github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/mitchellh/pointerstructure v1.2.0 // indirect
github.com/naoina/go-stringutil v0.1.0 // indirect
github.com/nsf/termbox-go v0.0.0-20190325093121-288510b9734e // indirect
github.com/opentracing/opentracing-go v1.1.0 // indirect
github.com/pion/dtls/v2 v2.2.7 // indirect
github.com/pion/logging v0.2.2 // indirect

9
go.sum
View file

@ -216,8 +216,8 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/karalabe/hid v1.0.1-0.20240306101548-573246063e52 h1:msKODTL1m0wigztaqILOtla9HeW1ciscYG4xjLtvk5I=
github.com/karalabe/hid v1.0.1-0.20240306101548-573246063e52/go.mod h1:qk1sX/IBgppQNcGCRoj90u6EGC056EBoIc1oEjCWla8=
github.com/karalabe/usb v0.0.3-0.20231219215548-8627268f6b0a h1:BGJeMa7efLsbPri2WJxtFOcjDPyjCdNlZWERE11GJAE=
github.com/karalabe/usb v0.0.3-0.20231219215548-8627268f6b0a/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
github.com/kilic/bls12-381 v0.1.0 h1:encrdjqKMEvabVQ7qYOKu1OvhqpK4s47wDYtNiPtlp4=
github.com/kilic/bls12-381 v0.1.0/go.mod h1:vDTTHJONJ6G+P2R74EhnyotQDTliQDnFEwhdmfzw1ig=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
@ -257,6 +257,7 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
@ -272,6 +273,8 @@ github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hz
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 h1:shk/vn9oCoOTmwcouEdwIeOtOGA/ELRUw/GwvxwfT+0=
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
github.com/nsf/termbox-go v0.0.0-20190325093121-288510b9734e h1:Vbib8wJAaMEF9jusI/kMSYMr/LtRzM7+F9MJgt/nH8k=
github.com/nsf/termbox-go v0.0.0-20190325093121-288510b9734e/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
@ -325,6 +328,8 @@ github.com/protolambda/ztyp v0.2.2 h1:rVcL3vBu9W/aV646zF6caLS/dyn9BN8NYiuJzicLNy
github.com/protolambda/ztyp v0.2.2/go.mod h1:9bYgKGqg3wJqT9ac1gI2hnVb0STQq7p/1lapqrqY1dU=
github.com/prysmaticlabs/gohashtree v0.0.4-beta h1:H/EbCuXPeTV3lpKeXGPpEV9gsUpkqOOVnWapUyeWro4=
github.com/prysmaticlabs/gohashtree v0.0.4-beta/go.mod h1:BFdtALS+Ffhg3lGQIHv9HDWuHS8cTvHZzrHWxwOtGOs=
github.com/reserve-protocol/trezor v0.0.0-20190523030725-9e38328dde28 h1:0XRrYV4bb+Y09cLQKsVE5gQ3HRebPXxY89zx+ZV2Fo0=
github.com/reserve-protocol/trezor v0.0.0-20190523030725-9e38328dde28/go.mod h1:Twg3zlQzleALAU+gR5xSK112gdh9YLcQ6owamVh0lbY=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=