feat(common): add network definition support for `EthereumSignAuth7702`
What changed, and why it matters
This commit adds an optional 'definitions' field to the EthereumSignAuth7702 message used in Trezor's firmware. This field lets the device receive extra network information (like chain details) alongside an authorization request. The change itself is a routine protobuf schema and generated-code update; it does not contain any obvious security bug, but it is part of a feature that handles cryptographic signing, so it deserves normal review attention.
Review the companion implementation commit(s) that consume EthereumDefinitions in EthereumSignAuth7702 to ensure chain_id and network metadata are validated before signing, and that untrusted definitions cannot spoof the intended network or trick the user into authorizing a delegate on the wrong chain.
Security signals we found
Adds optional network-definition payload to an experimental signing message (EIP-7702 authorization)
No validation or trust-boundary logic shown in the diff
Generated protobuf code only; implementation behavior is not visible here
Evidence from the diff
The diff extends the experimental EthereumSignAuth7702 message with an optional EthereumDefinitions submessage (field 5). Corresponding generated bindings are updated in Python, Rust, and the core messages module. No logic for parsing, validating, or acting on these definitions is visible in this commit; it only wires the field through the protocol and generated stubs. The change is additive and backward-compatible because the field is optional.
Changed components
common/protob/messages-ethereum.protocore/src/trezor/messages.pypython/src/trezorlib/messages.pyrust/trezor-client/src/protos/generated/messages_ethereum.rsInspect captured patch +43 / −11
diff --git a/common/protob/messages-ethereum.proto b/common/protob/messages-ethereum.proto
index e6cdb28d..c87ac21d 100644
--- a/common/protob/messages-ethereum.proto
+++ b/common/protob/messages-ethereum.proto
@@ -232,10 +232,11 @@ message EthereumDefinitions {
message EthereumSignAuth7702 {
option (experimental_message) = true;
- repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
- required uint64 chain_id = 2; // chain id on which the authorization is valid, or 0 for all
- required string delegate = 3; // address of the code that will be delegated to the EOA
- required uint64 nonce = 4; // account nonce for which the authorization is valid
+ repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
+ required uint64 chain_id = 2; // chain id on which the authorization is valid, or 0 for all
+ required string delegate = 3; // address of the code that will be delegated to the EOA
+ required uint64 nonce = 4; // account nonce for which the authorization is valid
+ optional EthereumDefinitions definitions = 5; // network definitions (for chain ID)
}
/**
diff --git a/core/src/trezor/messages.py b/core/src/trezor/messages.py
index 62043346..fdd5a56f 100644
--- a/core/src/trezor/messages.py
+++ b/core/src/trezor/messages.py
@@ -4301,6 +4301,7 @@ if TYPE_CHECKING:
chain_id: "int"
delegate: "str"
nonce: "int"
+ definitions: "EthereumDefinitions | None"
def __init__(
self,
@@ -4309,6 +4310,7 @@ if TYPE_CHECKING:
delegate: "str",
nonce: "int",
address_n: "list[int] | None" = None,
+ definitions: "EthereumDefinitions | None" = None,
) -> None:
pass
diff --git a/python/src/trezorlib/messages.py b/python/src/trezorlib/messages.py
index 0b726ac6..6c53b458 100644
--- a/python/src/trezorlib/messages.py
+++ b/python/src/trezorlib/messages.py
@@ -5889,6 +5889,7 @@ class EthereumSignAuth7702(protobuf.MessageType):
2: protobuf.Field("chain_id", "uint64", repeated=False, required=True),
3: protobuf.Field("delegate", "string", repeated=False, required=True),
4: protobuf.Field("nonce", "uint64", repeated=False, required=True),
+ 5: protobuf.Field("definitions", "EthereumDefinitions", repeated=False, required=False, default=None),
}
def __init__(
@@ -5898,11 +5899,13 @@ class EthereumSignAuth7702(protobuf.MessageType):
delegate: "str",
nonce: "int",
address_n: Optional[Sequence["int"]] = None,
+ definitions: Optional["EthereumDefinitions"] = None,
) -> None:
self.address_n: Sequence["int"] = address_n if address_n is not None else []
self.chain_id = chain_id
self.delegate = delegate
self.nonce = nonce
+ self.definitions = definitions
class EthereumAuth7702Signature(protobuf.MessageType):
diff --git a/rust/trezor-client/src/protos/generated/messages_ethereum.rs b/rust/trezor-client/src/protos/generated/messages_ethereum.rs
index 2dda2683..9cd2fb0e 100644
--- a/rust/trezor-client/src/protos/generated/messages_ethereum.rs
+++ b/rust/trezor-client/src/protos/generated/messages_ethereum.rs
@@ -4924,6 +4924,8 @@ pub struct EthereumSignAuth7702 {
pub delegate: ::std::option::Option<::std::string::String>,
// @@protoc_insertion_point(field:hw.trezor.messages.ethereum.EthereumSignAuth7702.nonce)
pub nonce: ::std::option::Option<u64>,
+ // @@protoc_insertion_point(field:hw.trezor.messages.ethereum.EthereumSignAuth7702.definitions)
+ pub definitions: ::protobuf::MessageField<EthereumDefinitions>,
// special fields
// @@protoc_insertion_point(special_field:hw.trezor.messages.ethereum.EthereumSignAuth7702.special_fields)
pub special_fields: ::protobuf::SpecialFields,
@@ -5015,7 +5017,7 @@ impl EthereumSignAuth7702 {
}
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
- let mut fields = ::std::vec::Vec::with_capacity(4);
+ let mut fields = ::std::vec::Vec::with_capacity(5);
let mut oneofs = ::std::vec::Vec::with_capacity(0);
fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>(
"address_n",
@@ -5037,6 +5039,11 @@ impl EthereumSignAuth7702 {
|m: &EthereumSignAuth7702| { &m.nonce },
|m: &mut EthereumSignAuth7702| { &mut m.nonce },
));
+ fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, EthereumDefinitions>(
+ "definitions",
+ |m: &EthereumSignAuth7702| { &m.definitions },
+ |m: &mut EthereumSignAuth7702| { &mut m.definitions },
+ ));
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<EthereumSignAuth7702>(
"EthereumSignAuth7702",
fields,
@@ -5058,6 +5065,11 @@ impl ::protobuf::Message for EthereumSignAuth7702 {
if self.nonce.is_none() {
return false;
}
+ for v in &self.definitions {
+ if !v.is_initialized() {
+ return false;
+ }
+ };
true
}
@@ -5079,6 +5091,9 @@ impl ::protobuf::Message for EthereumSignAuth7702 {
32 => {
self.nonce = ::std::option::Option::Some(is.read_uint64()?);
},
+ 42 => {
+ ::protobuf::rt::read_singular_message_into_field(is, &mut self.definitions)?;
+ },
tag => {
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
},
@@ -5103,6 +5118,10 @@ impl ::protobuf::Message for EthereumSignAuth7702 {
if let Some(v) = self.nonce {
my_size += ::protobuf::rt::uint64_size(4, v);
}
+ if let Some(v) = self.definitions.as_ref() {
+ let len = v.compute_size();
+ my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len;
+ }
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
self.special_fields.cached_size().set(my_size as u32);
my_size
@@ -5121,6 +5140,9 @@ impl ::protobuf::Message for EthereumSignAuth7702 {
if let Some(v) = self.nonce {
os.write_uint64(4, v)?;
}
+ if let Some(v) = self.definitions.as_ref() {
+ ::protobuf::rt::write_message_field_with_cached_size(5, v, os)?;
+ }
os.write_unknown_fields(self.special_fields.unknown_fields())?;
::std::result::Result::Ok(())
}
@@ -5142,6 +5164,7 @@ impl ::protobuf::Message for EthereumSignAuth7702 {
self.chain_id = ::std::option::Option::None;
self.delegate = ::std::option::Option::None;
self.nonce = ::std::option::Option::None;
+ self.definitions.clear();
self.special_fields.clear();
}
@@ -5151,6 +5174,7 @@ impl ::protobuf::Message for EthereumSignAuth7702 {
chain_id: ::std::option::Option::None,
delegate: ::std::option::Option::None,
nonce: ::std::option::Option::None,
+ definitions: ::protobuf::MessageField::none(),
special_fields: ::protobuf::SpecialFields::new(),
};
&instance
@@ -5503,15 +5527,17 @@ static file_descriptor_proto_data: &'static [u8] = b"\
\x20\x02(\tR\x07address\"\x99\x01\n\x13EthereumDefinitions\x12'\n\x0fenc\
oded_network\x18\x01\x20\x01(\x0cR\x0eencodedNetwork\x12#\n\rencoded_tok\
en\x18\x02\x20\x01(\x0cR\x0cencodedToken\x124\n\x16encoded_display_forma\
- t\x18\x03\x20\x01(\x0cR\x14encodedDisplayFormat\"\x86\x01\n\x14EthereumS\
+ t\x18\x03\x20\x01(\x0cR\x14encodedDisplayFormat\"\xda\x01\n\x14EthereumS\
ignAuth7702\x12\x1b\n\taddress_n\x18\x01\x20\x03(\rR\x08addressN\x12\x19\
\n\x08chain_id\x18\x02\x20\x02(\x04R\x07chainId\x12\x1a\n\x08delegate\
\x18\x03\x20\x02(\tR\x08delegate\x12\x14\n\x05nonce\x18\x04\x20\x02(\x04\
- R\x05nonce:\x04\x88\xb2\x19\x01\"\x84\x01\n\x19EthereumAuth7702Signature\
- \x12\x1f\n\x0bsignature_v\x18\x01\x20\x02(\rR\nsignatureV\x12\x1f\n\x0bs\
- ignature_r\x18\x02\x20\x02(\x0cR\nsignatureR\x12\x1f\n\x0bsignature_s\
- \x18\x03\x20\x02(\x0cR\nsignatureS:\x04\x88\xb2\x19\x01B<\n#com.satoshil\
- abs.trezor.lib.protobufB\x15TrezorMessageEthereum\
+ R\x05nonce\x12R\n\x0bdefinitions\x18\x05\x20\x01(\x0b20.hw.trezor.messag\
+ es.ethereum.EthereumDefinitionsR\x0bdefinitions:\x04\x88\xb2\x19\x01\"\
+ \x84\x01\n\x19EthereumAuth7702Signature\x12\x1f\n\x0bsignature_v\x18\x01\
+ \x20\x02(\rR\nsignatureV\x12\x1f\n\x0bsignature_r\x18\x02\x20\x02(\x0cR\
+ \nsignatureR\x12\x1f\n\x0bsignature_s\x18\x03\x20\x02(\x0cR\nsignatureS:\
+ \x04\x88\xb2\x19\x01B<\n#com.satoshilabs.trezor.lib.protobufB\x15TrezorM\
+ essageEthereum\
";
/// `FileDescriptorProto` object which was a source for this generated file
Why this scored 21/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.