Skip to content

Commit

Permalink
Update client: Add support for TX Simulation
Browse files Browse the repository at this point in the history
  • Loading branch information
cedelavergne-ledger committed Dec 10, 2024
1 parent 4b34374 commit 88986a6
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 3 deletions.
62 changes: 60 additions & 2 deletions client/src/ledger_app_clients/ethereum/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,17 @@ class FieldTag(IntEnum):
ADDRESS = 0x22
CHAIN_ID = 0x23
TICKER = 0x24
TX_HASH = 0x27
BLOCKCHAIN_FAMILY = 0x51
NETWORK_NAME = 0x52
NETWORK_ICON_HASH = 0x53
TRUSTED_NAME_TYPE = 0x70
TRUSTED_NAME_SOURCE = 0x71
TRUSTED_NAME_NFT_ID = 0x72
W3C_NORMALIZED_RISK = 0x80
W3C_NORMALIZED_CATEGORY = 0x81
W3C_PROVIDER_MSG = 0x82
W3C_TINY_URL = 0x83


class PKIPubKeyUsage(IntEnum):
Expand Down Expand Up @@ -503,8 +508,7 @@ def _prepare_network_info(self,
# Network Icon
payload += format_tlv(FieldTag.NETWORK_ICON_HASH, sha256(icon).digest())
# Append the data Signature
payload += format_tlv(FieldTag.DER_SIGNATURE,
sign_data(Key.CAL, payload))
payload += format_tlv(FieldTag.DER_SIGNATURE, sign_data(Key.CAL, payload))
return payload

def provide_network_information(self,
Expand Down Expand Up @@ -540,3 +544,57 @@ def provide_network_information(self,
assert response.status == StatusWord.OK
response = self._exchange(chunks[-1])
assert response.status == StatusWord.OK

def _prepare_tx_simulation(self,
tx_hash: bytes,
risk: int,
category: int,
message: str,
url: str,
chain_id: int) -> bytes:

# Construct the TLV payload
payload: bytes = format_tlv(FieldTag.STRUCT_TYPE, 9)
payload += format_tlv(FieldTag.STRUCT_VERSION, 1)
payload += format_tlv(FieldTag.CHAIN_ID, chain_id.to_bytes(8, 'big'))
payload += format_tlv(FieldTag.TX_HASH, tx_hash)
payload += format_tlv(FieldTag.W3C_NORMALIZED_RISK, risk.to_bytes(2, 'big'))
payload += format_tlv(FieldTag.W3C_NORMALIZED_CATEGORY, category)
payload += format_tlv(FieldTag.W3C_PROVIDER_MSG, message.encode('utf-8'))
payload += format_tlv(FieldTag.W3C_TINY_URL, url.encode('utf-8'))
# Append the data Signature
payload += format_tlv(FieldTag.DER_SIGNATURE, sign_data(Key.CAL, payload))
return payload

def provide_tx_simulation(self,
tx_hash: bytes,
risk: int,
category: int,
message: str,
url: str,
chain_id: int,
demo: bool) -> RAPDU:

if self._pki_client is None:
print(f"Ledger-PKI Not supported on '{self._firmware.name}'")
else:
# pylint: disable=line-too-long
if self._firmware == Firmware.NANOSP:
cert_apdu = "01010102010211040000000212010013020002140101160400000000200B45524332305F546F6B656E300200063101083201213321024CCA8FAD496AA5040A00A7EB2F5CC3B85376D88BA147A7D7054A99C64056188734010135010310040102000015473045022100C15795C2AE41E6FAE6B1362EE1AE216428507D7C1D6939B928559CC7A1F6425C02206139CF2E133DD62F3E00F183E42109C9853AC62B6B70C5079B9A80DBB9D54AB5" # noqa: E501
elif self._firmware == Firmware.NANOX:
cert_apdu = "01010102010211040000000212010013020002140101160400000000200B45524332305F546F6B656E300200063101083201213321024CCA8FAD496AA5040A00A7EB2F5CC3B85376D88BA147A7D7054A99C64056188734010135010215473045022100E3B956F93FBFF0D41908483888F0F75D4714662A692F7A38DC6C41A13294F9370220471991BECB3CA4F43413CADC8FF738A8CC03568BFA832B4DCFE8C469080984E5" # noqa: E501
elif self._firmware == Firmware.STAX:
cert_apdu = "01010102010211040000000212010013020002140101160400000000200B45524332305F546F6B656E300200063101083201213321024CCA8FAD496AA5040A00A7EB2F5CC3B85376D88BA147A7D7054A99C6405618873401013501041546304402206731FCD3E2432C5CA162381392FD17AD3A41EEF852E1D706F21A656AB165263602204B89FAE8DBAF191E2D79FB00EBA80D613CB7EDF0BE960CB6F6B29D96E1437F5F" # noqa: E501
elif self._firmware == Firmware.FLEX:
cert_apdu = "01010102010211040000000212010013020002140101160400000000200B45524332305F546F6B656E300200063101083201213321024CCA8FAD496AA5040A00A7EB2F5CC3B85376D88BA147A7D7054A99C64056188734010135010515473045022100B59EA8B958AA40578A6FBE9BBFB761020ACD5DBD8AA863C11DA17F42B2AFDE790220186316059EFA58811337D47C7F815F772EA42BBBCEA4AE123D1118C80588F5CB" # noqa: E501
else:
print(f"Invalid device '{self._firmware.name}'")
cert_apdu = ""
# pylint: enable=line-too-long
if cert_apdu:
self._pki_client.send_certificate(PKIPubKeyUsage.PUBKEY_USAGE_COIN_META, bytes.fromhex(cert_apdu))

# Add the network info
payload = self._prepare_tx_simulation(tx_hash, risk, category, message, url, chain_id)
response = self._exchange(self._cmd_builder.provide_tx_simulation(demo, payload))
return response
10 changes: 10 additions & 0 deletions client/src/ledger_app_clients/ethereum/command_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class InsType(IntEnum):
PROVIDE_TRUSTED_NAME = 0x22
EXTERNAL_PLUGIN_SETUP = 0x12
PROVIDE_NETWORK_INFORMATION = 0x30
PROVIDE_TX_SIMULATION = 0x32


class P1Type(IntEnum):
Expand All @@ -35,6 +36,8 @@ class P1Type(IntEnum):
SIGN_SUBSQT_CHUNK = 0x80
FIRST_CHUNK = 0x01
FOLLOWING_CHUNK = 0x00
TX_SIMU_NORMAL = 0x00
TX_SIMU_DEMO = 0x01


class P2Type(IntEnum):
Expand Down Expand Up @@ -427,3 +430,10 @@ def provide_network_information(self,
icon = icon[0xff:]
p1 = P1Type.FOLLOWING_CHUNK
return chunks

def provide_tx_simulation(self, demo: bool, tlv_payload: bytes) -> bytes:
# Check if the TLV payload is larger than 0xff
assert len(tlv_payload) < 0xff, "Payload too large"
# Serialize the payload
p1 = P1Type.TX_SIMU_DEMO if demo else P1Type.TX_SIMU_NORMAL
return self._serialize(InsType.PROVIDE_TX_SIMULATION, p1, 0x00, tlv_payload)
16 changes: 15 additions & 1 deletion client/src/ledger_app_clients/ethereum/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class SettingID(Enum):
NONCE = auto()
VERBOSE_EIP712 = auto()
DEBUG_DATA = auto()
W3C = auto()


def get_device_settings(firmware: Firmware) -> list[SettingID]:
Expand All @@ -19,12 +20,21 @@ def get_device_settings(firmware: Firmware) -> list[SettingID]:
SettingID.NONCE,
SettingID.DEBUG_DATA,
]
if firmware.is_nano:
return [
SettingID.BLIND_SIGNING,
SettingID.VERBOSE_ENS,
SettingID.NONCE,
SettingID.VERBOSE_EIP712,
SettingID.DEBUG_DATA,
]
return [
SettingID.BLIND_SIGNING,
SettingID.VERBOSE_ENS,
SettingID.NONCE,
SettingID.VERBOSE_EIP712,
SettingID.DEBUG_DATA,
SettingID.W3C,
]


Expand Down Expand Up @@ -67,6 +77,10 @@ def settings_toggle(firmware: Firmware, nav: Navigator, to_toggle: list[SettingI
if (setting_idx > 0) and (setting_idx % settings_per_page) == 0:
moves += [NavInsID.USE_CASE_SETTINGS_NEXT]
if setting in to_toggle:
moves += [NavIns(NavInsID.TOUCH, get_setting_position(firmware, setting_idx, settings_per_page))]
if setting == SettingID.W3C and firmware == Firmware.STAX:
moves += [NavInsID.USE_CASE_SETTINGS_NEXT]
moves += [NavIns(NavInsID.TOUCH, get_setting_position(firmware, 0, settings_per_page))]
else:
moves += [NavIns(NavInsID.TOUCH, get_setting_position(firmware, setting_idx, settings_per_page))]
moves += [NavInsID.USE_CASE_SETTINGS_MULTI_PAGE_EXIT]
nav.navigate(moves, screen_change_before_first_instruction=False)

0 comments on commit 88986a6

Please sign in to comment.