Skip to content

Commit 471c0cd

Browse files
committed
Apply review comments, TLV format for APDU data
1 parent 33287d6 commit 471c0cd

File tree

14 files changed

+355
-141
lines changed

14 files changed

+355
-141
lines changed

client/src/ledger_app_clients/ethereum/client.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from .tlv import format_tlv, FieldTag
1818
from .response_parser import pk_addr
1919
from .tx_simu import TxSimu
20+
from .tx_auth_7702 import TxAuth7702
2021

2122

2223
class StatusWord(IntEnum):
@@ -679,12 +680,8 @@ def provide_proxy_info(self, payload: bytes) -> RAPDU:
679680
self._exchange(chunk)
680681
return self._exchange(chunks[-1])
681682

682-
def sign_eip7702_authorization(self,
683-
bip32_path: str,
684-
delegate: bytes,
685-
nonce: int,
686-
chain_id: Optional[int] = None) -> RAPDU:
687-
return self._exchange_async(self._cmd_builder.sign_eip7702_authorization(bip32_path,
688-
delegate,
689-
nonce,
690-
chain_id))
683+
def sign_eip7702_authorization(self, auth_params: TxAuth7702) -> RAPDU:
684+
chunks = self._cmd_builder.sign_eip7702_authorization(auth_params.serialize())
685+
for chunk in chunks[:-1]:
686+
self._exchange(chunk)
687+
return self._exchange_async(chunks[-1])

client/src/ledger_app_clients/ethereum/command_builder.py

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -463,23 +463,8 @@ def provide_network_information(self,
463463
p1 = P1Type.FOLLOWING_CHUNK
464464
return chunks
465465

466-
def sign_eip7702_authorization(self,
467-
bip32_path: str,
468-
delegate: bytes,
469-
nonce: int,
470-
chain_id: Optional[int]) -> bytes:
471-
data = pack_derivation_path(bip32_path)
472-
data += delegate
473-
if chain_id is None:
474-
chain_id = 0
475-
tmp = self._intToBytes(chain_id)
476-
data += struct.pack(">B", len(tmp)) + tmp
477-
tmp = self._intToBytes(nonce)
478-
data += struct.pack(">B", len(tmp)) + tmp
479-
return self._serialize(InsType.SIGN_EIP7702_AUTHORIZATION,
480-
0x00,
481-
0x00,
482-
data)
466+
def sign_eip7702_authorization(self, tlv_payload: bytes) -> list[bytes]:
467+
return self.common_tlv_serialize(InsType.SIGN_EIP7702_AUTHORIZATION, tlv_payload)
483468

484469
def provide_enum_value(self, tlv_payload: bytes) -> list[bytes]:
485470
return self.common_tlv_serialize(InsType.PROVIDE_ENUM_VALUE, tlv_payload)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from typing import Optional
2+
from enum import IntEnum
3+
4+
from bip_utils import Bip32Utils
5+
6+
from .tlv import TlvSerializable
7+
8+
9+
class FieldTag(IntEnum):
10+
STRUCT_VERSION = 0x00
11+
DERIVATION_IDX = 0x01
12+
DELEGATE_ADDR = 0x02
13+
CHAIN_ID = 0x03
14+
NONCE = 0x04
15+
16+
17+
class TxAuth7702(TlvSerializable):
18+
bip32_path: str
19+
delegate: bytes
20+
nonce: int
21+
chain_id: Optional[int] = 0
22+
23+
def __init__(self,
24+
bip32_path: str,
25+
delegate: bytes,
26+
nonce: int,
27+
chain_id: Optional[int] = 0) -> None:
28+
self.bip32_path = bip32_path
29+
self.delegate = delegate
30+
self.nonce = nonce
31+
self.chain_id = chain_id
32+
33+
def serialize(self) -> bytes:
34+
payload: bytes = self.serialize_field(FieldTag.STRUCT_VERSION, 1)
35+
split = self.bip32_path.split("/")
36+
if split[0] != "m":
37+
raise ValueError("Error master expected in bip32 path")
38+
for value in split[1:]:
39+
if value == "":
40+
raise ValueError(f'Error missing value in split bip 32 path list "{split}"')
41+
if value.endswith('\''):
42+
payload += self.serialize_field(FieldTag.DERIVATION_IDX,
43+
Bip32Utils.HardenIndex(int(value[:-1])).to_bytes(4, byteorder='big'))
44+
else:
45+
payload += self.serialize_field(FieldTag.DERIVATION_IDX,
46+
int(value).to_bytes(4, byteorder='big'))
47+
payload += self.serialize_field(FieldTag.DELEGATE_ADDR, self.delegate)
48+
payload += self.serialize_field(FieldTag.NONCE, self.nonce.to_bytes(8, 'big'))
49+
payload += self.serialize_field(FieldTag.CHAIN_ID, self.chain_id.to_bytes(8, 'big'))
50+
return payload

doc/ethapp.adoc

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1470,24 +1470,30 @@ _Command_
14701470
[width="80%"]
14711471
|==============================================================
14721472
| *CLA* | *INS* | *P1* | *P2* | *LC*
1473-
| E0 | 34 | 00 | 00 | 00
1473+
| E0 | 34 | 01 : first chunk
1474+
1475+
00 : following chunk
1476+
| 00 | 00
14741477
|==============================================================
14751478
14761479
_Input data_
14771480
1481+
##### If P1 == first chunk
1482+
14781483
[width="80%"]
1479-
|==========================================
1480-
| *Description* | *Length (byte)*
1481-
| Number of BIP 32 derivations to perform (max 10) | 1
1482-
| First derivation index (big endian) | 4
1483-
| ... | 4
1484-
| Last derivation index (big endian) | 4
1485-
| Delegate address | 20
1486-
| Length of Chain ID (max 32) | 1
1487-
| Chain ID (smallest big endian encoding) | variable
1488-
| Length of Nonce (max 8) | 1
1489-
| Nonce (smallest big endian encoding) | variable
1490-
|==========================================
1484+
|====================================================================
1485+
| *Description* | *Length (byte)*
1486+
| struct size (BE) | 2
1487+
| link:tlv_structs.md#auth_7702[AUTH_7702 struct] | variable
1488+
|====================================================================
1489+
1490+
##### If P1 == following chunk
1491+
1492+
[width="80%"]
1493+
|====================================================================
1494+
| *Description* | *Length (byte)*
1495+
| link:tlv_structs.md#auth_7702[AUTH_7702 struct] | variable
1496+
|====================================================================
14911497
14921498
_Output data_
14931499

doc/tlv_structs.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,3 +312,13 @@ In version 1 of the protocol:
312312
| SELECTOR | 0x28 | uint[4] | function selector | x |
313313
| IMPL_ADDRESS | 0x29 | uint8[20] | implementation contract address | |
314314
| SIGNATURE | 0x15 | uint8[] | signature of the structure | |
315+
316+
## AUTH_7702
317+
318+
| Name | Tag | Payload type | Description | Optional |
319+
|----------------|------|-----------------|---------------------------------|----------|
320+
| STRUCT_VERSION | 0x00 | uint8 | structure version (currently 1) | |
321+
| DERIVATION_IDX | 0x01 | uint32 | BIP32 derivation path item | |
322+
| DELEGATE_ADDR | 0x02 | uint8[20] | delegate address | |
323+
| CHAIN_ID | 0x03 | uint64 | Chain ID (00 for no restriction)| |
324+
| NONCE | 0x04 | uint64 | nonce | |

src/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ static uint16_t handleApdu(command_t *cmd, uint32_t *flags, uint32_t *tx) {
278278

279279
#ifdef HAVE_EIP7702
280280
case INS_SIGN_EIP7702_AUTHORIZATION:
281-
sw = handleSignEIP7702Authorization(cmd->data, cmd->lc, flags);
281+
sw = handleSignEIP7702Authorization(cmd->p1, cmd->data, cmd->lc, flags);
282282
break;
283283
#endif // HAVE_EIP7702
284284

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
#ifdef HAVE_EIP7702
2+
3+
#include "auth_7702.h"
4+
#include "utils.h"
5+
#include "read.h"
6+
7+
enum {
8+
TAG_STRUCT_VERSION = 0x00,
9+
TAG_DERIVATION_IDX = 0x01,
10+
TAG_DELEGATE_ADDR = 0x02,
11+
TAG_CHAIN_ID = 0x03,
12+
TAG_NONCE = 0x04,
13+
};
14+
15+
enum {
16+
BIT_VERSION,
17+
BIT_DERIVATION_IDX,
18+
BIT_DELEGATE_ADDR,
19+
BIT_CHAIN_ID,
20+
BIT_NONCE,
21+
};
22+
23+
#define STRUCT_VERSION 0x01
24+
#define MASK_ALL \
25+
(SET_BIT(BIT_VERSION) | SET_BIT(BIT_DERIVATION_IDX) | SET_BIT(BIT_DELEGATE_ADDR) | \
26+
SET_BIT(BIT_CHAIN_ID) | SET_BIT(BIT_NONCE))
27+
28+
static bool handle_version(const s_tlv_data *data, s_auth_7702_ctx *context) {
29+
if (data->length != sizeof(uint8_t)) {
30+
return false;
31+
}
32+
context->mask_parsed |= SET_BIT(BIT_VERSION);
33+
return (data->value[0] == STRUCT_VERSION);
34+
}
35+
36+
static bool handle_derivation_idx(const s_tlv_data *data, s_auth_7702_ctx *context) {
37+
uint32_t idx;
38+
if (data->length != sizeof(uint32_t)) {
39+
return false;
40+
}
41+
if (context->auth_7702.bip32.length == MAX_BIP32_PATH) {
42+
return false;
43+
}
44+
idx = read_u32_be(data->value, 0);
45+
context->auth_7702.bip32.path[context->auth_7702.bip32.length] = idx;
46+
context->auth_7702.bip32.length++;
47+
context->mask_parsed |= SET_BIT(BIT_DERIVATION_IDX);
48+
return true;
49+
}
50+
51+
static bool handle_delegate_addr(const s_tlv_data *data, s_auth_7702_ctx *context) {
52+
if (data->length != sizeof(context->auth_7702.delegate)) {
53+
return false;
54+
}
55+
memmove(context->auth_7702.delegate, data->value, sizeof(context->auth_7702.delegate));
56+
context->mask_parsed |= SET_BIT(BIT_DELEGATE_ADDR);
57+
return true;
58+
}
59+
60+
static bool handle_chain_id(const s_tlv_data *data, s_auth_7702_ctx *context) {
61+
if (data->length != sizeof(uint64_t)) {
62+
return false;
63+
}
64+
context->auth_7702.chainId = read_u64_be(data->value, 0);
65+
context->mask_parsed |= SET_BIT(BIT_CHAIN_ID);
66+
return true;
67+
}
68+
69+
static bool handle_nonce(const s_tlv_data *data, s_auth_7702_ctx *context) {
70+
if (data->length != sizeof(uint64_t)) {
71+
return false;
72+
}
73+
context->auth_7702.nonce = read_u64_be(data->value, 0);
74+
context->mask_parsed |= SET_BIT(BIT_NONCE);
75+
return true;
76+
}
77+
78+
bool handle_auth_7702_struct(const s_tlv_data *data, s_auth_7702_ctx *context) {
79+
bool ret;
80+
81+
switch (data->tag) {
82+
case TAG_STRUCT_VERSION:
83+
ret = handle_version(data, context);
84+
break;
85+
case TAG_DERIVATION_IDX:
86+
ret = handle_derivation_idx(data, context);
87+
break;
88+
case TAG_DELEGATE_ADDR:
89+
ret = handle_delegate_addr(data, context);
90+
break;
91+
case TAG_CHAIN_ID:
92+
ret = handle_chain_id(data, context);
93+
break;
94+
case TAG_NONCE:
95+
ret = handle_nonce(data, context);
96+
break;
97+
default:
98+
PRINTF(TLV_TAG_ERROR_MSG, data->tag);
99+
ret = false;
100+
}
101+
return ret;
102+
}
103+
104+
bool verify_auth_7702_struct(s_auth_7702_ctx *context) {
105+
return ((context->mask_parsed & MASK_ALL) == MASK_ALL);
106+
}
107+
108+
#endif // HAVE_EIP7702
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#pragma once
2+
3+
#include <stdint.h>
4+
#include "bip32_utils.h"
5+
#include "common_utils.h"
6+
#include "tlv.h"
7+
8+
typedef struct {
9+
bip32_path_t bip32;
10+
uint64_t chainId;
11+
uint64_t nonce;
12+
uint8_t delegate[20];
13+
} s_auth_7702;
14+
15+
typedef struct {
16+
s_auth_7702 auth_7702;
17+
uint8_t mask_parsed;
18+
} s_auth_7702_ctx;
19+
20+
bool handle_auth_7702_struct(const s_tlv_data *data, s_auth_7702_ctx *context);
21+
bool verify_auth_7702_struct(s_auth_7702_ctx *context);

0 commit comments

Comments
 (0)