Skip to content

Commit 2affdc2

Browse files
Merge pull request #732 from LedgerHQ/feat/apa/contract_proxy
Proxy contract support
2 parents e199e4c + fd742ff commit 2affdc2

File tree

48 files changed

+2456
-20
lines changed

Some content is hidden

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

48 files changed

+2456
-20
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,6 @@ default.profdata
2525
fuzz-*.log
2626
crash-*
2727
report.html
28+
29+
# LSP
30+
.cache/

client/src/ledger_app_clients/ethereum/client.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,21 @@ def provide_network_information(self,
570570
assert response.status == StatusWord.OK
571571

572572
def provide_enum_value(self, payload: bytes) -> RAPDU:
573+
if self._pki_client is None:
574+
print(f"Ledger-PKI Not supported on '{self._firmware.name}'")
575+
else:
576+
# pylint: disable=line-too-long
577+
if self._firmware == Firmware.NANOSP:
578+
cert_apdu = "01010102010211040000000212010013020002140101160400000000200863616C6C646174613002000831010B32012133210381C0821E2A14AC2546FB0B9852F37CA2789D7D76483D79217FB36F51DCE1E7B434010135010315463044022076DD2EAB72E69D440D6ED8290C8C37E39F54294C23FF0F8520F836E7BE07455C02201D9A8A75223C1ADA1D9D00966A12EBB919D0BBF2E66F144C83FADCAA23672566" # noqa: E501
579+
elif self._firmware == Firmware.NANOX:
580+
cert_apdu = "01010102010211040000000212010013020002140101160400000000200863616C6C646174613002000831010B32012133210381C0821E2A14AC2546FB0B9852F37CA2789D7D76483D79217FB36F51DCE1E7B434010135010215463044022077FF9625006CB8A4AD41A4B04FF2112E92A732BD263CCE9B97D8E7D2536D04300220445B8EE3616FB907AA5E68359275E94D0A099C3E32A4FC8B3669C34083671F2F" # noqa: E501
581+
elif self._firmware == Firmware.STAX:
582+
cert_apdu = "01010102010211040000000212010013020002140101160400000000200863616C6C646174613002000831010B32012133210381C0821E2A14AC2546FB0B9852F37CA2789D7D76483D79217FB36F51DCE1E7B434010135010415473045022100A88646AD72CA012D5FDAF8F6AE0B7EBEF079212768D57323CB5B57CADD9EB20D022005872F8EA06092C9783F01AF02C5510588FB60CBF4BA51FB382B39C1E060BB6B" # noqa: E501
583+
elif self._firmware == Firmware.FLEX:
584+
cert_apdu = "01010102010211040000000212010013020002140101160400000000200863616C6C646174613002000831010B32012133210381C0821E2A14AC2546FB0B9852F37CA2789D7D76483D79217FB36F51DCE1E7B43401013501051546304402205305BDDDAD0284A2EAC2A9BE4CEF6604AE9415C5F46883448F5F6325026234A3022001ED743BCF33CCEB070FDD73C3D3FCC2CEE5AB30A5C3EB7D2A8D21C6F58D493F" # noqa: E501
585+
# pylint: enable=line-too-long
586+
self._pki_client.send_certificate(PKIPubKeyUsage.PUBKEY_USAGE_CALLDATA, bytes.fromhex(cert_apdu))
587+
573588
chunks = self._cmd_builder.provide_enum_value(payload)
574589
for chunk in chunks[:-1]:
575590
self._exchange(chunk)
@@ -636,3 +651,23 @@ def provide_tx_simulation(self, simu_params: TxSimu) -> RAPDU:
636651
for chunk in chunks[:-1]:
637652
self._exchange(chunk)
638653
return self._exchange(chunks[-1])
654+
655+
def provide_proxy_info(self, payload: bytes) -> RAPDU:
656+
if self._pki_client is None:
657+
print(f"Ledger-PKI Not supported on '{self._firmware.name}'")
658+
else:
659+
# pylint: disable=line-too-long
660+
if self._firmware == Firmware.NANOSP:
661+
cert_apdu = "01010102010211040000000212010013020002140101160400000000200863616C6C646174613002000831010B32012133210381C0821E2A14AC2546FB0B9852F37CA2789D7D76483D79217FB36F51DCE1E7B434010135010315463044022076DD2EAB72E69D440D6ED8290C8C37E39F54294C23FF0F8520F836E7BE07455C02201D9A8A75223C1ADA1D9D00966A12EBB919D0BBF2E66F144C83FADCAA23672566" # noqa: E501
662+
elif self._firmware == Firmware.NANOX:
663+
cert_apdu = "01010102010211040000000212010013020002140101160400000000200863616C6C646174613002000831010B32012133210381C0821E2A14AC2546FB0B9852F37CA2789D7D76483D79217FB36F51DCE1E7B434010135010215463044022077FF9625006CB8A4AD41A4B04FF2112E92A732BD263CCE9B97D8E7D2536D04300220445B8EE3616FB907AA5E68359275E94D0A099C3E32A4FC8B3669C34083671F2F" # noqa: E501
664+
elif self._firmware == Firmware.STAX:
665+
cert_apdu = "01010102010211040000000212010013020002140101160400000000200863616C6C646174613002000831010B32012133210381C0821E2A14AC2546FB0B9852F37CA2789D7D76483D79217FB36F51DCE1E7B434010135010415473045022100A88646AD72CA012D5FDAF8F6AE0B7EBEF079212768D57323CB5B57CADD9EB20D022005872F8EA06092C9783F01AF02C5510588FB60CBF4BA51FB382B39C1E060BB6B" # noqa: E501
666+
elif self._firmware == Firmware.FLEX:
667+
cert_apdu = "01010102010211040000000212010013020002140101160400000000200863616C6C646174613002000831010B32012133210381C0821E2A14AC2546FB0B9852F37CA2789D7D76483D79217FB36F51DCE1E7B43401013501051546304402205305BDDDAD0284A2EAC2A9BE4CEF6604AE9415C5F46883448F5F6325026234A3022001ED743BCF33CCEB070FDD73C3D3FCC2CEE5AB30A5C3EB7D2A8D21C6F58D493F" # noqa: E501
668+
# pylint: enable=line-too-long
669+
self._pki_client.send_certificate(PKIPubKeyUsage.PUBKEY_USAGE_CALLDATA, bytes.fromhex(cert_apdu))
670+
chunks = self._cmd_builder.provide_proxy_info(payload)
671+
for chunk in chunks[:-1]:
672+
self._exchange(chunk)
673+
return self._exchange(chunks[-1])

client/src/ledger_app_clients/ethereum/command_builder.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class InsType(IntEnum):
2828
PROVIDE_TRUSTED_NAME = 0x22
2929
PROVIDE_ENUM_VALUE = 0x24
3030
PROVIDE_TRANSACTION_INFO = 0x26
31+
PROVIDE_PROXY_INFO = 0x2a
3132
PROVIDE_NETWORK_INFORMATION = 0x30
3233
PROVIDE_TX_SIMULATION = 0x32
3334

@@ -467,3 +468,6 @@ def opt_in_tx_simulation(self) -> bytes:
467468

468469
def provide_tx_simulation(self, tlv_payload: bytes) -> list[bytes]:
469470
return self.common_tlv_serialize(InsType.PROVIDE_TX_SIMULATION, tlv_payload, p1l=[0x00], p2l=[0x01, 0x00])
471+
472+
def provide_proxy_info(self, tlv_payload: bytes) -> list[bytes]:
473+
return self.common_tlv_serialize(InsType.PROVIDE_PROXY_INFO, tlv_payload)

client/src/ledger_app_clients/ethereum/eip712/InputData.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -406,9 +406,12 @@ def handle_optional_domain_values(domain):
406406
domain["verifyingContract"] = "0x0000000000000000000000000000000000000000"
407407

408408

409-
def init_signature_context(types, domain):
409+
def init_signature_context(types, domain, filters):
410410
handle_optional_domain_values(domain)
411-
caddr = domain["verifyingContract"]
411+
if "address" in filters:
412+
caddr = filters["address"]
413+
else:
414+
caddr = domain["verifyingContract"]
412415
if caddr.startswith("0x"):
413416
caddr = caddr[2:]
414417
sig_ctx["caddr"] = bytearray.fromhex(caddr)
@@ -452,7 +455,9 @@ def process_data(aclient: EthAppClient,
452455
global app_client
453456
global autonext_handler
454457
global is_golden_run
458+
global current_path
455459

460+
current_path = []
456461
# deepcopy because this function modifies the dict
457462
data_json = copy.deepcopy(data_json)
458463
app_client = aclient
@@ -469,7 +474,7 @@ def process_data(aclient: EthAppClient,
469474
is_golden_run = golden_run
470475

471476
if filters:
472-
init_signature_context(types, domain)
477+
init_signature_context(types, domain, filters)
473478

474479
# send types definition
475480
for key in types.keys():

client/src/ledger_app_clients/ethereum/enum_value.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,6 @@ def serialize(self) -> bytes:
5454
payload += format_tlv(Tag.NAME, self.name)
5555
sig = self.signature
5656
if sig is None:
57-
sig = sign_data(Key.CAL, payload)
57+
sig = sign_data(Key.CALLDATA, payload)
5858
payload += format_tlv(Tag.SIGNATURE, sig)
5959
return payload
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from enum import IntEnum
2+
from typing import Optional
3+
from .tlv import format_tlv
4+
from .keychain import sign_data, Key
5+
6+
7+
class Tag(IntEnum):
8+
STRUCT_TYPE = 0x01
9+
STRUCT_VERSION = 0x02
10+
CHALLENGE = 0x12
11+
ADDRESS = 0x22
12+
CHAIN_ID = 0x23
13+
SELECTOR = 0x28
14+
IMPL_ADDRESS = 0x29
15+
SIGNATURE = 0x15
16+
17+
18+
class ProxyInfo:
19+
challenge: int
20+
address: bytes
21+
chain_id: int
22+
selector: Optional[bytes]
23+
impl_address: bytes
24+
signature: Optional[bytes]
25+
26+
def __init__(self,
27+
challenge: int,
28+
address: bytes,
29+
chain_id: int,
30+
impl_address: bytes,
31+
selector: Optional[bytes] = None,
32+
signature: Optional[bytes] = None):
33+
self.challenge = challenge
34+
self.address = address
35+
self.chain_id = chain_id
36+
self.selector = selector
37+
self.impl_address = impl_address
38+
self.signature = signature
39+
40+
def serialize(self) -> bytes:
41+
payload = bytearray()
42+
payload += format_tlv(Tag.STRUCT_TYPE, 0x26)
43+
payload += format_tlv(Tag.STRUCT_VERSION, 1)
44+
payload += format_tlv(Tag.CHALLENGE, self.challenge)
45+
payload += format_tlv(Tag.ADDRESS, self.address)
46+
payload += format_tlv(Tag.CHAIN_ID, self.chain_id)
47+
if self.selector is not None:
48+
payload += format_tlv(Tag.SELECTOR, self.selector)
49+
payload += format_tlv(Tag.IMPL_ADDRESS, self.impl_address)
50+
sig = self.signature
51+
if sig is None:
52+
sig = sign_data(Key.CALLDATA, payload)
53+
payload += format_tlv(Tag.SIGNATURE, sig)
54+
return payload

doc/ethapp.adoc

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,6 +1122,9 @@ None
11221122
11231123
### TRANSACTION INFO
11241124
1125+
#### Description
1126+
#### Coding
1127+
11251128
_Command_
11261129
11271130
[width="80%"]
@@ -1159,6 +1162,9 @@ None
11591162
11601163
### TRANSACTION FIELD DESCRIPTION
11611164
1165+
#### Description
1166+
#### Coding
1167+
11621168
_Command_
11631169
11641170
[width="80%"]
@@ -1194,6 +1200,50 @@ _Output data_
11941200
None
11951201
11961202
1203+
### PROVIDE PROXY INFO
1204+
1205+
#### Description
1206+
1207+
This command provides the app with the knowledge that a contract address is a proxy to another contract on a specific chain.
1208+
This can also (optionally) be restricted to a specific function within the contract.
1209+
1210+
#### Coding
1211+
1212+
_Command_
1213+
1214+
[width="80%"]
1215+
|==============================================================
1216+
| *CLA* | *INS* | *P1* | *P2* | *LC*
1217+
| E0 | 2A | 01 : first chunk
1218+
1219+
00 : following chunk
1220+
| 00 | 00
1221+
|==============================================================
1222+
1223+
_Input data_
1224+
1225+
##### If P1 == first chunk
1226+
1227+
[width="80%"]
1228+
|==========================================
1229+
| *Description* | *Length (byte)*
1230+
| Payload length | 2
1231+
| TLV payload | variable
1232+
|==========================================
1233+
1234+
##### If P1 == following chunk
1235+
1236+
[width="80%"]
1237+
|==========================================
1238+
| *Description* | *Length (byte)*
1239+
| TLV payload | variable
1240+
|==========================================
1241+
1242+
_Output data_
1243+
1244+
None
1245+
1246+
11971247
### PROVIDE NETWORK CONFIGURATION
11981248
11991249
#### Description

src/apdu_constants.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#define INS_PROVIDE_ENUM_VALUE 0x24
3131
#define INS_GTP_TRANSACTION_INFO 0x26
3232
#define INS_GTP_FIELD 0x28
33+
#define INS_PROVIDE_PROXY_INFO 0x2A
3334
#define INS_PROVIDE_NETWORK_CONFIGURATION 0x30
3435
#define INS_PROVIDE_TX_SIMULATION 0x32
3536

src/main.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include "cmd_tx_info.h"
4545
#include "cmd_field.h"
4646
#include "cmd_get_tx_simulation.h"
47+
#include "cmd_proxy_info.h"
4748

4849
tmpCtx_t tmpCtx;
4950
txContext_t txContext;
@@ -256,6 +257,12 @@ static uint16_t handleApdu(command_t *cmd, uint32_t *flags, uint32_t *tx) {
256257
break;
257258
#endif // HAVE_GENERIC_TX_PARSER
258259

260+
#if defined(HAVE_EIP712_FULL_SUPPORT) || defined(HAVE_GENERIC_TX_PARSER)
261+
case INS_PROVIDE_PROXY_INFO:
262+
sw = handle_proxy_info(cmd->p1, cmd->p2, cmd->lc, cmd->data);
263+
break;
264+
#endif
265+
259266
#ifdef HAVE_DYNAMIC_NETWORKS
260267
case INS_PROVIDE_NETWORK_CONFIGURATION:
261268
sw = handle_network_info(cmd->p1, cmd->p2, cmd->data, cmd->lc, tx);

src/signature.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#ifndef SIGNATURE_H_
2+
#define SIGNATURE_H_
3+
4+
#define ECDSA_SIGNATURE_MAX_LENGTH 73
5+
6+
#endif // SIGNATURE_H_

0 commit comments

Comments
 (0)