Skip to content

Commit d4fb82c

Browse files
Merge pull request #942 from LedgerHQ/fix/apa/mab
Add new owner field for MAB sourced trusted names
2 parents 8ced1fc + 1c2ca48 commit d4fb82c

13 files changed

Lines changed: 372 additions & 182 deletions

File tree

client/src/ledger_app_clients/ethereum/client.py

Lines changed: 5 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import struct
21
from enum import IntEnum
32
from typing import Optional
43
import rlp
@@ -11,7 +10,6 @@
1110
from .command_builder import CommandBuilder
1211
from .eip712 import EIP712FieldType
1312
from .keychain import sign_data, Key
14-
from .tlv import format_tlv, FieldTag
1513
from .response_parser import pk_addr
1614
from .tx_simu import TxSimu
1715
from .tx_auth_7702 import TxAuth7702
@@ -20,26 +18,7 @@
2018
from .dynamic_networks import DynamicNetwork
2119
from .safe import SafeAccount, AccountType
2220
from .gating import Gating
23-
24-
25-
class TrustedNameType(IntEnum):
26-
ACCOUNT = 0x01
27-
CONTRACT = 0x02
28-
NFT = 0x03
29-
TOKEN = 0x04
30-
WALLET = 0x05
31-
CONTEXT_ADDRESS = 0x06
32-
33-
34-
class TrustedNameSource(IntEnum):
35-
LAB = 0x00
36-
CAL = 0x01
37-
ENS = 0x02
38-
UD = 0x03
39-
FN = 0x04
40-
DNS = 0x05
41-
DYN_RESOLVER = 0x06
42-
MULTISIG_ADDRESS_BOOK = 0x07
21+
from .trusted_name import TrustedName, TrustedNameSource
4322

4423

4524
class EIP712CalldataParamPresence(IntEnum):
@@ -281,58 +260,15 @@ def perform_privacy_operation(self,
281260
bip32_path,
282261
pubkey))
283262

284-
def _provide_trusted_name_common(self, payload: bytes, name_source: TrustedNameSource) -> RAPDU:
285-
payload += format_tlv(FieldTag.STRUCT_TYPE, 3) # TrustedName
286-
if name_source == TrustedNameSource.CAL:
287-
key_id = 9
288-
key = Key.CAL
289-
else:
290-
key_id = 7
291-
key = Key.TRUSTED_NAME
292-
293-
self.pki_client.send_certificate(PKIPubKeyUsage.PUBKEY_USAGE_TRUSTED_NAME, name_source == TrustedNameSource.CAL)
263+
def provide_trusted_name(self, trusted_name: TrustedName) -> RAPDU:
264+
self.pki_client.send_certificate(PKIPubKeyUsage.PUBKEY_USAGE_TRUSTED_NAME,
265+
trusted_name.tn_source == TrustedNameSource.CAL)
294266

295-
payload += format_tlv(FieldTag.SIGNER_KEY_ID, key_id) # test key
296-
payload += format_tlv(FieldTag.SIGNER_ALGO, 1) # secp256k1
297-
payload += format_tlv(FieldTag.DER_SIGNATURE,
298-
sign_data(key, payload))
299-
chunks = self._cmd_builder.provide_trusted_name(payload)
267+
chunks = self._cmd_builder.provide_trusted_name(trusted_name.serialize())
300268
for chunk in chunks[:-1]:
301269
self._exchange(chunk)
302270
return self._exchange(chunks[-1])
303271

304-
def provide_trusted_name_v1(self, addr: bytes, name: str, challenge: int) -> RAPDU:
305-
payload = format_tlv(FieldTag.STRUCT_VERSION, 1)
306-
payload += format_tlv(FieldTag.CHALLENGE, challenge)
307-
payload += format_tlv(FieldTag.COIN_TYPE, 0x3c) # ETH in slip-44
308-
payload += format_tlv(FieldTag.TRUSTED_NAME, name)
309-
payload += format_tlv(FieldTag.ADDRESS, addr)
310-
return self._provide_trusted_name_common(payload, TrustedNameSource.ENS)
311-
312-
def provide_trusted_name_v2(self,
313-
addr: bytes,
314-
name: str,
315-
name_type: TrustedNameType,
316-
name_source: TrustedNameSource,
317-
chain_id: int,
318-
nft_id: Optional[int] = None,
319-
challenge: Optional[int] = None,
320-
not_valid_after: Optional[tuple[int, int, int]] = None) -> RAPDU:
321-
payload = format_tlv(FieldTag.STRUCT_VERSION, 2)
322-
payload += format_tlv(FieldTag.TRUSTED_NAME, name)
323-
payload += format_tlv(FieldTag.ADDRESS, addr)
324-
payload += format_tlv(FieldTag.TRUSTED_NAME_TYPE, name_type)
325-
payload += format_tlv(FieldTag.TRUSTED_NAME_SOURCE, name_source)
326-
payload += format_tlv(FieldTag.CHAIN_ID, chain_id)
327-
if nft_id is not None:
328-
payload += format_tlv(FieldTag.TRUSTED_NAME_NFT_ID, nft_id)
329-
if challenge is not None:
330-
payload += format_tlv(FieldTag.CHALLENGE, challenge)
331-
if not_valid_after is not None:
332-
assert len(not_valid_after) == 3
333-
payload += format_tlv(FieldTag.NOT_VALID_AFTER, struct.pack("BBB", *not_valid_after))
334-
return self._provide_trusted_name_common(payload, name_source)
335-
336272
def set_plugin(self,
337273
plugin_name: str,
338274
contract_addr: bytes,

client/src/ledger_app_clients/ethereum/gcs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from .tlv import TlvSerializable
66
from .keychain import sign_data, Key
7-
from .client import TrustedNameType, TrustedNameSource
7+
from .trusted_name import TrustedNameType, TrustedNameSource
88

99

1010
class TxInfoTag(IntEnum):

client/src/ledger_app_clients/ethereum/tlv.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,17 @@
55
class FieldTag(IntEnum):
66
STRUCT_TYPE = 0x01
77
STRUCT_VERSION = 0x02
8-
NOT_VALID_AFTER = 0x10
98
CHALLENGE = 0x12
10-
SIGNER_KEY_ID = 0x13
11-
SIGNER_ALGO = 0x14
129
DER_SIGNATURE = 0x15
13-
TRUSTED_NAME = 0x20
14-
COIN_TYPE = 0x21
1510
ADDRESS = 0x22
1611
CHAIN_ID = 0x23
1712
TICKER = 0x24
1813
TX_HASH = 0x27
1914
DOMAIN_HASH = 0x28
2015
SELECTOR = 0x40
21-
IMPL_ADDRESS = 0x41
22-
DELEGATION_TYPE = 0x42
2316
BLOCKCHAIN_FAMILY = 0x51
2417
NETWORK_NAME = 0x52
2518
NETWORK_ICON_HASH = 0x53
26-
TRUSTED_NAME_TYPE = 0x70
27-
TRUSTED_NAME_SOURCE = 0x71
28-
TRUSTED_NAME_NFT_ID = 0x72
2919
TX_CHECKS_NORMALIZED_RISK = 0x80
3020
TX_CHECKS_NORMALIZED_CATEGORY = 0x81
3121
MESSAGE = 0x82
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import struct
2+
from enum import IntEnum
3+
4+
from .keychain import Key, sign_data
5+
from .tlv import TlvSerializable
6+
7+
8+
class TrustedNameType(IntEnum):
9+
ACCOUNT = 0x01
10+
CONTRACT = 0x02
11+
NFT = 0x03
12+
TOKEN = 0x04
13+
WALLET = 0x05
14+
CONTEXT_ADDRESS = 0x06
15+
16+
17+
class TrustedNameSource(IntEnum):
18+
LAB = 0x00
19+
CAL = 0x01
20+
ENS = 0x02
21+
UD = 0x03
22+
FN = 0x04
23+
DNS = 0x05
24+
DYN_RESOLVER = 0x06
25+
MULTISIG_ADDRESS_BOOK = 0x07
26+
27+
28+
class Tag(IntEnum):
29+
STRUCT_TYPE = 0x01
30+
STRUCT_VERSION = 0x02
31+
NOT_VALID_AFTER = 0x10
32+
CHALLENGE = 0x12
33+
SIG_KEY_ID = 0x13
34+
SIG_ALGO = 0x14
35+
SIGNATURE = 0x15
36+
NAME = 0x20
37+
COIN_TYPE = 0x21
38+
ADDRESS = 0x22
39+
CHAIN_ID = 0x23
40+
TYPE = 0x70
41+
SOURCE = 0x71
42+
NFT_ID = 0x72
43+
OWNER = 0x74
44+
45+
46+
class TrustedName(TlvSerializable):
47+
struct_version: int
48+
coin_type: int | None
49+
not_valid_after: tuple[int, int, int] | None
50+
tn_type: TrustedNameType | None
51+
tn_source: TrustedNameSource | None
52+
name: str
53+
chain_id: int | None
54+
address: bytes
55+
challenge: bytes | None
56+
nft_id: int | None
57+
owner: bytes | None
58+
59+
def __init__(
60+
self,
61+
version: int,
62+
address: bytes,
63+
name: str,
64+
coin_type: int | None = None,
65+
not_valid_after: tuple[int, int, int] | None = None,
66+
challenge: bytes | None = None,
67+
tn_type: TrustedNameType | None = None,
68+
tn_source: TrustedNameSource | None = None,
69+
chain_id: int | None = None,
70+
nft_id: int | None = None,
71+
owner: bytes | None = None,
72+
signature: bytes | None = None,
73+
) -> None:
74+
self.version = version
75+
self.coin_type = coin_type
76+
self.not_valid_after = not_valid_after
77+
self.tn_type = tn_type
78+
self.tn_source = tn_source
79+
self.name = name
80+
self.chain_id = chain_id
81+
self.address = address
82+
self.challenge = challenge
83+
self.owner = owner
84+
self.nft_id = nft_id
85+
self.signature = signature
86+
87+
def serialize(self) -> bytes:
88+
payload = bytearray()
89+
payload += self.serialize_field(Tag.STRUCT_TYPE, 0x03)
90+
payload += self.serialize_field(Tag.STRUCT_VERSION, self.version)
91+
if self.coin_type is not None:
92+
payload += self.serialize_field(Tag.COIN_TYPE, self.coin_type)
93+
if self.not_valid_after is not None:
94+
payload += self.serialize_field(Tag.NOT_VALID_AFTER, struct.pack("BBB", *self.not_valid_after))
95+
if self.tn_type is not None:
96+
payload += self.serialize_field(Tag.TYPE, self.tn_type)
97+
if self.tn_source is not None:
98+
payload += self.serialize_field(Tag.SOURCE, self.tn_source)
99+
payload += self.serialize_field(Tag.NAME, self.name)
100+
if self.chain_id is not None:
101+
payload += self.serialize_field(Tag.CHAIN_ID, self.chain_id)
102+
payload += self.serialize_field(Tag.ADDRESS, self.address)
103+
if self.challenge is not None:
104+
payload += self.serialize_field(Tag.CHALLENGE, self.challenge)
105+
if self.nft_id is not None:
106+
payload += self.serialize_field(Tag.NFT_ID, self.nft_id)
107+
if self.owner is not None:
108+
payload += self.serialize_field(Tag.OWNER, self.owner)
109+
sig = self.signature
110+
if self.tn_source == TrustedNameSource.CAL:
111+
key_id = 9
112+
key = Key.CAL
113+
else:
114+
key_id = 7
115+
key = Key.TRUSTED_NAME
116+
payload += self.serialize_field(Tag.SIG_KEY_ID, key_id)
117+
payload += self.serialize_field(Tag.SIG_ALGO, 1)
118+
if sig is None:
119+
sig = sign_data(key, payload)
120+
payload += self.serialize_field(Tag.SIGNATURE, sig)
121+
return payload

client/src/ledger_app_clients/ethereum/utils.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
import rlp
88

99

10+
class CoinType(IntEnum):
11+
ETH = 0x3c
12+
13+
1014
class TxType(IntEnum):
1115
TRANSACTION = 0x00
1216
TYPED_DATA = 0x01

doc/ethapp.adoc

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,19 +1236,19 @@ _Input data_
12361236
##### If P1 == first chunk
12371237
12381238
[width="80%"]
1239-
|==========================================
1240-
| *Description* | *Length (byte)*
1241-
| Payload length | 2
1242-
| TLV payload | variable
1243-
|==========================================
1239+
|=========================================================================
1240+
| *Description* | *Length (byte)*
1241+
| Payload length | 2
1242+
| link:tlv_structs.md#trusted_name[TRUSTED_NAME struct] | variable
1243+
|=========================================================================
12441244
12451245
##### If P1 == following chunk
12461246
12471247
[width="80%"]
1248-
|==========================================
1249-
| *Description* | *Length (byte)*
1250-
| TLV payload | variable
1251-
|==========================================
1248+
|=========================================================================
1249+
| *Description* | *Length (byte)*
1250+
| link:tlv_structs.md#trusted_name[TRUSTED_NAME struct] | variable
1251+
|=========================================================================
12521252
12531253
_Output data_
12541254

doc/tlv_structs.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,25 @@
11
# TLV structures
22

3+
## TRUSTED_NAME
4+
5+
| Name | Tag | Payload type | Description | Optional |
6+
|-----------------|------|-----------------|---------------------------------|----------|
7+
| STRUCT_TYPE | 0x01 | uint8 | structure type (0x03) | |
8+
| STRUCT_VERSION | 0x02 | uint8 | structure version (currently 2) | |
9+
| NOT_VALID_AFTER | 0x10 | uint8[3] | app version (major,minor,patch) | x |
10+
| CHALLENGE | 0x12 | uint32 | | x |
11+
| SIG_KEY_ID | 0x13 | uint8 | | x |
12+
| SIG_ALGO | 0x14 | uint8 | | x |
13+
| SIGNATURE | 0x15 | uint8[] | | x |
14+
| NAME | 0x20 | char[] | what to substitute with | |
15+
| COIN_TYPE | 0x21 | uint8 | as defined in SLIP-44 | x |
16+
| ADDRESS | 0x22 | uint8[20] | address to substitute | |
17+
| CHAIN_ID | 0x23 | uint64 | | x |
18+
| TYPE | 0x70 | uint8 | | x |
19+
| SOURCE | 0x71 | uint8 | | x |
20+
| NFT_ID | 0x72 | uint256 | | x |
21+
| OWNER | 0x74 | uint8[20] | | x |
22+
323
## TRANSACTION_INFO
424

525
| Name | Tag | Payload type | Description | Optional | Source / value |

src/features/getPublicKey/getPublicKey.c

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,22 @@ uint16_t get_public_key_string(bip32_path_t *bip32,
2424

2525
uint16_t get_public_key(uint8_t *out, uint8_t outLength) {
2626
uint8_t raw_pubkey[65];
27-
cx_err_t error = CX_INTERNAL_ERROR;
27+
cx_err_t error;
2828

2929
if (outLength < ADDRESS_LENGTH) {
3030
return SWO_WRONG_DATA_LENGTH;
3131
}
32-
CX_CHECK(bip32_derive_get_pubkey_256(CX_CURVE_256K1,
33-
tmpCtx.transactionContext.bip32.path,
34-
tmpCtx.transactionContext.bip32.length,
35-
raw_pubkey,
36-
NULL,
37-
CX_SHA512));
38-
32+
if ((error = bip32_derive_get_pubkey_256(CX_CURVE_256K1,
33+
tmpCtx.transactionContext.bip32.path,
34+
tmpCtx.transactionContext.bip32.length,
35+
raw_pubkey,
36+
NULL,
37+
CX_SHA512)) != CX_OK) {
38+
PRINTF("Error: could not derive pubkey!\n");
39+
return error;
40+
}
3941
getEthAddressFromRawKey(raw_pubkey, out);
40-
error = SWO_SUCCESS;
41-
end:
42-
return error;
42+
return SWO_SUCCESS;
4343
}
4444

4545
uint32_t set_result_get_publicKey() {

0 commit comments

Comments
 (0)