Skip to content

Commit e0a55ef

Browse files
Merge pull request #677 from LedgerHQ/develop
App release 1.13.0
2 parents 65031ce + 81cb59a commit e0a55ef

Some content is hidden

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

45 files changed

+1505
-197
lines changed

CHANGELOG.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,31 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/)
66
and this project adheres to [Semantic Versioning](http://semver.org/).
77

8+
## [1.13.0](https://github.com/ledgerhq/app-ethereum/compare/1.12.2...1.13.0) - 2024-11-26
9+
10+
### Added
11+
12+
- (clone) IoTeX
13+
- (network) Defi Oracle Meta
14+
- (network) IoTeX
15+
- (network) IoTeX Testnet
16+
- (network) Neo X Mainnet
17+
- (network) Neo X Testnet
18+
- (network) Bitlayer
19+
- (network) Bitlayer Testnet
20+
- Dynamic network handling, can get new networks at runtime from the CAL instead of from the hardcoded list
21+
- Support for Ethermint's non-standard EIP-712 verifyingContract
22+
23+
### Changed
24+
25+
- Improved error handling in swap mode
26+
- Provide NFT info APDU does not require a loaded NFT (721/1155) internal plugin anymore
27+
28+
### Fixed
29+
30+
- Potential overflow on the UI buffer used for amounts
31+
- RLP parsing issue with legacy transactions
32+
833
## [1.12.2](https://github.com/ledgerhq/app-ethereum/compare/1.12.1...1.12.2) - 2024-10-24
934

1035
### Fixed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ endif
3636
include ./makefile_conf/chain/$(CHAIN).mk
3737

3838
APPVERSION_M = 1
39-
APPVERSION_N = 12
40-
APPVERSION_P = 2
39+
APPVERSION_N = 13
40+
APPVERSION_P = 0
4141
APPVERSION = $(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)
4242

4343
# Application source files

client/src/ledger_app_clients/ethereum/client.py

Lines changed: 80 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from .keychain import sign_data, Key
1313
from .tlv import format_tlv
1414

15+
from hashlib import sha256
1516
from web3 import Web3
1617

1718

@@ -43,21 +44,25 @@ class TrustedNameSource(IntEnum):
4344
DNS = 0x05
4445

4546

46-
class TrustedNameTag(IntEnum):
47+
class FieldTag(IntEnum):
4748
STRUCT_TYPE = 0x01
4849
STRUCT_VERSION = 0x02
4950
NOT_VALID_AFTER = 0x10
5051
CHALLENGE = 0x12
5152
SIGNER_KEY_ID = 0x13
5253
SIGNER_ALGO = 0x14
53-
SIGNATURE = 0x15
54-
NAME = 0x20
54+
DER_SIGNATURE = 0x15
55+
TRUSTED_NAME = 0x20
5556
COIN_TYPE = 0x21
5657
ADDRESS = 0x22
5758
CHAIN_ID = 0x23
58-
NAME_TYPE = 0x70
59-
NAME_SOURCE = 0x71
60-
NFT_ID = 0x72
59+
TICKER = 0x24
60+
BLOCKCHAIN_FAMILY = 0x51
61+
NETWORK_NAME = 0x52
62+
NETWORK_ICON_HASH = 0x53
63+
TRUSTED_NAME_TYPE = 0x70
64+
TRUSTED_NAME_SOURCE = 0x71
65+
TRUSTED_NAME_NFT_ID = 0x72
6166

6267

6368
class PKIPubKeyUsage(IntEnum):
@@ -266,22 +271,22 @@ def _provide_trusted_name_common(self, payload: bytes) -> RAPDU:
266271
# pylint: enable=line-too-long
267272

268273
self._pki_client.send_certificate(PKIPubKeyUsage.PUBKEY_USAGE_COIN_META, bytes.fromhex(cert_apdu))
269-
payload += format_tlv(TrustedNameTag.STRUCT_TYPE, 3) # TrustedName
270-
payload += format_tlv(TrustedNameTag.SIGNER_KEY_ID, 0) # test key
271-
payload += format_tlv(TrustedNameTag.SIGNER_ALGO, 1) # secp256k1
272-
payload += format_tlv(TrustedNameTag.SIGNATURE,
274+
payload += format_tlv(FieldTag.STRUCT_TYPE, 3) # TrustedName
275+
payload += format_tlv(FieldTag.SIGNER_KEY_ID, 0) # test key
276+
payload += format_tlv(FieldTag.SIGNER_ALGO, 1) # secp256k1
277+
payload += format_tlv(FieldTag.DER_SIGNATURE,
273278
sign_data(Key.TRUSTED_NAME, payload))
274279
chunks = self._cmd_builder.provide_trusted_name(payload)
275280
for chunk in chunks[:-1]:
276281
self._exchange(chunk)
277282
return self._exchange(chunks[-1])
278283

279284
def provide_trusted_name_v1(self, addr: bytes, name: str, challenge: int) -> RAPDU:
280-
payload = format_tlv(TrustedNameTag.STRUCT_VERSION, 1)
281-
payload += format_tlv(TrustedNameTag.CHALLENGE, challenge)
282-
payload += format_tlv(TrustedNameTag.COIN_TYPE, 0x3c) # ETH in slip-44
283-
payload += format_tlv(TrustedNameTag.NAME, name)
284-
payload += format_tlv(TrustedNameTag.ADDRESS, addr)
285+
payload = format_tlv(FieldTag.STRUCT_VERSION, 1)
286+
payload += format_tlv(FieldTag.CHALLENGE, challenge)
287+
payload += format_tlv(FieldTag.COIN_TYPE, 0x3c) # ETH in slip-44
288+
payload += format_tlv(FieldTag.TRUSTED_NAME, name)
289+
payload += format_tlv(FieldTag.ADDRESS, addr)
285290
return self._provide_trusted_name_common(payload)
286291

287292
def provide_trusted_name_v2(self,
@@ -293,19 +298,19 @@ def provide_trusted_name_v2(self,
293298
nft_id: Optional[int] = None,
294299
challenge: Optional[int] = None,
295300
not_valid_after: Optional[tuple[int]] = None) -> RAPDU:
296-
payload = format_tlv(TrustedNameTag.STRUCT_VERSION, 2)
297-
payload += format_tlv(TrustedNameTag.NAME, name)
298-
payload += format_tlv(TrustedNameTag.ADDRESS, addr)
299-
payload += format_tlv(TrustedNameTag.NAME_TYPE, name_type)
300-
payload += format_tlv(TrustedNameTag.NAME_SOURCE, name_source)
301-
payload += format_tlv(TrustedNameTag.CHAIN_ID, chain_id)
301+
payload = format_tlv(FieldTag.STRUCT_VERSION, 2)
302+
payload += format_tlv(FieldTag.TRUSTED_NAME, name)
303+
payload += format_tlv(FieldTag.ADDRESS, addr)
304+
payload += format_tlv(FieldTag.TRUSTED_NAME_TYPE, name_type)
305+
payload += format_tlv(FieldTag.TRUSTED_NAME_SOURCE, name_source)
306+
payload += format_tlv(FieldTag.CHAIN_ID, chain_id)
302307
if nft_id is not None:
303-
payload += format_tlv(TrustedNameTag.NFT_ID, nft_id)
308+
payload += format_tlv(FieldTag.TRUSTED_NAME_NFT_ID, nft_id)
304309
if challenge is not None:
305-
payload += format_tlv(TrustedNameTag.CHALLENGE, challenge)
310+
payload += format_tlv(FieldTag.CHALLENGE, challenge)
306311
if not_valid_after is not None:
307312
assert len(not_valid_after) == 3
308-
payload += format_tlv(TrustedNameTag.NOT_VALID_AFTER, struct.pack("BBB", *not_valid_after))
313+
payload += format_tlv(FieldTag.NOT_VALID_AFTER, struct.pack("BBB", *not_valid_after))
309314
return self._provide_trusted_name_common(payload)
310315

311316
def set_plugin(self,
@@ -468,3 +473,54 @@ def provide_token_metadata(self,
468473
decimals,
469474
chain_id,
470475
sig))
476+
477+
def _prepare_network_info(self,
478+
name: str,
479+
ticker: str,
480+
chain_id: int,
481+
icon: Optional[bytes] = None) -> bytes:
482+
483+
payload = format_tlv(FieldTag.STRUCT_TYPE, 8)
484+
payload += format_tlv(FieldTag.STRUCT_VERSION, 1)
485+
payload += format_tlv(FieldTag.BLOCKCHAIN_FAMILY, 1)
486+
payload += format_tlv(FieldTag.CHAIN_ID, chain_id.to_bytes(8, 'big'))
487+
payload += format_tlv(FieldTag.NETWORK_NAME, name.encode('utf-8'))
488+
payload += format_tlv(FieldTag.TICKER, ticker.encode('utf-8'))
489+
if icon:
490+
# Network Icon
491+
payload += format_tlv(FieldTag.NETWORK_ICON_HASH, sha256(icon).digest())
492+
# Append the data Signature
493+
payload += format_tlv(FieldTag.DER_SIGNATURE,
494+
sign_data(Key.CAL, payload))
495+
return payload
496+
497+
def provide_network_information(self,
498+
name: str,
499+
ticker: str,
500+
chain_id: int,
501+
icon: Optional[bytes] = None) -> RAPDU:
502+
503+
if self._pki_client is None:
504+
print(f"Ledger-PKI Not supported on '{self._firmware.name}'")
505+
else:
506+
# pylint: disable=line-too-long
507+
if self._firmware == Firmware.NANOSP:
508+
cert_apdu = "01010102010211040000000212010013020002140101160400000000200B45524332305F546F6B656E300200063101083201213321024CCA8FAD496AA5040A00A7EB2F5CC3B85376D88BA147A7D7054A99C64056188734010135010310040102000015473045022100C15795C2AE41E6FAE6B1362EE1AE216428507D7C1D6939B928559CC7A1F6425C02206139CF2E133DD62F3E00F183E42109C9853AC62B6B70C5079B9A80DBB9D54AB5" # noqa: E501
509+
elif self._firmware == Firmware.NANOX:
510+
cert_apdu = "01010102010211040000000212010013020002140101160400000000200B45524332305F546F6B656E300200063101083201213321024CCA8FAD496AA5040A00A7EB2F5CC3B85376D88BA147A7D7054A99C64056188734010135010215473045022100E3B956F93FBFF0D41908483888F0F75D4714662A692F7A38DC6C41A13294F9370220471991BECB3CA4F43413CADC8FF738A8CC03568BFA832B4DCFE8C469080984E5" # noqa: E501
511+
elif self._firmware == Firmware.STAX:
512+
cert_apdu = "01010102010211040000000212010013020002140101160400000000200B45524332305F546F6B656E300200063101083201213321024CCA8FAD496AA5040A00A7EB2F5CC3B85376D88BA147A7D7054A99C6405618873401013501041546304402206731FCD3E2432C5CA162381392FD17AD3A41EEF852E1D706F21A656AB165263602204B89FAE8DBAF191E2D79FB00EBA80D613CB7EDF0BE960CB6F6B29D96E1437F5F" # noqa: E501
513+
elif self._firmware == Firmware.FLEX:
514+
cert_apdu = "01010102010211040000000212010013020002140101160400000000200B45524332305F546F6B656E300200063101083201213321024CCA8FAD496AA5040A00A7EB2F5CC3B85376D88BA147A7D7054A99C64056188734010135010515473045022100B59EA8B958AA40578A6FBE9BBFB761020ACD5DBD8AA863C11DA17F42B2AFDE790220186316059EFA58811337D47C7F815F772EA42BBBCEA4AE123D1118C80588F5CB" # noqa: E501
515+
# pylint: enable=line-too-long
516+
517+
self._pki_client.send_certificate(PKIPubKeyUsage.PUBKEY_USAGE_COIN_META, bytes.fromhex(cert_apdu))
518+
519+
# Add the network info
520+
payload = self._prepare_network_info(name, ticker, chain_id, icon)
521+
chunks = self._cmd_builder.provide_network_information(payload, icon)
522+
for chunk in chunks[:-1]:
523+
response = self._exchange(chunk)
524+
assert response.status == StatusWord.OK
525+
response = self._exchange(chunks[-1])
526+
assert response.status == StatusWord.OK

client/src/ledger_app_clients/ethereum/command_builder.py

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import struct
55
from enum import IntEnum
6-
from typing import Optional
6+
from typing import List, Optional
77
from ragger.bip import pack_derivation_path
88

99
from .eip712 import EIP712FieldType
@@ -25,13 +25,16 @@ class InsType(IntEnum):
2525
GET_CHALLENGE = 0x20
2626
PROVIDE_TRUSTED_NAME = 0x22
2727
EXTERNAL_PLUGIN_SETUP = 0x12
28+
PROVIDE_NETWORK_INFORMATION = 0x30
2829

2930

3031
class P1Type(IntEnum):
3132
COMPLETE_SEND = 0x00
3233
PARTIAL_SEND = 0x01
3334
SIGN_FIRST_CHUNK = 0x00
3435
SIGN_SUBSQT_CHUNK = 0x80
36+
FIRST_CHUNK = 0x01
37+
FOLLOWING_CHUNK = 0x00
3538

3639

3740
class P2Type(IntEnum):
@@ -48,6 +51,8 @@ class P2Type(IntEnum):
4851
FILTERING_TOKEN_ADDR_CHECK = 0xfd
4952
FILTERING_AMOUNT_FIELD = 0xfe
5053
FILTERING_RAW = 0xff
54+
NETWORK_CONFIG = 0x00
55+
NETWORK_ICON = 0x01
5156

5257

5358
class CommandBuilder:
@@ -262,21 +267,11 @@ def sign(self, bip32_path: str, rlp_data: bytes, vrs: list) -> list[bytes]:
262267
payload += rlp_data
263268
p1 = P1Type.SIGN_FIRST_CHUNK
264269
while len(payload) > 0:
265-
chunk_size = 0xff
266-
267-
# TODO: Fix the app & remove this, issue #409
268-
if len(vrs) == 3:
269-
if len(payload) > chunk_size:
270-
import rlp
271-
diff = len(rlp.encode(vrs)) - (len(payload) - chunk_size)
272-
if diff > 0:
273-
chunk_size -= diff
274-
275270
apdus.append(self._serialize(InsType.SIGN,
276271
p1,
277272
0x00,
278-
payload[:chunk_size]))
279-
payload = payload[chunk_size:]
273+
payload[:0xff]))
274+
payload = payload[0xff:]
280275
p1 = P1Type.SIGN_SUBSQT_CHUNK
281276
return apdus
282277

@@ -408,3 +403,27 @@ def provide_erc20_token_information(self,
408403
0x00,
409404
0x00,
410405
payload)
406+
407+
def provide_network_information(self,
408+
tlv_payload: bytes,
409+
icon: Optional[bytes] = None) -> list[bytes]:
410+
chunks: List[bytes] = []
411+
412+
# Check if the TLV payload is larger than 0xff
413+
assert len(tlv_payload) < 0xff, "Payload too large"
414+
# Serialize the payload
415+
chunks.append(self._serialize(InsType.PROVIDE_NETWORK_INFORMATION,
416+
0x00,
417+
P2Type.NETWORK_CONFIG,
418+
tlv_payload))
419+
420+
if icon:
421+
p1 = P1Type.FIRST_CHUNK
422+
while len(icon) > 0:
423+
chunks.append(self._serialize(InsType.PROVIDE_NETWORK_INFORMATION,
424+
p1,
425+
P2Type.NETWORK_ICON,
426+
icon[:0xff]))
427+
icon = icon[0xff:]
428+
p1 = P1Type.FOLLOWING_CHUNK
429+
return chunks

doc/eth_contract_support_embedded.adoc

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
1-
Ethereum application : Embedded Contract Support
1+
= Ethereum application : Embedded Contract Support
22
================================================
3-
Ledger Firmware Team <hello@ledger.fr>
4-
Application version 1.3.0 - 05th of July 2020
53
6-
## 1.3.0
7-
- Initial release
84
95
## About
106

0 commit comments

Comments
 (0)