Skip to content

Commit 190a4df

Browse files
Merge pull request #858 from LedgerHQ/feat/apa/multisig
Nested GCS & GCS in EIP-712
2 parents a160fba + 859d18c commit 190a4df

File tree

344 files changed

+4950
-362
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

344 files changed

+4950
-362
lines changed

client/src/ledger_app_clients/ethereum/client.py

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ class TrustedNameSource(IntEnum):
3636
DNS = 0x05
3737

3838

39+
class EIP712CalldataParamPresence(IntEnum):
40+
NONE = 0x00
41+
PRESENT_FILTERED = 0x01
42+
PRESENT_VERIFYING_CONTRACT = 0x02
43+
44+
3945
class SignMode(IntEnum):
4046
BASIC = 0x00
4147
STORE = 0x01
@@ -52,7 +58,7 @@ def __init__(self, backend: BackendInterface):
5258
def _exchange_async(self, payload: bytes):
5359
return self._backend.exchange_async_raw(payload)
5460

55-
def _exchange(self, payload: bytes):
61+
def _exchange(self, payload: bytes) -> RAPDU:
5662
return self._backend.exchange_raw(payload)
5763

5864
def response(self) -> Optional[RAPDU]:
@@ -155,6 +161,60 @@ def eip712_filtering_trusted_name(self,
155161
sig,
156162
discarded))
157163

164+
def eip712_filtering_calldata_info(self,
165+
index: int,
166+
value_filter_flag: bool,
167+
callee_filter_flag: int,
168+
chain_id_filter_flag: bool,
169+
selector_filter_flag: bool,
170+
amount_filter_flag: bool,
171+
spender_filter_flag: int,
172+
sig: bytes):
173+
return self._exchange(self._cmd_builder.eip712_filtering_calldata_info(index,
174+
value_filter_flag,
175+
callee_filter_flag,
176+
chain_id_filter_flag,
177+
selector_filter_flag,
178+
amount_filter_flag,
179+
spender_filter_flag,
180+
sig))
181+
182+
def eip712_filtering_calldata_value(self,
183+
index: int,
184+
sig: bytes,
185+
discarded: bool):
186+
return self._exchange(self._cmd_builder.eip712_filtering_calldata_value(index, sig, discarded))
187+
188+
def eip712_filtering_calldata_callee(self,
189+
index: int,
190+
sig: bytes,
191+
discarded: bool):
192+
return self._exchange(self._cmd_builder.eip712_filtering_calldata_callee(index, sig, discarded))
193+
194+
def eip712_filtering_calldata_chain_id(self,
195+
index: int,
196+
sig: bytes,
197+
discarded: bool):
198+
return self._exchange(self._cmd_builder.eip712_filtering_calldata_chain_id(index, sig, discarded))
199+
200+
def eip712_filtering_calldata_selector(self,
201+
index: int,
202+
sig: bytes,
203+
discarded: bool):
204+
return self._exchange(self._cmd_builder.eip712_filtering_calldata_selector(index, sig, discarded))
205+
206+
def eip712_filtering_calldata_amount(self,
207+
index: int,
208+
sig: bytes,
209+
discarded: bool):
210+
return self._exchange(self._cmd_builder.eip712_filtering_calldata_amount(index, sig, discarded))
211+
212+
def eip712_filtering_calldata_spender(self,
213+
index: int,
214+
sig: bytes,
215+
discarded: bool):
216+
return self._exchange(self._cmd_builder.eip712_filtering_calldata_spender(index, sig, discarded))
217+
158218
def eip712_filtering_raw(self, name: str, sig: bytes, discarded: bool):
159219
return self._exchange_async(self._cmd_builder.eip712_filtering_raw(name, sig, discarded))
160220

@@ -176,16 +236,19 @@ def serialize_tx(self, tx_params: dict) -> tuple[bytes, bytes]:
176236
return encoded_tx, tx_hash
177237

178238
def sign(self,
179-
bip32_path: str,
180-
tx_params: dict,
239+
bip32_path: Optional[str] = None,
240+
tx_params: Optional[dict] = None,
181241
mode: SignMode = SignMode.BASIC):
182-
tx, _ = self.serialize_tx(tx_params)
183-
chunks = self._cmd_builder.sign(bip32_path, tx, mode)
242+
if tx_params is None:
243+
tx = None
244+
else:
245+
tx, _ = self.serialize_tx(tx_params)
246+
chunks = self._cmd_builder.sign(mode, bip32_path, tx)
184247
for chunk in chunks[:-1]:
185248
self._exchange(chunk)
186249
return self._exchange_async(chunks[-1])
187250

188-
def get_challenge(self):
251+
def get_challenge(self) -> RAPDU:
189252
return self._exchange(self._cmd_builder.get_challenge())
190253

191254
def get_public_addr(self,
@@ -421,6 +484,12 @@ def provide_transaction_info(self, payload: bytes) -> RAPDU:
421484
self._exchange(chunk)
422485
return self._exchange(chunks[-1])
423486

487+
def provide_transaction_field_desc(self, payload: bytes) -> RAPDU:
488+
chunks = self._cmd_builder.provide_transaction_field_desc(payload)
489+
for chunk in chunks[:-1]:
490+
self._exchange(chunk)
491+
return self._exchange(chunks[-1])
492+
424493
def opt_in_tx_simulation(self):
425494
return self._exchange_async(self._cmd_builder.opt_in_tx_simulation())
426495

client/src/ledger_app_clients/ethereum/command_builder.py

Lines changed: 126 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class InsType(IntEnum):
2929
PROVIDE_TRUSTED_NAME = 0x22
3030
PROVIDE_ENUM_VALUE = 0x24
3131
PROVIDE_TRANSACTION_INFO = 0x26
32+
PROVIDE_TRANSACTION_FIELD_DESC = 0x28
3233
PROVIDE_PROXY_INFO = 0x2a
3334
PROVIDE_NETWORK_INFORMATION = 0x30
3435
PROVIDE_TX_SIMULATION = 0x32
@@ -55,6 +56,13 @@ class P2Type(IntEnum):
5556
FILTERING_ACTIVATE = 0x00
5657
FILTERING_DISCARDED_PATH = 0x01
5758
FILTERING_MESSAGE_INFO = 0x0f
59+
FILTERING_CALLDATA_SPENDER = 0xf4
60+
FILTERING_CALLDATA_AMOUNT = 0xf5
61+
FILTERING_CALLDATA_SELECTOR = 0xf6
62+
FILTERING_CALLDATA_CHAIN_ID = 0xf7
63+
FILTERING_CALLDATA_CALLEE = 0xf8
64+
FILTERING_CALLDATA_VALUE = 0xf9
65+
FILTERING_CALLDATA_INFO = 0xfa
5866
FILTERING_TRUSTED_NAME = 0xfb
5967
FILTERING_DATETIME = 0xfc
6068
FILTERING_TOKEN_ADDR_CHECK = 0xfd
@@ -145,8 +153,7 @@ def eip712_send_struct_impl_struct_field(self, data: bytearray) -> list[bytes]:
145153
chunks = []
146154
# Add a 16-bit integer with the data's byte length (network byte order)
147155
data_w_length = bytearray()
148-
data_w_length.append((len(data) & 0xff00) >> 8)
149-
data_w_length.append(len(data) & 0x00ff)
156+
data_w_length += struct.pack(">H", len(data))
150157
data_w_length += data
151158
while len(data_w_length) > 0:
152159
p1 = P1Type.PARTIAL_SEND if len(data_w_length) > 0xff else P1Type.COMPLETE_SEND
@@ -261,6 +268,108 @@ def eip712_filtering_trusted_name(self,
261268
P2Type.FILTERING_TRUSTED_NAME,
262269
data)
263270

271+
def eip712_filtering_calldata_info(self,
272+
index: int,
273+
value_filter_flag: bool,
274+
callee_filter_flag: int,
275+
chain_id_filter_flag: bool,
276+
selector_filter_flag: bool,
277+
amount_filter_flag: bool,
278+
spender_filter_flag: int,
279+
sig: bytes) -> bytes:
280+
data = bytearray()
281+
data += struct.pack(">B", index)
282+
data += struct.pack(">B", value_filter_flag)
283+
data += struct.pack(">?", callee_filter_flag)
284+
data += struct.pack(">B", chain_id_filter_flag)
285+
data += struct.pack(">B", selector_filter_flag)
286+
data += struct.pack(">B", amount_filter_flag)
287+
data += struct.pack(">?", spender_filter_flag)
288+
data.append(len(sig))
289+
data += sig
290+
return self._serialize(InsType.EIP712_SEND_FILTERING,
291+
int(False),
292+
P2Type.FILTERING_CALLDATA_INFO,
293+
data)
294+
295+
def eip712_filtering_calldata_value(self,
296+
index: int,
297+
sig: bytes,
298+
discarded: bool):
299+
data = bytearray()
300+
data += struct.pack(">B", index)
301+
data.append(len(sig))
302+
data += sig
303+
return self._serialize(InsType.EIP712_SEND_FILTERING,
304+
int(discarded),
305+
P2Type.FILTERING_CALLDATA_VALUE,
306+
data)
307+
308+
def eip712_filtering_calldata_callee(self,
309+
index: int,
310+
sig: bytes,
311+
discarded: bool):
312+
data = bytearray()
313+
data += struct.pack(">B", index)
314+
data.append(len(sig))
315+
data += sig
316+
return self._serialize(InsType.EIP712_SEND_FILTERING,
317+
int(discarded),
318+
P2Type.FILTERING_CALLDATA_CALLEE,
319+
data)
320+
321+
def eip712_filtering_calldata_chain_id(self,
322+
index: int,
323+
sig: bytes,
324+
discarded: bool):
325+
data = bytearray()
326+
data += struct.pack(">B", index)
327+
data.append(len(sig))
328+
data += sig
329+
return self._serialize(InsType.EIP712_SEND_FILTERING,
330+
int(discarded),
331+
P2Type.FILTERING_CALLDATA_CHAIN_ID,
332+
data)
333+
334+
def eip712_filtering_calldata_selector(self,
335+
index: int,
336+
sig: bytes,
337+
discarded: bool):
338+
data = bytearray()
339+
data += struct.pack(">B", index)
340+
data.append(len(sig))
341+
data += sig
342+
return self._serialize(InsType.EIP712_SEND_FILTERING,
343+
int(discarded),
344+
P2Type.FILTERING_CALLDATA_SELECTOR,
345+
data)
346+
347+
def eip712_filtering_calldata_amount(self,
348+
index: int,
349+
sig: bytes,
350+
discarded: bool):
351+
data = bytearray()
352+
data += struct.pack(">B", index)
353+
data.append(len(sig))
354+
data += sig
355+
return self._serialize(InsType.EIP712_SEND_FILTERING,
356+
int(discarded),
357+
P2Type.FILTERING_CALLDATA_AMOUNT,
358+
data)
359+
360+
def eip712_filtering_calldata_spender(self,
361+
index: int,
362+
sig: bytes,
363+
discarded: bool):
364+
data = bytearray()
365+
data += struct.pack(">B", index)
366+
data.append(len(sig))
367+
data += sig
368+
return self._serialize(InsType.EIP712_SEND_FILTERING,
369+
int(discarded),
370+
P2Type.FILTERING_CALLDATA_SPENDER,
371+
data)
372+
264373
def eip712_filtering_raw(self, name: str, sig: bytes, discarded: bool) -> bytes:
265374
return self._serialize(InsType.EIP712_SEND_FILTERING,
266375
int(discarded),
@@ -280,15 +389,21 @@ def set_external_plugin(self, plugin_name: str, contract_address: bytes, selecto
280389
0x00,
281390
data)
282391

283-
def sign(self, bip32_path: str, rlp_data: bytes, p2: int) -> list[bytes]:
284-
apdus = []
285-
payload = pack_derivation_path(bip32_path)
286-
payload += rlp_data
392+
def sign(self,
393+
mode: int,
394+
bip32_path: Optional[str] = None,
395+
rlp_data: Optional[bytes] = None) -> list[bytes]:
396+
apdus: list[bytes] = []
397+
payload = bytearray()
398+
if bip32_path is not None:
399+
payload = pack_derivation_path(bip32_path)
400+
if rlp_data is not None:
401+
payload += rlp_data
287402
p1 = P1Type.SIGN_FIRST_CHUNK
288-
while len(payload) > 0:
403+
while (len(payload) > 0) or ((len(apdus) == 0) and (len(payload) == 0)):
289404
apdus.append(self._serialize(InsType.SIGN,
290405
p1,
291-
p2,
406+
mode,
292407
payload[:0xff]))
293408
payload = payload[0xff:]
294409
p1 = P1Type.SIGN_SUBSQT_CHUNK
@@ -476,6 +591,9 @@ def provide_enum_value(self, tlv_payload: bytes) -> list[bytes]:
476591
def provide_transaction_info(self, tlv_payload: bytes) -> list[bytes]:
477592
return self.common_tlv_serialize(InsType.PROVIDE_TRANSACTION_INFO, tlv_payload)
478593

594+
def provide_transaction_field_desc(self, tlv_payload: bytes) -> list[bytes]:
595+
return self.common_tlv_serialize(InsType.PROVIDE_TRANSACTION_FIELD_DESC, tlv_payload)
596+
479597
def opt_in_tx_simulation(self) -> bytes:
480598
# Serialize the payload
481599
return self._serialize(InsType.PROVIDE_TX_SIMULATION, P1Type.OPT_IN_TX_CHECK, 0x00)

0 commit comments

Comments
 (0)