chore(clear_signing): support native currency addresses
What changed, and why it matters
This commit adds a new optional data field called native_currency_address to the message format used for Trezor's Ethereum clear-signing feature. It only changes protocol definitions and generated serialization code across Python, Rust, and the core firmware. There is no actual logic that uses the new field yet, and the commit is marked as a routine chore with no changelog entry. It does not appear to fix or introduce a security vulnerability on its own.
No immediate security action is required for this commit alone. When reviewing the follow-up code that consumes native_currency_address, verify that each address is validated (e.g., length, checksum/encoding), that repeated fields have reasonable size limits, and that the field cannot be used to spoof token or currency display in clear-signing prompts.
Security signals we found
Adds a new repeated bytes field to an existing protobuf message used in Ethereum transaction display metadata
No bounds checks or validation logic are visible in the diff because no consumer code is included
Generated protobuf code handles the field with standard repeated-bytes serialization
Commit is tagged [no changelog] and titled as a chore, indicating routine feature plumbing rather than a security fix
Evidence from the diff
The change extends the EthereumERC7730FieldInfo protobuf message with a repeated bytes field native_currency_address (field number 14). Corresponding generated code is updated in core/src/trezor/messages.py, python/src/trezorlib/messages.py, and rust/trezor-client/src/protos/generated/messages_definitions.rs, including descriptor data, accessors, serialization, deserialization, and default empty vectors/sequences. No parsing, validation, display, or signing logic consumes this field in the supplied diff. The commit title frames it as clear-signing support for native currency addresses.
Changed components
common/protob/messages-definitions.protocore/src/trezor/messages.pypython/src/trezorlib/messages.pyrust/trezor-client/src/protos/generated/messages_definitions.rsInspect captured patch +37 / −12
### common/protob/messages-definitions.proto
@@ -186,6 +186,7 @@ message EthereumERC7730FieldInfo {
optional EthereumERC7730Path token_path = 4;
optional bytes threshold = 5;
optional string threshold_message = 13;
+ repeated bytes native_currency_address = 14;
// UnitFormatter params
optional uint32 decimals = 6;
### core/src/trezor/messages.py
@@ -3404,6 +3404,7 @@ class EthereumERC7730FieldInfo(protobuf.MessageType):
token_path: "EthereumERC7730Path | None"
threshold: "AnyBytes | None"
threshold_message: "str | None"
+ native_currency_address: "list[AnyBytes]"
decimals: "int | None"
base: "str | None"
prefix: "bool | None"
@@ -3418,6 +3419,7 @@ def __init__(
path: "EthereumERC7730Path",
label: "str",
formatter: "EthereumERC7730FieldFormatterType",
+ native_currency_address: "list[AnyBytes] | None" = None,
enum_values: "list[EthereumERC7730EnumEntry] | None" = None,
token_path: "EthereumERC7730Path | None" = None,
threshold: "AnyBytes | None" = None,
### python/src/trezorlib/messages.py
@@ -4883,6 +4883,7 @@ class EthereumERC7730FieldInfo(protobuf.MessageType):
4: protobuf.Field("token_path", "EthereumERC7730Path", repeated=False, required=False, default=None),
5: protobuf.Field("threshold", "bytes", repeated=False, required=False, default=None),
13: protobuf.Field("threshold_message", "string", repeated=False, required=False, default=None),
+ 14: protobuf.Field("native_currency_address", "bytes", repeated=True, required=False, default=None),
6: protobuf.Field("decimals", "uint32", repeated=False, required=False, default=None),
7: protobuf.Field("base", "string", repeated=False, required=False, default=None),
8: protobuf.Field("prefix", "bool", repeated=False, required=False, default=None),
@@ -4898,6 +4899,7 @@ def __init__(
path: "EthereumERC7730Path",
label: "str",
formatter: "EthereumERC7730FieldFormatterType",
+ native_currency_address: Optional[Sequence["bytes"]] = None,
enum_values: Optional[Sequence["EthereumERC7730EnumEntry"]] = None,
token_path: Optional["EthereumERC7730Path"] = None,
threshold: Optional["bytes"] = None,
@@ -4909,6 +4911,7 @@ def __init__(
callee_path: Optional["EthereumERC7730Path"] = None,
selector: Optional["bytes"] = None,
) -> None:
+ self.native_currency_address: Sequence["bytes"] = native_currency_address if native_currency_address is not None else []
self.enum_values: Sequence["EthereumERC7730EnumEntry"] = enum_values if enum_values is not None else []
self.path = path
self.label = label
### rust/trezor-client/src/protos/generated/messages_definitions.rs
@@ -1661,6 +1661,8 @@ pub struct EthereumERC7730FieldInfo {
pub threshold: ::std::option::Option<::std::vec::Vec<u8>>,
// @@protoc_insertion_point(field:hw.trezor.messages.definitions.EthereumERC7730FieldInfo.threshold_message)
pub threshold_message: ::std::option::Option<::std::string::String>,
+ // @@protoc_insertion_point(field:hw.trezor.messages.definitions.EthereumERC7730FieldInfo.native_currency_address)
+ pub native_currency_address: ::std::vec::Vec<::std::vec::Vec<u8>>,
// @@protoc_insertion_point(field:hw.trezor.messages.definitions.EthereumERC7730FieldInfo.decimals)
pub decimals: ::std::option::Option<u32>,
// @@protoc_insertion_point(field:hw.trezor.messages.definitions.EthereumERC7730FieldInfo.base)
@@ -1968,7 +1970,7 @@ impl EthereumERC7730FieldInfo {
}
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
- let mut fields = ::std::vec::Vec::with_capacity(13);
+ let mut fields = ::std::vec::Vec::with_capacity(14);
let mut oneofs = ::std::vec::Vec::with_capacity(0);
fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, EthereumERC7730Path>(
"path",
@@ -2000,6 +2002,11 @@ impl EthereumERC7730FieldInfo {
|m: &EthereumERC7730FieldInfo| { &m.threshold_message },
|m: &mut EthereumERC7730FieldInfo| { &mut m.threshold_message },
));
+ fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>(
+ "native_currency_address",
+ |m: &EthereumERC7730FieldInfo| { &m.native_currency_address },
+ |m: &mut EthereumERC7730FieldInfo| { &mut m.native_currency_address },
+ ));
fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
"decimals",
|m: &EthereumERC7730FieldInfo| { &m.decimals },
@@ -2100,6 +2107,9 @@ impl ::protobuf::Message for EthereumERC7730FieldInfo {
106 => {
self.threshold_message = ::std::option::Option::Some(is.read_string()?);
},
+ 114 => {
+ self.native_currency_address.push(is.read_bytes()?);
+ },
48 => {
self.decimals = ::std::option::Option::Some(is.read_uint32()?);
},
@@ -2153,6 +2163,9 @@ impl ::protobuf::Message for EthereumERC7730FieldInfo {
if let Some(v) = self.threshold_message.as_ref() {
my_size += ::protobuf::rt::string_size(13, &v);
}
+ for value in &self.native_currency_address {
+ my_size += ::protobuf::rt::bytes_size(14, &value);
+ };
if let Some(v) = self.decimals {
my_size += ::protobuf::rt::uint32_size(6, v);
}
@@ -2200,6 +2213,9 @@ impl ::protobuf::Message for EthereumERC7730FieldInfo {
if let Some(v) = self.threshold_message.as_ref() {
os.write_string(13, v)?;
}
+ for v in &self.native_currency_address {
+ os.write_bytes(14, &v)?;
+ };
if let Some(v) = self.decimals {
os.write_uint32(6, v)?;
}
@@ -2244,6 +2260,7 @@ impl ::protobuf::Message for EthereumERC7730FieldInfo {
self.token_path.clear();
self.threshold = ::std::option::Option::None;
self.threshold_message = ::std::option::Option::None;
+ self.native_currency_address.clear();
self.decimals = ::std::option::Option::None;
self.base = ::std::option::Option::None;
self.prefix = ::std::option::Option::None;
@@ -2262,6 +2279,7 @@ impl ::protobuf::Message for EthereumERC7730FieldInfo {
token_path: ::protobuf::MessageField::none(),
threshold: ::std::option::Option::None,
threshold_message: ::std::option::Option::None,
+ native_currency_address: ::std::vec::Vec::new(),
decimals: ::std::option::Option::None,
base: ::std::option::Option::None,
prefix: ::std::option::Option::None,
@@ -3374,24 +3392,25 @@ static file_descriptor_proto_data: &'static [u8] = b"\
h\x18\x02\x20\x01(\x0e2<.hw.trezor.messages.definitions.EthereumERC7730C\
ontainerPathR\rcontainerPath\x12\x1f\n\x0bconst_value\x18\x03\x20\x01(\t\
R\nconstValue\x12\x1f\n\x0bslice_start\x18\x04\x20\x01(\x11R\nsliceStart\
- \x12\x1b\n\tslice_end\x18\x05\x20\x01(\x11R\x08sliceEnd\"\xbe\x05\n\x18E\
+ \x12\x1b\n\tslice_end\x18\x05\x20\x01(\x11R\x08sliceEnd\"\xf6\x05\n\x18E\
thereumERC7730FieldInfo\x12G\n\x04path\x18\x01\x20\x02(\x0b23.hw.trezor.\
messages.definitions.EthereumERC7730PathR\x04path\x12\x14\n\x05label\x18\
\x02\x20\x02(\tR\x05label\x12_\n\tformatter\x18\x03\x20\x02(\x0e2A.hw.tr\
ezor.messages.definitions.EthereumERC7730FieldFormatterTypeR\tformatter\
\x12R\n\ntoken_path\x18\x04\x20\x01(\x0b23.hw.trezor.messages.definition\
s.EthereumERC7730PathR\ttokenPath\x12\x1c\n\tthreshold\x18\x05\x20\x01(\
\x0cR\tthreshold\x12+\n\x11threshold_message\x18\r\x20\x01(\tR\x10thresh\
- oldMessage\x12\x1a\n\x08decimals\x18\x06\x20\x01(\rR\x08decimals\x12\x12\
- \n\x04base\x18\x07\x20\x01(\tR\x04base\x12\x16\n\x06prefix\x18\x08\x20\
- \x01(\x08R\x06prefix\x12.\n\x13const_token_address\x18\t\x20\x01(\x0cR\
- \x11constTokenAddress\x12T\n\x0bcallee_path\x18\n\x20\x01(\x0b23.hw.trez\
- or.messages.definitions.EthereumERC7730PathR\ncalleePath\x12\x1a\n\x08se\
- lector\x18\x0b\x20\x01(\x0cR\x08selector\x12Y\n\x0benum_values\x18\x0c\
- \x20\x03(\x0b28.hw.trezor.messages.definitions.EthereumERC7730EnumEntryR\
- \nenumValues\"B\n\x18EthereumERC7730EnumEntry\x12\x10\n\x03key\x18\x01\
- \x20\x02(\rR\x03key\x12\x14\n\x05value\x18\x02\x20\x02(\tR\x05value\"\
- \xfa\x02\n\x19EthereumDisplayFormatInfo\x12\x19\n\x08chain_id\x18\x01\
+ oldMessage\x126\n\x17native_currency_address\x18\x0e\x20\x03(\x0cR\x15na\
+ tiveCurrencyAddress\x12\x1a\n\x08decimals\x18\x06\x20\x01(\rR\x08decimal\
+ s\x12\x12\n\x04base\x18\x07\x20\x01(\tR\x04base\x12\x16\n\x06prefix\x18\
+ \x08\x20\x01(\x08R\x06prefix\x12.\n\x13const_token_address\x18\t\x20\x01\
+ (\x0cR\x11constTokenAddress\x12T\n\x0bcallee_path\x18\n\x20\x01(\x0b23.h\
+ w.trezor.messages.definitions.EthereumERC7730PathR\ncalleePath\x12\x1a\n\
+ \x08selector\x18\x0b\x20\x01(\x0cR\x08selector\x12Y\n\x0benum_values\x18\
+ \x0c\x20\x03(\x0b28.hw.trezor.messages.definitions.EthereumERC7730EnumEn\
+ tryR\nenumValues\"B\n\x18EthereumERC7730EnumEntry\x12\x10\n\x03key\x18\
+ \x01\x20\x02(\rR\x03key\x12\x14\n\x05value\x18\x02\x20\x02(\tR\x05value\
+ \"\xfa\x02\n\x19EthereumDisplayFormatInfo\x12\x19\n\x08chain_id\x18\x01\
\x20\x02(\x04R\x07chainId\x12\x18\n\x07address\x18\x02\x20\x02(\x0cR\x07\
address\x12\x19\n\x08func_sig\x18\x03\x20\x02(\x0cR\x07funcSig\x12\x16\n\
\x06intent\x18\x04\x20\x02(\tR\x06intent\x12i\n\x15parameter_definitions\Why this scored 20/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.