diff --git a/.markdownlint.jsonc b/.markdownlint.jsonc new file mode 100644 index 0000000000..fc0e6f70ad --- /dev/null +++ b/.markdownlint.jsonc @@ -0,0 +1,290 @@ +// Example markdownlint configuration with all properties set to their default value +{ + + // Default state for all rules + "default": true, + + // Path to configuration file to extend + "extends": null, + + // MD001/heading-increment : Heading levels should only increment by one level at a time : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md001.md + "MD001": true, + + // MD003/heading-style : Heading style : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md003.md + "MD003": { + // Heading style + "style": "consistent" + }, + + // MD004/ul-style : Unordered list style : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md004.md + "MD004": { + // List style + "style": "consistent" + }, + + // MD005/list-indent : Inconsistent indentation for list items at the same level : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md005.md + "MD005": true, + + // MD007/ul-indent : Unordered list indentation : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md007.md + "MD007": { + // Spaces for indent + "indent": 2, + // Whether to indent the first level of the list + "start_indented": false, + // Spaces for first level indent (when start_indented is set) + "start_indent": 2 + }, + + // MD009/no-trailing-spaces : Trailing spaces : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md009.md + "MD009": { + // Spaces for line break + "br_spaces": 2, + // Allow spaces for empty lines in list items + "list_item_empty_lines": false, + // Include unnecessary breaks + "strict": false + }, + + // MD010/no-hard-tabs : Hard tabs : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md010.md + "MD010": { + // Include code blocks + "code_blocks": true, + // Fenced code languages to ignore + "ignore_code_languages": [], + // Number of spaces for each hard tab + "spaces_per_tab": 1 + }, + + // MD011/no-reversed-links : Reversed link syntax : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md011.md + "MD011": true, + + // MD012/no-multiple-blanks : Multiple consecutive blank lines : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md012.md + "MD012": { + // Consecutive blank lines + "maximum": 1 + }, + + // MD013/line-length : Line length : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md013.md + "MD013": { + // Number of characters + "line_length": 120, + // Number of characters for headings + "heading_line_length": 80, + // Number of characters for code blocks + "code_block_line_length": 80, + // Include code blocks + "code_blocks": false, + // Include tables + "tables": false, + // Include headings + "headings": true, + // Strict length checking + "strict": false, + // Stern length checking + "stern": false + }, + + // MD014/commands-show-output : Dollar signs used before commands without showing output : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md014.md + "MD014": true, + + // MD018/no-missing-space-atx : No space after hash on atx style heading : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md018.md + "MD018": true, + + // MD019/no-multiple-space-atx : Multiple spaces after hash on atx style heading : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md019.md + "MD019": true, + + // MD020/no-missing-space-closed-atx : No space inside hashes on closed atx style heading : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md020.md + "MD020": true, + + // MD021/no-multiple-space-closed-atx : Multiple spaces inside hashes on closed atx style heading : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md021.md + "MD021": true, + + // MD022/blanks-around-headings : Headings should be surrounded by blank lines : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md022.md + "MD022": { + // Blank lines above heading + "lines_above": 1, + // Blank lines below heading + "lines_below": 1 + }, + + // MD023/heading-start-left : Headings must start at the beginning of the line : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md023.md + "MD023": true, + + // MD024/no-duplicate-heading : Multiple headings with the same content : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md024.md + "MD024": { + // Only check sibling headings + "siblings_only": true + }, + + // MD025/single-title/single-h1 : Multiple top-level headings in the same document : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md025.md + "MD025": { + // Heading level + "level": 1, + // RegExp for matching title in front matter + "front_matter_title": "^\\s*title\\s*[:=]" + }, + + // MD026/no-trailing-punctuation : Trailing punctuation in heading : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md026.md + "MD026": { + // Punctuation characters + "punctuation": ".,;:!。,;:!" + }, + + // MD027/no-multiple-space-blockquote : Multiple spaces after blockquote symbol : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md027.md + "MD027": true, + + // MD028/no-blanks-blockquote : Blank line inside blockquote : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md028.md + "MD028": true, + + // MD029/ol-prefix : Ordered list item prefix : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md029.md + "MD029": { + // List style + "style": "one_or_ordered" + }, + + // MD030/list-marker-space : Spaces after list markers : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md030.md + "MD030": { + // Spaces for single-line unordered list items + "ul_single": 1, + // Spaces for single-line ordered list items + "ol_single": 1, + // Spaces for multi-line unordered list items + "ul_multi": 1, + // Spaces for multi-line ordered list items + "ol_multi": 1 + }, + + // MD031/blanks-around-fences : Fenced code blocks should be surrounded by blank lines : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md031.md + "MD031": { + // Include list items + "list_items": true + }, + + // MD032/blanks-around-lists : Lists should be surrounded by blank lines : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md032.md + "MD032": true, + + // MD033/no-inline-html : Inline HTML : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md033.md + "MD033": { + // Allowed elements + "allowed_elements": ["br", "div", "img", "details", "summary", "a", "p", "h1", "strong"] + }, + + // MD034/no-bare-urls : Bare URL used : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md034.md + "MD034": true, + + // MD035/hr-style : Horizontal rule style : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md035.md + "MD035": { + // Horizontal rule style + "style": "consistent" + }, + + // MD036/no-emphasis-as-heading : Emphasis used instead of a heading : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md036.md + "MD036": { + // Punctuation characters + "punctuation": ".,;:!?。,;:!?" + }, + + // MD037/no-space-in-emphasis : Spaces inside emphasis markers : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md037.md + "MD037": true, + + // MD038/no-space-in-code : Spaces inside code span elements : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md038.md + "MD038": true, + + // MD039/no-space-in-links : Spaces inside link text : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md039.md + "MD039": true, + + // MD040/fenced-code-language : Fenced code blocks should have a language specified : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md040.md + "MD040": { + // List of languages + "allowed_languages": [], + // Require language only + "language_only": false + }, + + // MD041/first-line-heading/first-line-h1 : First line in a file should be a top-level heading : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md041.md + "MD041": false, + + // MD042/no-empty-links : No empty links : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md042.md + "MD042": true, + + // MD043/required-headings : Required heading structure : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md043.md + "MD043": { + // List of headings + "headings": ["*"], + // Match case of headings + "match_case": false + }, + + // MD044/proper-names : Proper names should have the correct capitalization : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md044.md + "MD044": { + // List of proper names + "names": [], + // Include code blocks + "code_blocks": true, + // Include HTML elements + "html_elements": true + }, + + // MD045/no-alt-text : Images should have alternate text (alt text) : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md045.md + "MD045": false, + + // MD046/code-block-style : Code block style : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md046.md + "MD046": { + // Block style + "style": "consistent" + }, + + // MD047/single-trailing-newline : Files should end with a single newline character : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md047.md + "MD047": true, + + // MD048/code-fence-style : Code fence style : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md048.md + "MD048": { + // Code fence style + "style": "consistent" + }, + + // MD049/emphasis-style : Emphasis style : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md049.md + "MD049": { + // Emphasis style + "style": "consistent" + }, + + // MD050/strong-style : Strong style : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md050.md + "MD050": { + // Strong style + "style": "consistent" + }, + + // MD051/link-fragments : Link fragments should be valid : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md051.md + "MD051": true, + + // MD052/reference-links-images : Reference links and images should use a label that is defined : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md052.md + "MD052": { + // Include shortcut syntax + "shortcut_syntax": false + }, + + // MD053/link-image-reference-definitions : Link and image reference definitions should be needed : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md053.md + "MD053": { + // Ignored definitions + "ignored_definitions": [ + "//" + ] + }, + + // MD054/link-image-style : Link and image style : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md054.md + "MD054": { + // Allow autolinks + "autolink": true, + // Allow inline links and images + "inline": true, + // Allow full reference links and images + "full": true, + // Allow collapsed reference links and images + "collapsed": true, + // Allow shortcut reference links and images + "shortcut": true, + // Allow URLs as inline links + "url_inline": true + } +} diff --git a/.mdl.rb b/.mdl.rb deleted file mode 100644 index 0b971f650e..0000000000 --- a/.mdl.rb +++ /dev/null @@ -1,11 +0,0 @@ -# Style file for mdl -# https://github.com/markdownlint/markdownlint/blob/main/docs/creating_styles.md - -# Include all rules -all - -# Disable specific rules -#exclude_rule 'MD012' - -# Update rules configuration -rule 'MD013', :line_length => 120 diff --git a/.mdlrc b/.mdlrc deleted file mode 100644 index dd25d916db..0000000000 --- a/.mdlrc +++ /dev/null @@ -1,14 +0,0 @@ -# markdownlint config file - -# Use custom style file -style "#{File.dirname(__FILE__)}/.mdl.rb" - -# MD002 - First header in file should be a top level header -# MD005 - Inconsistent indentation for list items at the same level -# MD007 - Unordered list indentation -# MD014 - Dollar signs used before commands without showing output -# MD024 - Multiple headers with the same content -# MD029 - Ordered list item prefix -# MD033 - Inline HTML -# MD041 - First line in file should be a top level header -rules "~MD002,~MD005,~MD007,~MD014,~MD024,~MD029,~MD033,~MD041" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 764c270661..36353fa818 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer @@ -14,32 +14,33 @@ repos: - id: check-case-conflict - repo: https://github.com/codespell-project/codespell - rev: v2.3.0 + rev: v2.4.1 hooks: - id: codespell - args: ['--ignore-words-list', 'ontop,shft,hte', '--skip', 'makefile_conf/chain/*,tests/ragger/eip712_input_files/*'] + args: ['--ignore-words-list', 'ontop,shft,hte,eSpace', '--skip', 'makefile_conf/chain/*,tests/ragger/eip712_input_files/*'] - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v12.0.1 + rev: v14.0.6 hooks: - id: clang-format types_or: [c] - repo: https://github.com/Mateusz-Grzelinski/actionlint-py - rev: v1.7.6.22 + rev: v1.7.7.24 hooks: - id: actionlint types_or: [yaml] args: [-shellcheck='' -pyflakes=''] - - repo: https://github.com/markdownlint/markdownlint - rev: v0.12.0 + - repo: https://github.com/DavidAnson/markdownlint-cli2 + rev: v0.13.0 hooks: - - id: markdownlint + - id: markdownlint-cli2 types_or: [markdown] + exclude: CHANGELOG.md - repo: https://github.com/PyCQA/pylint - rev: v3.3.3 + rev: v3.3.8 hooks: - id: pylint language: system diff --git a/client/CHANGELOG.md b/client/CHANGELOG.md index 9ff0ea51d9..24c1b831d8 100644 --- a/client/CHANGELOG.md +++ b/client/CHANGELOG.md @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Support for Apex devices - + ## [0.5.0] - 2025-06-30 ### Added diff --git a/client/src/ledger_app_clients/ethereum/client.py b/client/src/ledger_app_clients/ethereum/client.py index cae740a04e..4b7427f3e2 100644 --- a/client/src/ledger_app_clients/ethereum/client.py +++ b/client/src/ledger_app_clients/ethereum/client.py @@ -19,6 +19,7 @@ from .ledger_pki import PKIClient, PKIPubKeyUsage from .dynamic_networks import DynamicNetwork from .safe import SafeAccount, AccountType +from .gating import Gating class TrustedNameType(IntEnum): @@ -539,3 +540,12 @@ def provide_safe_account(self, safe_params: SafeAccount): for chunk in chunks[:-1]: self._exchange(chunk) return self._exchange_async(chunks[-1]) + + def provide_gating(self, gating_descriptor: Gating): + # Send ledgerPKI certificate + self.pki_client.send_certificate(PKIPubKeyUsage.PUBKEY_USAGE_GATING) + + chunks = self._cmd_builder.provide_gating(gating_descriptor.serialize()) + for chunk in chunks[:-1]: + self._exchange(chunk) + return self._exchange(chunks[-1]) diff --git a/client/src/ledger_app_clients/ethereum/command_builder.py b/client/src/ledger_app_clients/ethereum/command_builder.py index 1467efe1f3..20e33e7c19 100644 --- a/client/src/ledger_app_clients/ethereum/command_builder.py +++ b/client/src/ledger_app_clients/ethereum/command_builder.py @@ -35,6 +35,7 @@ class InsType(IntEnum): PROVIDE_TX_SIMULATION = 0x32 SIGN_EIP7702_AUTHORIZATION = 0x34 PROVIDE_SAFE_ACCOUNT = 0x36 + PROVIDE_GATING = 0x38 class P1Type(IntEnum): @@ -606,3 +607,6 @@ def provide_proxy_info(self, tlv_payload: bytes) -> list[bytes]: def provide_safe_account(self, tlv_payload: bytes, p2: int) -> list[bytes]: return self.common_tlv_serialize(InsType.PROVIDE_SAFE_ACCOUNT, tlv_payload, p2l=[p2]) + + def provide_gating(self, tlv_payload: bytes) -> list[bytes]: + return self.common_tlv_serialize(InsType.PROVIDE_GATING, tlv_payload) diff --git a/client/src/ledger_app_clients/ethereum/gating.py b/client/src/ledger_app_clients/ethereum/gating.py new file mode 100644 index 0000000000..2aac801a7e --- /dev/null +++ b/client/src/ledger_app_clients/ethereum/gating.py @@ -0,0 +1,49 @@ +from typing import Optional +from .tlv import TlvSerializable, FieldTag +from .keychain import sign_data, Key + + +class Gating(TlvSerializable): + address: bytes + chain_id: int + intro_message: str + tiny_url: str + selector: Optional[bytes] + signature: Optional[bytes] + + def __init__(self, + address: bytes, + chain_id: int, + intro_message: str, + tiny_url: str, + selector: Optional[bytes] = None, + signature: Optional[bytes] = None) -> None: + + self.address = address + self.chain_id = chain_id + self.intro_message = intro_message + self.tiny_url = tiny_url + self.selector = selector + self.signature = signature + + def serialize(self) -> bytes: + assert self.address is not None, "Address is required" + assert self.chain_id is not None, "Chain ID is required" + assert self.intro_message is not None, "Intro message is required" + assert self.tiny_url is not None, "Tiny URL is required" + + # Construct the TLV payload + payload: bytes = self.serialize_field(FieldTag.STRUCT_TYPE, 0x0D) + payload += self.serialize_field(FieldTag.STRUCT_VERSION, 1) + payload += self.serialize_field(FieldTag.ADDRESS, self.address) + payload += self.serialize_field(FieldTag.CHAIN_ID, self.chain_id.to_bytes(8, 'big')) + payload += self.serialize_field(FieldTag.MESSAGE, self.intro_message.encode('utf-8')) + payload += self.serialize_field(FieldTag.TINY_URL, self.tiny_url.encode('utf-8')) + if self.selector: + payload += self.serialize_field(FieldTag.SELECTOR, self.selector) + # Append the data Signature + sig = self.signature + if sig is None: + sig = sign_data(Key.GATING, payload) + payload += self.serialize_field(FieldTag.DER_SIGNATURE, sig) + return payload diff --git a/client/src/ledger_app_clients/ethereum/keychain.py b/client/src/ledger_app_clients/ethereum/keychain.py index 8d9574c9c4..bf52eba5ce 100644 --- a/client/src/ledger_app_clients/ethereum/keychain.py +++ b/client/src/ledger_app_clients/ethereum/keychain.py @@ -16,6 +16,7 @@ class Key(Enum): NETWORK = auto() TRANSACTION_CHECKS = auto() SAFE = auto() + GATING = auto() _keys: dict[Key, SigningKey] = {} diff --git a/client/src/ledger_app_clients/ethereum/keychain/gating.pem b/client/src/ledger_app_clients/ethereum/keychain/gating.pem new file mode 100644 index 0000000000..200e79f62d --- /dev/null +++ b/client/src/ledger_app_clients/ethereum/keychain/gating.pem @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BgUrgQQACg== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHQCAQEEIGyOwr4XDwHOnN4KiaN3IskTN0eC+61bFpvES5SsgFcsoAcGBSuBBAAK +oUQDQgAEM+XApEHzselXzhImk9+SfV8cp3R2n3LtOKAkxTs2qvcJDV0Q0amEqeVl +xFfCUeZf95q7i12VJXTalzmTDOk9Pw== +-----END EC PRIVATE KEY----- diff --git a/client/src/ledger_app_clients/ethereum/ledger_pki.py b/client/src/ledger_app_clients/ethereum/ledger_pki.py index 9cd7261dc0..863c1fa76e 100644 --- a/client/src/ledger_app_clients/ethereum/ledger_pki.py +++ b/client/src/ledger_app_clients/ethereum/ledger_pki.py @@ -22,6 +22,7 @@ class PKIPubKeyUsage(IntEnum): PUBKEY_USAGE_CALLDATA = 0x0b PUBKEY_USAGE_NETWORK = 0x0c PUBKEY_USAGE_SAFE_ACCOUNT = 0x0e + PUBKEY_USAGE_GATING = 0x0f # pylint: disable=line-too-long @@ -130,6 +131,14 @@ class PKIPubKeyUsage(IntEnum): DeviceType.APEX_M: "", # noqa: E501 DeviceType.APEX_P: "010101020102110400000002120100130200021401011604000000002004536166653002000531010E320121332103E9800A3BEE47205CF50389904B035B4D33CDF894BDE73A83C88A98BC42C1419C34010135010615473045022100EEDA7D85FF4B3B162060C580B5F039898C6FBBDD894DBA1625764CFE4A5175ED02200F33F00DC0379BDBA9832062B6FDB926FB3F8B4ABEC3BC7ADA92BD2F80DAF295", # noqa: E501 }, + PKIPubKeyUsage.PUBKEY_USAGE_GATING: { + DeviceType.NANOSP: "010101020102110400000002120100130200021401011604000000002006476174696E673002000B31010F32012133210333E5C0A441F3B1E957CE122693DF927D5F1CA774769F72ED38A024C53B36AAF73401013501031546304402205EE2B9452DA90B4C147A81712BE3C3582B67D6944F89B5DA23FCBD97DAB1A8E602207208186C0C4CFBBC87DFA495FD4541CB1DA6DC508A346D99F1C24A70C76DADD6", # noqa: E501 + DeviceType.NANOX: "010101020102110400000002120100130200021401011604000000002006476174696E673002000B31010F32012133210333E5C0A441F3B1E957CE122693DF927D5F1CA774769F72ED38A024C53B36AAF734010135010215473045022100EF7343DDC44CE5E294BF4300D42AA880F4CAB323184BA6FEF08FF9DD8FA60349022045875326E53D985BE4C5EC1B1E22FC7B3D0995280127AB794387CAAF586B4A87", # noqa: E501 + DeviceType.STAX: "010101020102110400000002120100130200021401011604000000002006476174696E673002000B31010F32012133210333E5C0A441F3B1E957CE122693DF927D5F1CA774769F72ED38A024C53B36AAF73401013501041546304402207584521838920FF07DF369BA576024BDC39C31E4AFC79A5B663D1C7CF43E3CD3022043D8CE2A3EA70903D9568C484F4FCB9F31F532C30282CA79555739AD7BAF36FB", # noqa: E501 + DeviceType.FLEX: "010101020102110400000002120100130200021401011604000000002006476174696E673002000B31010F32012133210333E5C0A441F3B1E957CE122693DF927D5F1CA774769F72ED38A024C53B36AAF734010135010515473045022100B22844E85815639EAE17CB080EDE3986BA2A43F63D7453EFF4AA73B96B9F0F8F02206DFABE54E2CC1460142BF0CBB85FBD946F541EFA804F0D009CB7BFCA8DB71FF0", # noqa: E501 + DeviceType.APEX_M: "", # noqa: E501 + DeviceType.APEX_P: "010101020102110400000002120100130200021401011604000000002006476174696E673002000B31010F32012133210333E5C0A441F3B1E957CE122693DF927D5F1CA774769F72ED38A024C53B36AAF7340101350106154730450221008D7667AE80FF1D48896FDE03A8023CE52D704E6B0A451AA1BFE3F2A1FAA63D2902200CD2DAC679D53EBB116523965ECC65FF4F8FFF90175D1FA760D2027179D39091", # noqa: E501 + }, } diff --git a/client/src/ledger_app_clients/ethereum/tlv.py b/client/src/ledger_app_clients/ethereum/tlv.py index a78830b106..84e7c1b860 100644 --- a/client/src/ledger_app_clients/ethereum/tlv.py +++ b/client/src/ledger_app_clients/ethereum/tlv.py @@ -17,6 +17,7 @@ class FieldTag(IntEnum): TICKER = 0x24 TX_HASH = 0x27 DOMAIN_HASH = 0x28 + SELECTOR = 0x40 BLOCKCHAIN_FAMILY = 0x51 NETWORK_NAME = 0x52 NETWORK_ICON_HASH = 0x53 @@ -25,8 +26,8 @@ class FieldTag(IntEnum): TRUSTED_NAME_NFT_ID = 0x72 TX_CHECKS_NORMALIZED_RISK = 0x80 TX_CHECKS_NORMALIZED_CATEGORY = 0x81 - TX_CHECKS_PROVIDER_MSG = 0x82 - TX_CHECKS_TINY_URL = 0x83 + MESSAGE = 0x82 + TINY_URL = 0x83 TX_CHECKS_SIMULATION_TYPE = 0x84 THRESHOLD = 0xa0, SIGNERS_COUNT = 0xa1, diff --git a/client/src/ledger_app_clients/ethereum/tx_simu.py b/client/src/ledger_app_clients/ethereum/tx_simu.py index b58a18dcda..ecd0966e56 100644 --- a/client/src/ledger_app_clients/ethereum/tx_simu.py +++ b/client/src/ledger_app_clients/ethereum/tx_simu.py @@ -20,7 +20,7 @@ class TxSimu(TlvSerializable): tx_hash: Optional[bytes] = None chain_id: Optional[int] = None domain_hash: Optional[bytes] = None - provider_message: Optional[str] + provider_message: Optional[str] def __init__(self, simu_type: SimuType, @@ -53,13 +53,13 @@ def serialize(self) -> bytes: payload += self.serialize_field(FieldTag.TX_HASH, self.tx_hash) payload += self.serialize_field(FieldTag.TX_CHECKS_NORMALIZED_RISK, self.risk) payload += self.serialize_field(FieldTag.TX_CHECKS_NORMALIZED_CATEGORY, self.category) - payload += self.serialize_field(FieldTag.TX_CHECKS_TINY_URL, self.tiny_url.encode('utf-8')) + payload += self.serialize_field(FieldTag.TINY_URL, self.tiny_url.encode('utf-8')) if self.chain_id: payload += self.serialize_field(FieldTag.CHAIN_ID, self.chain_id.to_bytes(8, 'big')) if self.domain_hash: payload += self.serialize_field(FieldTag.DOMAIN_HASH, self.domain_hash) if self.provider_message: - payload += self.serialize_field(FieldTag.TX_CHECKS_PROVIDER_MSG, self.provider_message.encode('utf-8')) + payload += self.serialize_field(FieldTag.MESSAGE, self.provider_message.encode('utf-8')) # Append the data Signature payload += self.serialize_field(FieldTag.DER_SIGNATURE, sign_data(Key.TRANSACTION_CHECKS, payload)) diff --git a/doc/eip712.md b/doc/eip712.md index 5271ef2ee0..7ec73578f9 100644 --- a/doc/eip712.md +++ b/doc/eip712.md @@ -2,8 +2,7 @@ ## Specifications -https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md - +[Reference](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md) ## Example message diff --git a/doc/ethapp.adoc b/doc/ethapp.adoc index a85d8cfa70..53b57135fb 100644 --- a/doc/ethapp.adoc +++ b/doc/ethapp.adoc @@ -1704,6 +1704,52 @@ _Output data_ None +### PROVIDE GATED SIGNING + +#### Description + +This command provides the Gating descriptor. + +The descriptor is then presented to the user before the Transaction. + +The information is sent in TLV (Tag-Length-Value) mode. + +#### Coding + +_Command_ + +[width="80%"] +|============================================================================================================================== +| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* +| E0 | 38 | 00 : first chunk + + 01 : following chunk + | 00 +|============================================================================================================================== + +_Input data_ + +###### If P1 == first chunk + +[width="80%"] +|==================================================================== +| *Description* | *Length (byte)* +| Payload length | 2 +| link:tlv_structs.md#gating_descriptor[GATING_DESCRIPTOR struct] | variable +|==================================================================== + +###### If P1 == following chunk + +[width="80%"] +|==================================================================== +| *Description* | *Length (byte)* +| link:tlv_structs.md#gating_descriptor[GATING_DESCRIPTOR struct] | variable +|==================================================================== + +_Output data_ + +None + ## Transport protocol ### General transport description diff --git a/doc/tlv_structs.md b/doc/tlv_structs.md index 26f94a0ade..669b95bf90 100644 --- a/doc/tlv_structs.md +++ b/doc/tlv_structs.md @@ -22,7 +22,8 @@ > - `$.metadata.owner` is optional, made `CREATOR_NAME` optional > - `$.metadata.info.legalName` is optional, made `CREATOR_LEGAL_NAME` optional > - `$.display.formats..intent` is optional, possible fallbacks: `$.display.formats..$id`, `$.display.formats.` -> - `CONTRACT_NAME` is not really materialized in the spec, closest is `$.metadata.info.$id`, but `$id` is supposed to be internal +> - `CONTRACT_NAME` is not really materialized in the spec, closest is `$.metadata.info.$id`, + but `$id` is supposed to be internal > - `$.metadata.info.lastUpdate` is optional, made `DEPLOY_DATE` optional ## ENUM_VALUE @@ -402,3 +403,18 @@ The _Role_ is normalized and interpreted like this: | SIGNATURE | 0x15 | uint8[] | Signature of the structure | | The signature is computed on the full payload data, using `CX_CURVE_SECP256K1`. + +## GATING_SIGNING + +### GATING_DESCRIPTOR + +| Name | Tag | Payload type | Description | Value | +|-------------------|------|--------------|----------------------------------|-----------------------------------| +| STRUCTURE_TYPE | 0x01 | uint8 | Structure type | `0x0D` (`TYPE_GATED_SIGNING`) | +| STRUCTURE_VERSION | 0x02 | uint8 | Structure version | `0x01` | +| ADDRESS | 0x22 | uint8[20] | Transaction Address | | +| CHAIN_ID | 0x23 | uint64 | Transaction chain ID | | +| SELECTOR | 0x40 | uint8[4] | Smart contract function selector | | +| SIGNATURE | 0x15 | uint8[] | Signature of the structure | | + +The signature is computed on the full payload data, using `CX_CURVE_SECP256K1`. diff --git a/glyphs/ledger_14px.png b/glyphs/ledger_14px.png new file mode 100644 index 0000000000..bdce2a2cda Binary files /dev/null and b/glyphs/ledger_14px.png differ diff --git a/glyphs/ledger_48px.png b/glyphs/ledger_48px.png new file mode 100644 index 0000000000..45cd73b2a5 Binary files /dev/null and b/glyphs/ledger_48px.png differ diff --git a/glyphs/ledger_64px.png b/glyphs/ledger_64px.png new file mode 100644 index 0000000000..b1073ce3ae Binary files /dev/null and b/glyphs/ledger_64px.png differ diff --git a/makefile_conf/chain/harmony.mk b/makefile_conf/chain/harmony.mk index de4b8adbbc..0210324bf2 100644 --- a/makefile_conf/chain/harmony.mk +++ b/makefile_conf/chain/harmony.mk @@ -1,4 +1,4 @@ PATH_APP_LOAD_PARAMS += "44'/1023'" TICKER = "ONE" CHAIN_ID = 1666600000 -APPNAME = "Harmony" \ No newline at end of file +APPNAME = "Harmony" diff --git a/makefile_conf/chain/iotex.mk b/makefile_conf/chain/iotex.mk index f69d79e7c3..b6cda8ad38 100644 --- a/makefile_conf/chain/iotex.mk +++ b/makefile_conf/chain/iotex.mk @@ -1,4 +1,4 @@ PATH_APP_LOAD_PARAMS += "44'/304'" TICKER = "IOTX" CHAIN_ID = 4689 -APPNAME = "IoTeX" \ No newline at end of file +APPNAME = "IoTeX" diff --git a/makefile_conf/features.mk b/makefile_conf/features.mk index 481f366efe..482b1aabc2 100644 --- a/makefile_conf/features.mk +++ b/makefile_conf/features.mk @@ -58,6 +58,9 @@ ifeq ($(TARGET_NAME),$(filter $(TARGET_NAME),TARGET_STAX TARGET_FLEX TARGET_APEX DEFINES += HAVE_SAFE_ACCOUNT endif +# Gating signing +DEFINES += HAVE_GATING_SUPPORT + EIP7702_TEST_WHITELIST ?= 0 ifneq ($(EIP7702_TEST_WHITELIST),0) DEFINES += HAVE_EIP7702_WHITELIST_TEST diff --git a/src/apdu_constants.h b/src/apdu_constants.h index 644dd57be1..1b7c3f8248 100644 --- a/src/apdu_constants.h +++ b/src/apdu_constants.h @@ -35,6 +35,7 @@ #define INS_PROVIDE_TX_SIMULATION 0x32 #define INS_SIGN_EIP7702_AUTHORIZATION 0x34 #define INS_PROVIDE_SAFE_ACCOUNT 0x36 +#define INS_PROVIDE_GATING 0x38 #define INS_STR(x) \ (x == INS_GET_PUBLIC_KEY ? "GET_PUBLIC_KEY" \ @@ -62,6 +63,7 @@ : x == INS_PROVIDE_TX_SIMULATION ? "PROVIDE_TX_SIMULATION" \ : x == INS_SIGN_EIP7702_AUTHORIZATION ? "SIGN_EIP7702_AUTHORIZATION" \ : x == INS_PROVIDE_SAFE_ACCOUNT ? "PROVIDE_SAFE_ACCOUNT" \ + : x == INS_PROVIDE_GATING ? "PROVIDE_GATING" \ : "Unknown") #define P1_CONFIRM 0x01 #define P1_NON_CONFIRM 0x00 diff --git a/src/features/provide_gating/cmd_get_gating.c b/src/features/provide_gating/cmd_get_gating.c new file mode 100644 index 0000000000..8d808e067a --- /dev/null +++ b/src/features/provide_gating/cmd_get_gating.c @@ -0,0 +1,609 @@ +/******************************************************************************* + * Ledger Ethereum App + * (c) 2025 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ********************************************************************************/ + +#ifdef HAVE_GATING_SUPPORT + +#include "cmd_get_gating.h" +#include "apdu_constants.h" +#include "hash_bytes.h" +#include "public_keys.h" +#include "getPublicKey.h" +#include "tlv.h" +#include "tlv_apdu.h" +#include "utils.h" +#include "nbgl_use_case.h" +#include "os_pki.h" +#include "network.h" +#include "ui_callbacks.h" +#include "ui_nbgl.h" +#include "common_utils.h" +#include "plugin_utils.h" +#include "mem_utils.h" +#include "utils.h" +#include "ui_logic.h" +#include "feature_signTx.h" +#include "proxy_info.h" + +// Display the Dated Signing screen 1/X times +#define GATED_SIGNING_MAX_COUNT 10 + +#define TYPE_GATED_SIGNING 0x0D +#define STRUCT_VERSION 0x01 + +#define GATING_MSG_SIZE 100 +#define GATING_URL_SIZE 30 + +enum { + TAG_STRUCTURE_TYPE = 0x01, + TAG_STRUCTURE_VERSION = 0x02, + TAG_ADDRESS = 0x22, + TAG_CHAIN_ID = 0x23, + TAG_SELECTOR = 0x40, + TAG_INTRO_MSG = 0x82, + TAG_TINY_URL = 0x83, + TAG_DER_SIGNATURE = 0x15, +}; + +enum { + BIT_STRUCTURE_TYPE, + BIT_STRUCTURE_VERSION, + BIT_ADDRESS, + BIT_CHAIN_ID, + BIT_SELECTOR, + BIT_INTRO_MSG, + BIT_TINY_URL, + BIT_DER_SIGNATURE, +}; + +typedef struct gating_s { + uint64_t chain_id; + const char selector[SELECTOR_SIZE]; + const char intro_msg[GATING_MSG_SIZE + 1]; // +1 for the null terminator + const char tiny_url[GATING_URL_SIZE + 1]; // +1 for the null terminator + const char addr[ADDRESS_LENGTH]; +} gating_t; + +typedef struct { + gating_t *gating; + uint8_t sig_size; + uint8_t *sig; + cx_sha256_t hash_ctx; + uint32_t rcv_flags; +} s_gating_ctx; + +// Global structure to store the tx gating parameters +static gating_t *GATING = NULL; +static nbgl_preludeDetails_t prelude_details = {0}; +static nbgl_genericDetails_t generic_details = {0}; + +// Macros to check the field length +#define CHECK_FIELD_LENGTH(tag, len, expected) \ + do { \ + if (len != expected) { \ + PRINTF("%s Size mismatch!\n", tag); \ + return SWO_INCORRECT_DATA; \ + } \ + } while (0) +#define CHECK_FIELD_OVERFLOW(tag, field, len) \ + do { \ + if (len >= sizeof(field)) { \ + PRINTF("%s Size overflow!\n", tag); \ + return SWO_INSUFFICIENT_MEMORY; \ + } \ + } while (0) + +// Macro to check the field value +#define CHECK_FIELD_VALUE(tag, value, expected) \ + do { \ + if (value != expected) { \ + PRINTF("%s Value mismatch!\n", tag); \ + return SWO_INCORRECT_DATA; \ + } \ + } while (0) + +// Macro to check the field value +#define CHECK_EMPTY_BUFFER(tag, field, len) \ + do { \ + if (memcmp(field, empty, len) == 0) { \ + PRINTF("%s Zero buffer!\n", tag); \ + return SWO_INCORRECT_DATA; \ + } \ + } while (0) + +// Macro to copy the field +#define COPY_FIELD(field, data) \ + do { \ + memmove((void *) field, data->value, data->length); \ + } while (0) + +/** + * @brief Parse the STRUCTURE_TYPE value. + * + * @param[in] data the tlv data + * @param[in] context Gating context + * @return APDU Response code + */ +static uint16_t parse_struct_type(const s_tlv_data *data, s_gating_ctx *context) { + CHECK_FIELD_LENGTH("STRUCTURE_TYPE", data->length, 1); + CHECK_FIELD_VALUE("STRUCTURE_TYPE", data->value[0], TYPE_GATED_SIGNING); + context->rcv_flags |= SET_BIT(BIT_STRUCTURE_TYPE); + return SWO_SUCCESS; +} + +/** + * @brief Parse the STRUCTURE_VERSION value. + * + * @param[in] data the tlv data + * @param[in] context Gating context + * @return APDU Response code + */ +static uint16_t parse_struct_version(const s_tlv_data *data, s_gating_ctx *context) { + CHECK_FIELD_LENGTH("STRUCTURE_VERSION", data->length, 1); + CHECK_FIELD_VALUE("STRUCTURE_VERSION", data->value[0], STRUCT_VERSION); + context->rcv_flags |= SET_BIT(BIT_STRUCTURE_VERSION); + return SWO_SUCCESS; +} + +/** + * @brief Parse the SELECTOR value. + * + * @param[in] data the tlv data + * @param[in] context Gating context + * @return APDU Response code + */ +static uint16_t parse_selector(const s_tlv_data *data, s_gating_ctx *context) { + uint8_t empty[SELECTOR_SIZE] = {0}; + CHECK_FIELD_LENGTH("SELECTOR", data->length, SELECTOR_SIZE); + CHECK_EMPTY_BUFFER("SELECTOR", data->value, data->length); + COPY_FIELD(context->gating->selector, data); + context->rcv_flags |= SET_BIT(BIT_SELECTOR); + return SWO_SUCCESS; +} + +/** + * @brief Parse the ADDRESS value. + * + * @param[in] data the tlv data + * @param[in] context Gating context + * @return APDU Response code + */ +static uint16_t parse_address(const s_tlv_data *data, s_gating_ctx *context) { + uint8_t empty[ADDRESS_LENGTH] = {0}; + CHECK_FIELD_LENGTH("ADDRESS", data->length, ADDRESS_LENGTH); + CHECK_EMPTY_BUFFER("ADDRESS", data->value, data->length); + COPY_FIELD(context->gating->addr, data); + context->rcv_flags |= SET_BIT(BIT_ADDRESS); + return SWO_SUCCESS; +} + +/** + * @brief Parse the CHAIN_ID value. + * + * @param[in] data the tlv data + * @param[in] context Gating context + * @return APDU Response code + */ +static uint16_t parse_chain_id(const s_tlv_data *data, s_gating_ctx *context) { + uint64_t chain_id; + uint64_t max_range; + + CHECK_FIELD_LENGTH("CHAIN_ID", data->length, sizeof(uint64_t)); + // Check if the chain ID is supported + // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2294.md + max_range = 0x7FFFFFFFFFFFFFDB; + chain_id = u64_from_BE(data->value, data->length); + // Check if the chain_id is supported + if ((chain_id > max_range) || (chain_id == 0)) { + PRINTF("Unsupported chain ID: %u\n", chain_id); + return SWO_INCORRECT_DATA; + } + + context->gating->chain_id = chain_id; + context->rcv_flags |= SET_BIT(BIT_CHAIN_ID); + return SWO_SUCCESS; +} + +/** + * @brief Parse the INTRO_MSG value. + * + * @param[in] data the tlv data + * @param[in] context Gating context + * @return APDU Response code + */ +static uint16_t parse_intro_msg(const s_tlv_data *data, s_gating_ctx *context) { + CHECK_FIELD_OVERFLOW("INTRO_MSG", context->gating->intro_msg, data->length); + // Check if the name is printable + if (!is_printable((const char *) data->value, data->length)) { + PRINTF("INTRO_MSG is not printable!\n"); + return SWO_INCORRECT_DATA; + } + COPY_FIELD(context->gating->intro_msg, data); + context->rcv_flags |= SET_BIT(BIT_INTRO_MSG); + return SWO_SUCCESS; +} + +/** + * @brief Parse the TINY_URL value. + * + * @param[in] data the tlv data + * @param[in] context Gating context + * @return APDU Response code + */ +static uint16_t parse_tiny_url(const s_tlv_data *data, s_gating_ctx *context) { + CHECK_FIELD_OVERFLOW("TINY_URL", context->gating->tiny_url, data->length); + // Check if the name is printable + if (!is_printable((const char *) data->value, data->length)) { + PRINTF("TINY_URL is not printable!\n"); + return SWO_INCORRECT_DATA; + } + COPY_FIELD(context->gating->tiny_url, data); + context->rcv_flags |= SET_BIT(BIT_TINY_URL); + return SWO_SUCCESS; +} + +/** + * @brief Parse the SIGNATURE value. + * + * @param[in] data the tlv data + * @param[in] context Gating context + * @return APDU Response code + */ +static uint16_t parse_signature(const s_tlv_data *data, s_gating_ctx *context) { + context->sig_size = data->length; + context->sig = (uint8_t *) data->value; + context->rcv_flags |= SET_BIT(BIT_DER_SIGNATURE); + return SWO_SUCCESS; +} + +/** + * @brief Verify the payload signature + * + * Verify the SHA-256 hash of the payload against the public key + * + * @param[in] context Gating context + * @return whether it was successful + */ +static bool verify_signature(s_gating_ctx *context) { + uint8_t hash[INT256_LENGTH]; + cx_err_t error = CX_INTERNAL_ERROR; + bool ret_code = false; + + CX_CHECK( + cx_hash_no_throw((cx_hash_t *) &context->hash_ctx, CX_LAST, NULL, 0, hash, INT256_LENGTH)); + + CX_CHECK(check_signature_with_pubkey("Gating Signing", + hash, + sizeof(hash), + NULL, + 0, + CERTIFICATE_PUBLIC_KEY_USAGE_GATED_SIGNING, + (uint8_t *) (context->sig), + context->sig_size)); + + ret_code = true; +end: + return ret_code; +} + +/** + * @brief Verify the received fields + * + * Check the mandatory fields are present + * + * @param[in] context Gating context + * @return whether it was successful + */ +static bool verify_fields(s_gating_ctx *context) { + uint32_t expected_fields; + + expected_fields = (1 << BIT_STRUCTURE_TYPE) | (1 << BIT_STRUCTURE_VERSION) | + (1 << BIT_CHAIN_ID) | (1 << BIT_ADDRESS) | (1 << BIT_INTRO_MSG) | + (1 << BIT_TINY_URL) | (1 << BIT_DER_SIGNATURE); + + return ((context->rcv_flags & expected_fields) == expected_fields); +} + +/** + * @brief Print the gating parameters. + * + * @param[in] context Gating context + * Only for debug purpose. + */ +static void print_gating_info(s_gating_ctx *context) { + char chain_str[sizeof(uint64_t) * 2 + 1] = {0}; + + PRINTF("****************************************************************************\n"); + PRINTF("[GATING] - Retrieved Gating descriptor:\n"); + PRINTF("[GATING] - Address: %.*h\n", ADDRESS_LENGTH, context->gating->addr); + if (context->gating->chain_id != 0) { + u64_to_string(context->gating->chain_id, chain_str, sizeof(chain_str)); + PRINTF("[GATING] - ChainID: %s\n", chain_str); + } + if (allzeroes((const void *) context->gating->selector, SELECTOR_SIZE) == 0) { + PRINTF("[GATING] - Selector: %.*h\n", SELECTOR_SIZE, context->gating->selector); + } + PRINTF("[GATING] - Intro Msg: %s\n", context->gating->intro_msg); + PRINTF("[GATING] - Tiny URL: %s\n", context->gating->tiny_url); +} + +/** + * @brief Parse the received TLV. + * + * @param[in] data the tlv data + * @param[in] context Gating context + * @return APDU Response code + */ +static bool handle_gating_tlv(const s_tlv_data *data, s_gating_ctx *context) { + uint16_t sw = SWO_PARAMETER_ERROR_NO_INFO; + + switch (data->tag) { + case TAG_STRUCTURE_TYPE: + sw = parse_struct_type(data, context); + break; + case TAG_STRUCTURE_VERSION: + sw = parse_struct_version(data, context); + break; + case TAG_CHAIN_ID: + sw = parse_chain_id(data, context); + break; + case TAG_ADDRESS: + sw = parse_address(data, context); + break; + case TAG_SELECTOR: + sw = parse_selector(data, context); + break; + case TAG_INTRO_MSG: + sw = parse_intro_msg(data, context); + break; + case TAG_TINY_URL: + sw = parse_tiny_url(data, context); + break; + case TAG_DER_SIGNATURE: + sw = parse_signature(data, context); + break; + default: + PRINTF(TLV_TAG_ERROR_MSG, data->tag); + sw = SWO_SUCCESS; + break; + } + if ((sw == SWO_SUCCESS) && (data->tag != TAG_DER_SIGNATURE)) { + hash_nbytes(data->raw, data->raw_size, (cx_hash_t *) &context->hash_ctx); + } + return (sw == SWO_SUCCESS); +} + +/** + * @brief Parse the TLV payload containing the TX Gating parameters. + * + * @param[in] payload buffer received + * @param[in] size of the buffer + * @return whether the TLV payload was handled successfully or not + */ +static bool handle_tlv_payload(const uint8_t *payload, uint16_t size) { + bool parsing_ret; + s_gating_ctx ctx = {0}; + + if (mem_buffer_allocate((void **) &GATING, sizeof(gating_t)) == false) { + PRINTF("Error: Not enough memory!\n"); + return false; + } + ctx.gating = GATING; + + // Reset the structures + explicit_bzero(GATING, sizeof(gating_t)); + // Initialize the hash context + cx_sha256_init(&ctx.hash_ctx); + + parsing_ret = tlv_parse(payload, size, (f_tlv_data_handler) &handle_gating_tlv, &ctx); + if (!parsing_ret || !verify_fields(&ctx) || !verify_signature(&ctx)) { + explicit_bzero(GATING, sizeof(gating_t)); + explicit_bzero(&ctx, sizeof(s_gating_ctx)); + return false; + } + print_gating_info(&ctx); + return true; +} + +/** + * @brief Handle Gating APDU. + * + * @param[in] p1 APDU parameter 1 (indicates Data payload or Opt-In request) + * @param[in] p2 APDU parameter 2 (indicates if the payload is the first chunk) + * @param[in] data buffer received + * @param[in] length of the buffer + * @return APDU Response code + */ +uint16_t handle_gating(uint8_t p1, uint8_t p2, const uint8_t *data, uint8_t length) { + uint16_t sw = SWO_PARAMETER_ERROR_NO_INFO; + + switch (p2) { + case 0x00: + if (!tlv_from_apdu(p1 == P1_FIRST_CHUNK, length, data, &handle_tlv_payload)) { + sw = SWO_INCORRECT_DATA; + } else { + sw = SWO_SUCCESS; + } + break; + default: + PRINTF("Error: Unexpected P1 (%u)!\n", p1); + sw = SWO_WRONG_P1_P2; + break; + } + return sw; +} + +/** + * @brief Clear the Gating parameters. + * + */ +void clear_gating(void) { + mem_buffer_cleanup((void **) &GATING); +} + +/** + * @brief Check the FROM_ADDRESS vs Gating payload. + * + * @param[in] tx_address Transaction address + * @return whether it was successful + */ +static bool check_gating_from_address(uint8_t *tx_address) { + uint8_t *address = NULL; + + // Get the implementation address for the received descriptor address + address = (uint8_t *) get_implem_contract(&GATING->chain_id, + (const uint8_t *) GATING->addr, + (const uint8_t *) GATING->selector); + if (address == NULL) { + // Implem addr is NULL, checking with Descriptor address + address = (uint8_t *) GATING->addr; + } + + if (memcmp(address, tx_address, ADDRESS_LENGTH) != 0) { + PRINTF("[GATING] FROM addr mismatch: %.*h != %.*h\n", + ADDRESS_LENGTH, + address, + ADDRESS_LENGTH, + tx_address); + return false; + } + return true; +} + +/** + * @brief Check the CHAIN_ID vs Gating payload. + * + * @return whether it was successful + */ +static bool check_gating_chain_id(void) { + uint64_t chain_id = get_tx_chain_id(); + // Check Chain_ID in case of a standard transaction (No EIP191, No EIP712) + if ((appState == APP_STATE_SIGNING_TX) && (GATING->chain_id != chain_id)) { + PRINTF("[GATING] Chain_ID mismatch: %u != %u\n", GATING->chain_id, chain_id); + return false; + } + return true; +} + +/** + * @brief Check the SELECTOR vs Gating payload. + * + * @return whether it was successful + */ +static bool check_gating_selector(void) { + // Check if the descriptor is set + if (allzeroes((const void *) GATING->selector, SELECTOR_SIZE)) { + return true; + } + if (memcmp(GATING->selector, txContext.selector_bytes, SELECTOR_SIZE) != 0) { + PRINTF("[GATING] SELECTOR mismatch: %.*h != %.*h\n", + SELECTOR_SIZE, + GATING->selector, + SELECTOR_SIZE, + txContext.selector_bytes); + return false; + } + return true; +} + +/** + * @brief Check the TX vs Gating parameters (CHAIN_ID, TX_HASH). + * + * @param[in] tx_address Transaction address + * @return whether it was successful + */ +static bool check_tx_gating_params(uint8_t *tx_address) { + if (check_gating_chain_id() == false) { + return false; + } + if (check_gating_from_address(tx_address) == false) { + return false; + } + if (check_gating_selector() == false) { + return false; + } + return true; +} + +/** + * @brief Configure the warning prelude set for the NBGL review flows. + * + */ +static void set_gating_ui_screen(void) { + explicit_bzero(&prelude_details, sizeof(prelude_details)); + explicit_bzero(&generic_details, sizeof(generic_details)); + + generic_details.title = "Discover safer signing"; +#ifdef SCREEN_SIZE_WALLET + generic_details.type = QRCODE_WARNING; + generic_details.qrCode.url = GATING->tiny_url; + generic_details.qrCode.text1 = GATING->tiny_url; + generic_details.qrCode.text2 = "Discover a safer way to sign your transactions"; + generic_details.qrCode.centered = true; +#endif + + prelude_details.icon = &ICON_LEDGER; +#ifdef SCREEN_SIZE_WALLET + prelude_details.title = "There is a safer\nway to sign"; + prelude_details.description = GATING->intro_msg; +#else + prelude_details.title = "\bA safer way exists:"; + prelude_details.description = GATING->tiny_url; +#endif + prelude_details.buttonText = "Learn more"; + prelude_details.footerText = "Continue to blind signing"; + prelude_details.details = &generic_details; + + warning.prelude = &prelude_details; +} + +/** + * @brief Configure the warning prelude set for the NBGL review flows. + * + * @param[in] tx_address Transaction address + * @return whether the descriptor corresponds to the current transaction + */ +bool set_gating_warning(uint8_t *tx_address) { + uint8_t counter = 0; + + // Check if the descriptor is set + if ((GATING == NULL) || (allzeroes((const void *) GATING->addr, ADDRESS_LENGTH))) { + PRINTF("[GATING] Descriptor not received\n"); + return true; + } + + // Gated signing received => Verify parameters of the Transaction + if (!check_tx_gating_params(tx_address)) { + PRINTF("[GATING] Parameters mismatch\n"); + return false; + } + + // Check the counter + counter = N_storage.gating_counter + 1; + PRINTF("[GATING] Counter: %d/%d\n", counter, GATED_SIGNING_MAX_COUNT); + nvm_write((void *) &N_storage.gating_counter, (void *) &counter, sizeof(counter)); + if (((counter - 1) % GATED_SIGNING_MAX_COUNT) != 0) { + PRINTF("[GATING] Skip gating screen\n"); + return true; + } + + // Gated signing valid => Adapt the UI screens + set_gating_ui_screen(); + return true; +} + +#endif // HAVE_GATING_SUPPORT diff --git a/src/features/provide_gating/cmd_get_gating.h b/src/features/provide_gating/cmd_get_gating.h new file mode 100644 index 0000000000..9465fd6369 --- /dev/null +++ b/src/features/provide_gating/cmd_get_gating.h @@ -0,0 +1,31 @@ +/******************************************************************************* + * Ledger Ethereum App + * (c) 2025 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ********************************************************************************/ + +#pragma once + +#ifdef HAVE_GATING_SUPPORT + +#include +#include + +uint16_t handle_gating(uint8_t p1, uint8_t p2, const uint8_t *data, uint8_t length); + +void clear_gating(void); +bool set_gating_warning(uint8_t *tx_address); + +#endif // HAVE_GATING_SUPPORT diff --git a/src/features/signMessageEIP712_common/common_712.c b/src/features/signMessageEIP712_common/common_712.c index 9567a888a8..388738bfc6 100644 --- a/src/features/signMessageEIP712_common/common_712.c +++ b/src/features/signMessageEIP712_common/common_712.c @@ -99,5 +99,8 @@ void ui_712_start(e_eip712_filtering_mode filtering) { if (filtering == EIP712_FILTERING_BASIC) { // If the user has requested a filtered view, we will not show the warning warning.predefinedSet |= SET_BIT(BLIND_SIGNING_WARN); +#ifdef HAVE_GATING_SUPPORT + warning.predefinedSet |= SET_BIT(GATED_SIGNING_WARN); +#endif } } diff --git a/src/features/signTx/ethUstream.c b/src/features/signTx/ethUstream.c index 37a5fce44f..171c2db07b 100644 --- a/src/features/signTx/ethUstream.c +++ b/src/features/signTx/ethUstream.c @@ -315,6 +315,13 @@ static bool processData(txContext_t *context) { if (copySize == 1 && *context->workBuffer == 0x00) { context->content->dataPresent = false; } + + if ((context->currentFieldPos == 0) && (copySize >= 4)) { + // Consider the 4 1st bytes are the selector + // Store it to be able to check it later against Gating + memcpy(context->selector_bytes, context->workBuffer, SELECTOR_LENGTH); + } + if (context->store_calldata) { if (context->currentFieldPos == 0) { if (copySize < 4) { diff --git a/src/features/signTx/ethUstream.h b/src/features/signTx/ethUstream.h index 70d0b7bcd8..8d40f5e7ab 100644 --- a/src/features/signTx/ethUstream.h +++ b/src/features/signTx/ethUstream.h @@ -25,6 +25,8 @@ #include "common_utils.h" #include "tx_content.h" +#define SELECTOR_LENGTH 4 + typedef enum customStatus_e { CUSTOM_NOT_HANDLED, CUSTOM_HANDLED, @@ -141,6 +143,7 @@ typedef struct txContext_t { bool store_calldata; uint8_t batch_nb_tx; uint8_t current_batch_size; + uint8_t selector_bytes[SELECTOR_LENGTH]; } txContext_t; bool init_tx(txContext_t *context, cx_sha3_t *sha3, txContent_t *content, bool store_calldata); diff --git a/src/features/signTx/feature_signTx.h b/src/features/signTx/feature_signTx.h index bdbe095f8e..0c779ce4ce 100644 --- a/src/features/signTx/feature_signTx.h +++ b/src/features/signTx/feature_signTx.h @@ -22,8 +22,7 @@ extern cx_sha3_t *g_tx_hash_ctx; customStatus_e customProcessor(txContext_t *context); uint16_t finalize_parsing(const txContext_t *context); -void ux_approve_tx(bool fromPlugin); -void start_signature_flow(void); +uint16_t ux_approve_tx(bool fromPlugin); uint16_t handle_parsing_status(parserStatus_e status); diff --git a/src/features/signTx/logic_signTx.c b/src/features/signTx/logic_signTx.c index 234f0437eb..5d57bb15c4 100644 --- a/src/features/signTx/logic_signTx.c +++ b/src/features/signTx/logic_signTx.c @@ -499,14 +499,13 @@ __attribute__((noinline)) static uint16_t finalize_parsing_helper(const txContex return error; } -void start_signature_flow(void) { +static uint16_t start_signature_flow(void) { if (pluginType == PLUGIN_TYPE_NONE) { - ux_approve_tx(false); - } else { - dataContext.tokenContext.pluginUiState = PLUGIN_UI_OUTSIDE; - dataContext.tokenContext.pluginUiCurrentItem = 0; - ux_approve_tx(true); + return ux_approve_tx(false); } + dataContext.tokenContext.pluginUiState = PLUGIN_UI_OUTSIDE; + dataContext.tokenContext.pluginUiCurrentItem = 0; + return ux_approve_tx(true); } uint16_t finalize_parsing(const txContext_t *context) { @@ -520,7 +519,9 @@ uint16_t finalize_parsing(const txContext_t *context) { if ((get_current_calldata() == NULL) || (calldata_get_selector(get_current_calldata()) == NULL)) { PRINTF("Asked to store calldata but none was provided!\n"); - return SWO_INCORRECT_DATA; + sw = SWO_INCORRECT_DATA; + } else { + sw = SWO_SUCCESS; } } else { // If called from swap, the user has already validated a standard transaction @@ -537,9 +538,10 @@ uint16_t finalize_parsing(const txContext_t *context) { os_sched_exit(0); } io_seproxyhal_touch_tx_ok(); + sw = SWO_SUCCESS; } else { - start_signature_flow(); + sw = start_signature_flow(); } } - return SWO_SUCCESS; + return sw; } diff --git a/src/ledger_pki.c b/src/ledger_pki.c index 3014da9bb2..c369abbc3d 100644 --- a/src/ledger_pki.c +++ b/src/ledger_pki.c @@ -15,6 +15,7 @@ : x == CERTIFICATE_PUBLIC_KEY_USAGE_CALLDATA ? "CALLDATA" \ : x == CERTIFICATE_PUBLIC_KEY_USAGE_NETWORK ? "NETWORK" \ : x == CERTIFICATE_PUBLIC_KEY_USAGE_LES_MULTISIG ? "SAFE" \ + : x == CERTIFICATE_PUBLIC_KEY_USAGE_GATED_SIGNING ? "GATED_SIGNING" \ : "Unknown") int check_signature_with_pubkey(const char *tag, diff --git a/src/main.c b/src/main.c index 337ef9e90e..8a3707c909 100644 --- a/src/main.c +++ b/src/main.c @@ -42,6 +42,7 @@ #include "cmd_tx_info.h" #include "cmd_field.h" #include "cmd_get_tx_simulation.h" +#include "cmd_get_gating.h" #include "cmd_proxy_info.h" #include "commands_7702.h" #include "sign_message.h" @@ -273,6 +274,12 @@ static uint16_t handleApdu(command_t *cmd, uint32_t *flags, uint32_t *tx) { break; #endif +#ifdef HAVE_GATING_SUPPORT + case INS_PROVIDE_GATING: + sw = handle_gating(cmd->p1, cmd->p2, cmd->data, cmd->lc); + break; +#endif + default: sw = SWO_INVALID_INS; break; diff --git a/src/nbgl/ui_approve_tx.c b/src/nbgl/ui_approve_tx.c index 25b0904ff9..d3ef18f079 100644 --- a/src/nbgl/ui_approve_tx.c +++ b/src/nbgl/ui_approve_tx.c @@ -11,6 +11,7 @@ #include "network_icons.h" #include "network.h" #include "cmd_get_tx_simulation.h" +#include "cmd_get_gating.h" #include "utils.h" #include "mem_utils.h" #include "ui_utils.h" @@ -40,6 +41,12 @@ static void _cleanup(void) { ui_all_cleanup(); enum_value_cleanup(); proxy_cleanup(); +#ifdef HAVE_TRANSACTION_CHECKS + clear_tx_simulation(); +#endif +#ifdef HAVE_GATING_SUPPORT + clear_gating(); +#endif } // Review callback function to handle user confirmation or cancellation @@ -52,9 +59,6 @@ static void reviewChoice(bool confirm) { nbgl_useCaseReviewStatus(STATUS_TYPE_TRANSACTION_REJECTED, ui_idle); } _cleanup(); -#ifdef HAVE_TRANSACTION_CHECKS - clear_tx_simulation(); -#endif } /** @@ -342,12 +346,13 @@ static bool ux_init(bool fromPlugin, uint8_t title_len, uint8_t finish_len) { } /** - * Display the transaction review screen. + * Init the strings for the transaction review screen. * * @param[in] fromPlugin If true, the data is coming from a plugin, otherwise it is a standard * transaction + * @return status code indicating success or failure */ -void ux_approve_tx(bool fromPlugin) { +static uint16_t ux_init_strings(bool fromPlugin) { char op_name[sizeof(strings.common.fullAmount)]; const char *title_prefix = "Review transaction"; const char *tx_check_str = NULL; @@ -355,14 +360,6 @@ void ux_approve_tx(bool fromPlugin) { uint8_t title_len = 1; // Initialize lengths to 1 for '\0' character uint8_t finish_len = 1; // Initialize lengths to 1 for '\0' character - // Initialize the warning structure - explicit_bzero(&warning, sizeof(nbgl_warning_t)); - if (tmpContent.txContent.dataPresent) { - warning.predefinedSet |= SET_BIT(BLIND_SIGNING_WARN); - } -#ifdef HAVE_TRANSACTION_CHECKS - set_tx_simulation_warning(); -#endif tx_check_str = ui_tx_simulation_finish_str(); // Compute the title and finish message lengths @@ -371,7 +368,7 @@ void ux_approve_tx(bool fromPlugin) { finish_len += 12; // strlen(" transaction"); if (fromPlugin) { if (!plugin_ui_get_id()) { - return; + return SWO_INCORRECT_DATA; } get_lowercase_operation(op_name, sizeof(op_name)); @@ -394,7 +391,7 @@ void ux_approve_tx(bool fromPlugin) { // Initialize the buffers if (!ux_init(fromPlugin, title_len, finish_len)) { // Initialization failed, cleanup and return - return; + return SWO_INSUFFICIENT_MEMORY; } // Prepare the title and finish messages @@ -404,7 +401,7 @@ void ux_approve_tx(bool fromPlugin) { // Prepare the suffix if (mem_buffer_allocate((void **) &suffix_str, title_len) == false) { // Memory allocation failed, cleanup and return - return; + return SWO_INSUFFICIENT_MEMORY; } snprintf(suffix_str, title_len, @@ -424,24 +421,47 @@ void ux_approve_tx(bool fromPlugin) { strlcat(g_finishMsg, "?", finish_len); #endif - // Start the review process - if (warning.predefinedSet == 0) { - nbgl_useCaseReview(TYPE_TRANSACTION, - g_pairsList, - get_tx_icon(fromPlugin), - g_titleMsg, - NULL, - g_finishMsg, - reviewChoice); - } else { - nbgl_useCaseAdvancedReview(TYPE_TRANSACTION, - g_pairsList, - get_tx_icon(fromPlugin), - g_titleMsg, - NULL, - g_finishMsg, - NULL, - &warning, - reviewChoice); + return SWO_SUCCESS; +} + +/** + * Display the transaction review screen. + * + * @param[in] fromPlugin If true, the data is coming from a plugin, otherwise it is a standard + * transaction + * @return status code indicating success or failure + */ +uint16_t ux_approve_tx(bool fromPlugin) { + uint16_t sw = SWO_PARAMETER_ERROR_NO_INFO; + // Initialize the warning structure + explicit_bzero(&warning, sizeof(nbgl_warning_t)); + if (tmpContent.txContent.dataPresent) { + warning.predefinedSet |= SET_BIT(BLIND_SIGNING_WARN); +#ifdef HAVE_GATING_SUPPORT + warning.predefinedSet |= SET_BIT(GATED_SIGNING_WARN); + if (set_gating_warning(tmpContent.txContent.destination) == false) { + return SWO_INCORRECT_DATA; + } +#endif } +#ifdef HAVE_TRANSACTION_CHECKS + set_tx_simulation_warning(); +#endif + + // Initialize the strings + sw = ux_init_strings(fromPlugin); + if (sw != SWO_SUCCESS) { + return sw; + } + + nbgl_useCaseAdvancedReview(TYPE_TRANSACTION, + g_pairsList, + get_tx_icon(fromPlugin), + g_titleMsg, + NULL, + g_finishMsg, + NULL, + &warning, + reviewChoice); + return SWO_SUCCESS; } diff --git a/src/nbgl/ui_nbgl.h b/src/nbgl/ui_nbgl.h index 5789c22e4e..6cb778f7c1 100644 --- a/src/nbgl/ui_nbgl.h +++ b/src/nbgl/ui_nbgl.h @@ -7,7 +7,13 @@ #ifdef SCREEN_SIZE_WALLET #define ICON_APP_WARNING LARGE_WARNING_ICON #define ICON_APP_REVIEW LARGE_REVIEW_ICON +#if defined(TARGET_APEX) +#define ICON_LEDGER C_ledger_48px #else +#define ICON_LEDGER C_ledger_64px +#endif +#else +#define ICON_LEDGER C_ledger_14px #define ICON_APP_WARNING WARNING_ICON #define ICON_APP_REVIEW REVIEW_ICON #endif diff --git a/src/plugins.c b/src/plugins.c index b69b443df1..e12de453c1 100644 --- a/src/plugins.c +++ b/src/plugins.c @@ -14,7 +14,6 @@ bool plugin_ui_get_id(void) { if (!eth_plugin_call(ETH_PLUGIN_QUERY_CONTRACT_ID, (void *) &pluginQueryContractID)) { PRINTF("Plugin query contract ID call failed\n"); reset_app_context(); - io_send_sw(SWO_INCORRECT_DATA); return false; } return true; diff --git a/src/shared_context.h b/src/shared_context.h index a540c675d5..7e2ad41813 100644 --- a/src/shared_context.h +++ b/src/shared_context.h @@ -7,8 +7,6 @@ #include "main_std_app.h" #include "eth_plugin_interface.h" -#define SELECTOR_LENGTH 4 - #define PLUGIN_ID_LENGTH 30 #define N_storage (*(volatile internalStorage_t *) PIC(&N_storage_real)) @@ -24,6 +22,9 @@ typedef struct internalStorage_t { bool tx_check_enable; // hidden setting (not shown in the UI) bool tx_check_opt_in; +#endif +#ifdef HAVE_GATING_SUPPORT + uint8_t gating_counter; #endif bool eip7702_enable; bool displayHash; diff --git a/tests/fuzzing/README.md b/tests/fuzzing/README.md index 89db1e283b..9e4d93bab4 100644 --- a/tests/fuzzing/README.md +++ b/tests/fuzzing/README.md @@ -11,7 +11,6 @@ any kind of access violation, the fuzzing process is stopped, a report regarding and the input that triggered the bug is written to disk under the name `crash-*`. The vulnerable input file created can be passed as an argument to the fuzzer to triage the issue. - ## Manual usage based on Ledger container ### Preparation @@ -48,7 +47,8 @@ cmake --build build ./build/fuzzer -max_len=8192 ``` -If you want to do a fuzzing campain on more than one core and compute the coverage results, you can use the `local_run.sh` script within the container (it'll only run the address and UB sanitizers). +If you want to do a fuzzing campaign on more than one core and compute the coverage results, +you can use the `local_run.sh` script within the container (it'll only run the address and UB sanitizers). ## Full usage based on `clusterfuzzlite` container diff --git a/tests/ragger/eip712_input_files/00-simple_mail-filter.json b/tests/ragger/eip712_input_files/00-simple_mail-filter.json index bb4957037d..4cc85e90b0 100644 --- a/tests/ragger/eip712_input_files/00-simple_mail-filter.json +++ b/tests/ragger/eip712_input_files/00-simple_mail-filter.json @@ -10,4 +10,4 @@ "name": "To" } } -} \ No newline at end of file +} diff --git a/tests/ragger/eip712_input_files/08-opensea-filter.json b/tests/ragger/eip712_input_files/08-opensea-filter.json index e70eb802a8..6692edffde 100644 --- a/tests/ragger/eip712_input_files/08-opensea-filter.json +++ b/tests/ragger/eip712_input_files/08-opensea-filter.json @@ -18,4 +18,4 @@ "name": "Expiration Time" } } -} \ No newline at end of file +} diff --git a/tests/ragger/eip712_input_files/11-complex_structs-filter.json b/tests/ragger/eip712_input_files/11-complex_structs-filter.json index 7623043124..11030d3e8d 100644 --- a/tests/ragger/eip712_input_files/11-complex_structs-filter.json +++ b/tests/ragger/eip712_input_files/11-complex_structs-filter.json @@ -18,4 +18,4 @@ "name": "Attachment" } } -} \ No newline at end of file +} diff --git a/tests/ragger/eip712_input_files/12-sign_in-filter.json b/tests/ragger/eip712_input_files/12-sign_in-filter.json index 46aa82aec8..9dab8852b5 100644 --- a/tests/ragger/eip712_input_files/12-sign_in-filter.json +++ b/tests/ragger/eip712_input_files/12-sign_in-filter.json @@ -10,4 +10,4 @@ "name": "Identifier" } } -} \ No newline at end of file +} diff --git a/tests/ragger/snapshots/apex_p/test_blind_sign/warning/00000.png b/tests/ragger/snapshots/apex_p/test_blind_sign/warning/00000.png index eb1e09fcd6..671038a299 100644 Binary files a/tests/ragger/snapshots/apex_p/test_blind_sign/warning/00000.png and b/tests/ragger/snapshots/apex_p/test_blind_sign/warning/00000.png differ diff --git a/tests/ragger/snapshots/apex_p/test_blind_sign_nonce_nonzero/warning/00000.png b/tests/ragger/snapshots/apex_p/test_blind_sign_nonce_nonzero/warning/00000.png index eb1e09fcd6..671038a299 100644 Binary files a/tests/ragger/snapshots/apex_p/test_blind_sign_nonce_nonzero/warning/00000.png and b/tests/ragger/snapshots/apex_p/test_blind_sign_nonce_nonzero/warning/00000.png differ diff --git a/tests/ragger/snapshots/apex_p/test_blind_sign_nonzero/warning/00000.png b/tests/ragger/snapshots/apex_p/test_blind_sign_nonzero/warning/00000.png index eb1e09fcd6..671038a299 100644 Binary files a/tests/ragger/snapshots/apex_p/test_blind_sign_nonzero/warning/00000.png and b/tests/ragger/snapshots/apex_p/test_blind_sign_nonzero/warning/00000.png differ diff --git a/tests/ragger/snapshots/apex_p/test_blind_sign_rejected/warning/00000.png b/tests/ragger/snapshots/apex_p/test_blind_sign_rejected/warning/00000.png index eb1e09fcd6..671038a299 100644 Binary files a/tests/ragger/snapshots/apex_p/test_blind_sign_rejected/warning/00000.png and b/tests/ragger/snapshots/apex_p/test_blind_sign_rejected/warning/00000.png differ diff --git a/tests/ragger/snapshots/apex_p/test_gating_blind_signing/00000.png b/tests/ragger/snapshots/apex_p/test_gating_blind_signing/00000.png new file mode 100644 index 0000000000..0418b7642f Binary files /dev/null and b/tests/ragger/snapshots/apex_p/test_gating_blind_signing/00000.png differ diff --git a/tests/ragger/snapshots/apex_p/test_gating_blind_signing/00001.png b/tests/ragger/snapshots/apex_p/test_gating_blind_signing/00001.png new file mode 100644 index 0000000000..b328a0c032 Binary files /dev/null and b/tests/ragger/snapshots/apex_p/test_gating_blind_signing/00001.png differ diff --git a/tests/ragger/snapshots/apex_p/test_gating_blind_signing/00002.png b/tests/ragger/snapshots/apex_p/test_gating_blind_signing/00002.png new file mode 100644 index 0000000000..6c55201368 Binary files /dev/null and b/tests/ragger/snapshots/apex_p/test_gating_blind_signing/00002.png differ diff --git a/tests/ragger/snapshots/apex_p/test_gating_blind_signing/00003.png b/tests/ragger/snapshots/apex_p/test_gating_blind_signing/00003.png new file mode 100644 index 0000000000..bf89d401f9 Binary files /dev/null and b/tests/ragger/snapshots/apex_p/test_gating_blind_signing/00003.png differ diff --git a/tests/ragger/snapshots/apex_p/test_gating_blind_signing/00004.png b/tests/ragger/snapshots/apex_p/test_gating_blind_signing/00004.png new file mode 100644 index 0000000000..4aba0a2bc2 Binary files /dev/null and b/tests/ragger/snapshots/apex_p/test_gating_blind_signing/00004.png differ diff --git a/tests/ragger/snapshots/apex_p/test_gating_blind_signing/00005.png b/tests/ragger/snapshots/apex_p/test_gating_blind_signing/00005.png new file mode 100644 index 0000000000..0a471a44c5 Binary files /dev/null and b/tests/ragger/snapshots/apex_p/test_gating_blind_signing/00005.png differ diff --git a/tests/ragger/snapshots/apex_p/test_gating_blind_signing/warning/00000.png b/tests/ragger/snapshots/apex_p/test_gating_blind_signing/warning/00000.png new file mode 100644 index 0000000000..73afc5f740 Binary files /dev/null and b/tests/ragger/snapshots/apex_p/test_gating_blind_signing/warning/00000.png differ diff --git a/tests/ragger/snapshots/apex_p/test_gating_blind_signing/warning/00001.png b/tests/ragger/snapshots/apex_p/test_gating_blind_signing/warning/00001.png new file mode 100644 index 0000000000..671038a299 Binary files /dev/null and b/tests/ragger/snapshots/apex_p/test_gating_blind_signing/warning/00001.png differ diff --git a/tests/ragger/snapshots/apex_p/test_gating_blind_signing_with_proxy/00000.png b/tests/ragger/snapshots/apex_p/test_gating_blind_signing_with_proxy/00000.png new file mode 100644 index 0000000000..0418b7642f Binary files /dev/null and b/tests/ragger/snapshots/apex_p/test_gating_blind_signing_with_proxy/00000.png differ diff --git a/tests/ragger/snapshots/apex_p/test_gating_blind_signing_with_proxy/00001.png b/tests/ragger/snapshots/apex_p/test_gating_blind_signing_with_proxy/00001.png new file mode 100644 index 0000000000..2adc1edc10 Binary files /dev/null and b/tests/ragger/snapshots/apex_p/test_gating_blind_signing_with_proxy/00001.png differ diff --git a/tests/ragger/snapshots/apex_p/test_gating_blind_signing_with_proxy/00002.png b/tests/ragger/snapshots/apex_p/test_gating_blind_signing_with_proxy/00002.png new file mode 100644 index 0000000000..18dfc75662 Binary files /dev/null and b/tests/ragger/snapshots/apex_p/test_gating_blind_signing_with_proxy/00002.png differ diff --git a/tests/ragger/snapshots/apex_p/test_gating_blind_signing_with_proxy/00003.png b/tests/ragger/snapshots/apex_p/test_gating_blind_signing_with_proxy/00003.png new file mode 100644 index 0000000000..bf89d401f9 Binary files /dev/null and b/tests/ragger/snapshots/apex_p/test_gating_blind_signing_with_proxy/00003.png differ diff --git a/tests/ragger/snapshots/apex_p/test_gating_blind_signing_with_proxy/00004.png b/tests/ragger/snapshots/apex_p/test_gating_blind_signing_with_proxy/00004.png new file mode 100644 index 0000000000..4aba0a2bc2 Binary files /dev/null and b/tests/ragger/snapshots/apex_p/test_gating_blind_signing_with_proxy/00004.png differ diff --git a/tests/ragger/snapshots/apex_p/test_gating_blind_signing_with_proxy/00005.png b/tests/ragger/snapshots/apex_p/test_gating_blind_signing_with_proxy/00005.png new file mode 100644 index 0000000000..0a471a44c5 Binary files /dev/null and b/tests/ragger/snapshots/apex_p/test_gating_blind_signing_with_proxy/00005.png differ diff --git a/tests/ragger/snapshots/apex_p/test_gating_blind_signing_with_proxy/warning/00000.png b/tests/ragger/snapshots/apex_p/test_gating_blind_signing_with_proxy/warning/00000.png new file mode 100644 index 0000000000..73afc5f740 Binary files /dev/null and b/tests/ragger/snapshots/apex_p/test_gating_blind_signing_with_proxy/warning/00000.png differ diff --git a/tests/ragger/snapshots/apex_p/test_gating_blind_signing_with_proxy/warning/00001.png b/tests/ragger/snapshots/apex_p/test_gating_blind_signing_with_proxy/warning/00001.png new file mode 100644 index 0000000000..671038a299 Binary files /dev/null and b/tests/ragger/snapshots/apex_p/test_gating_blind_signing_with_proxy/warning/00001.png differ diff --git a/tests/ragger/snapshots/apex_p/test_sign_parameter_selector/00006.png b/tests/ragger/snapshots/apex_p/test_sign_parameter_selector/00006.png index eb1e09fcd6..671038a299 100644 Binary files a/tests/ragger/snapshots/apex_p/test_sign_parameter_selector/00006.png and b/tests/ragger/snapshots/apex_p/test_sign_parameter_selector/00006.png differ diff --git a/tests/ragger/snapshots/apex_p/test_tx_simulation_blind_sign_benign/warning/00000.png b/tests/ragger/snapshots/apex_p/test_tx_simulation_blind_sign_benign/warning/00000.png index eb1e09fcd6..671038a299 100644 Binary files a/tests/ragger/snapshots/apex_p/test_tx_simulation_blind_sign_benign/warning/00000.png and b/tests/ragger/snapshots/apex_p/test_tx_simulation_blind_sign_benign/warning/00000.png differ diff --git a/tests/ragger/snapshots/apex_p/test_tx_simulation_blind_sign_issue/warning/00000.png b/tests/ragger/snapshots/apex_p/test_tx_simulation_blind_sign_issue/warning/00000.png index eb1e09fcd6..671038a299 100644 Binary files a/tests/ragger/snapshots/apex_p/test_tx_simulation_blind_sign_issue/warning/00000.png and b/tests/ragger/snapshots/apex_p/test_tx_simulation_blind_sign_issue/warning/00000.png differ diff --git a/tests/ragger/snapshots/flex/test_blind_sign/warning/00000.png b/tests/ragger/snapshots/flex/test_blind_sign/warning/00000.png index a0f791bbe1..1306c15ef5 100644 Binary files a/tests/ragger/snapshots/flex/test_blind_sign/warning/00000.png and b/tests/ragger/snapshots/flex/test_blind_sign/warning/00000.png differ diff --git a/tests/ragger/snapshots/flex/test_blind_sign_nonce_nonzero/warning/00000.png b/tests/ragger/snapshots/flex/test_blind_sign_nonce_nonzero/warning/00000.png index a0f791bbe1..1306c15ef5 100644 Binary files a/tests/ragger/snapshots/flex/test_blind_sign_nonce_nonzero/warning/00000.png and b/tests/ragger/snapshots/flex/test_blind_sign_nonce_nonzero/warning/00000.png differ diff --git a/tests/ragger/snapshots/flex/test_blind_sign_nonzero/warning/00000.png b/tests/ragger/snapshots/flex/test_blind_sign_nonzero/warning/00000.png index a0f791bbe1..1306c15ef5 100644 Binary files a/tests/ragger/snapshots/flex/test_blind_sign_nonzero/warning/00000.png and b/tests/ragger/snapshots/flex/test_blind_sign_nonzero/warning/00000.png differ diff --git a/tests/ragger/snapshots/flex/test_blind_sign_rejected/warning/00000.png b/tests/ragger/snapshots/flex/test_blind_sign_rejected/warning/00000.png index a0f791bbe1..1306c15ef5 100644 Binary files a/tests/ragger/snapshots/flex/test_blind_sign_rejected/warning/00000.png and b/tests/ragger/snapshots/flex/test_blind_sign_rejected/warning/00000.png differ diff --git a/tests/ragger/snapshots/flex/test_gating_blind_signing/00000.png b/tests/ragger/snapshots/flex/test_gating_blind_signing/00000.png new file mode 100644 index 0000000000..cb9dfa48d6 Binary files /dev/null and b/tests/ragger/snapshots/flex/test_gating_blind_signing/00000.png differ diff --git a/tests/ragger/snapshots/flex/test_gating_blind_signing/00001.png b/tests/ragger/snapshots/flex/test_gating_blind_signing/00001.png new file mode 100644 index 0000000000..1d0377dcea Binary files /dev/null and b/tests/ragger/snapshots/flex/test_gating_blind_signing/00001.png differ diff --git a/tests/ragger/snapshots/flex/test_gating_blind_signing/00002.png b/tests/ragger/snapshots/flex/test_gating_blind_signing/00002.png new file mode 100644 index 0000000000..68b0d2b87f Binary files /dev/null and b/tests/ragger/snapshots/flex/test_gating_blind_signing/00002.png differ diff --git a/tests/ragger/snapshots/flex/test_gating_blind_signing/00003.png b/tests/ragger/snapshots/flex/test_gating_blind_signing/00003.png new file mode 100644 index 0000000000..0319d1b55f Binary files /dev/null and b/tests/ragger/snapshots/flex/test_gating_blind_signing/00003.png differ diff --git a/tests/ragger/snapshots/flex/test_gating_blind_signing/00004.png b/tests/ragger/snapshots/flex/test_gating_blind_signing/00004.png new file mode 100644 index 0000000000..435aa78bdb Binary files /dev/null and b/tests/ragger/snapshots/flex/test_gating_blind_signing/00004.png differ diff --git a/tests/ragger/snapshots/flex/test_gating_blind_signing/00005.png b/tests/ragger/snapshots/flex/test_gating_blind_signing/00005.png new file mode 100644 index 0000000000..78ae4c933a Binary files /dev/null and b/tests/ragger/snapshots/flex/test_gating_blind_signing/00005.png differ diff --git a/tests/ragger/snapshots/flex/test_gating_blind_signing/warning/00000.png b/tests/ragger/snapshots/flex/test_gating_blind_signing/warning/00000.png new file mode 100644 index 0000000000..3147a2c7d6 Binary files /dev/null and b/tests/ragger/snapshots/flex/test_gating_blind_signing/warning/00000.png differ diff --git a/tests/ragger/snapshots/flex/test_gating_blind_signing/warning/00001.png b/tests/ragger/snapshots/flex/test_gating_blind_signing/warning/00001.png new file mode 100644 index 0000000000..1306c15ef5 Binary files /dev/null and b/tests/ragger/snapshots/flex/test_gating_blind_signing/warning/00001.png differ diff --git a/tests/ragger/snapshots/flex/test_gating_blind_signing_with_proxy/00000.png b/tests/ragger/snapshots/flex/test_gating_blind_signing_with_proxy/00000.png new file mode 100644 index 0000000000..cb9dfa48d6 Binary files /dev/null and b/tests/ragger/snapshots/flex/test_gating_blind_signing_with_proxy/00000.png differ diff --git a/tests/ragger/snapshots/flex/test_gating_blind_signing_with_proxy/00001.png b/tests/ragger/snapshots/flex/test_gating_blind_signing_with_proxy/00001.png new file mode 100644 index 0000000000..17da5c3816 Binary files /dev/null and b/tests/ragger/snapshots/flex/test_gating_blind_signing_with_proxy/00001.png differ diff --git a/tests/ragger/snapshots/flex/test_gating_blind_signing_with_proxy/00002.png b/tests/ragger/snapshots/flex/test_gating_blind_signing_with_proxy/00002.png new file mode 100644 index 0000000000..08aa2aa80e Binary files /dev/null and b/tests/ragger/snapshots/flex/test_gating_blind_signing_with_proxy/00002.png differ diff --git a/tests/ragger/snapshots/flex/test_gating_blind_signing_with_proxy/00003.png b/tests/ragger/snapshots/flex/test_gating_blind_signing_with_proxy/00003.png new file mode 100644 index 0000000000..0319d1b55f Binary files /dev/null and b/tests/ragger/snapshots/flex/test_gating_blind_signing_with_proxy/00003.png differ diff --git a/tests/ragger/snapshots/flex/test_gating_blind_signing_with_proxy/00004.png b/tests/ragger/snapshots/flex/test_gating_blind_signing_with_proxy/00004.png new file mode 100644 index 0000000000..435aa78bdb Binary files /dev/null and b/tests/ragger/snapshots/flex/test_gating_blind_signing_with_proxy/00004.png differ diff --git a/tests/ragger/snapshots/flex/test_gating_blind_signing_with_proxy/00005.png b/tests/ragger/snapshots/flex/test_gating_blind_signing_with_proxy/00005.png new file mode 100644 index 0000000000..78ae4c933a Binary files /dev/null and b/tests/ragger/snapshots/flex/test_gating_blind_signing_with_proxy/00005.png differ diff --git a/tests/ragger/snapshots/flex/test_gating_blind_signing_with_proxy/warning/00000.png b/tests/ragger/snapshots/flex/test_gating_blind_signing_with_proxy/warning/00000.png new file mode 100644 index 0000000000..3147a2c7d6 Binary files /dev/null and b/tests/ragger/snapshots/flex/test_gating_blind_signing_with_proxy/warning/00000.png differ diff --git a/tests/ragger/snapshots/flex/test_gating_blind_signing_with_proxy/warning/00001.png b/tests/ragger/snapshots/flex/test_gating_blind_signing_with_proxy/warning/00001.png new file mode 100644 index 0000000000..1306c15ef5 Binary files /dev/null and b/tests/ragger/snapshots/flex/test_gating_blind_signing_with_proxy/warning/00001.png differ diff --git a/tests/ragger/snapshots/flex/test_sign_parameter_selector/00006.png b/tests/ragger/snapshots/flex/test_sign_parameter_selector/00006.png index a0f791bbe1..1306c15ef5 100644 Binary files a/tests/ragger/snapshots/flex/test_sign_parameter_selector/00006.png and b/tests/ragger/snapshots/flex/test_sign_parameter_selector/00006.png differ diff --git a/tests/ragger/snapshots/flex/test_tx_simulation_blind_sign_benign/warning/00000.png b/tests/ragger/snapshots/flex/test_tx_simulation_blind_sign_benign/warning/00000.png index a0f791bbe1..1306c15ef5 100644 Binary files a/tests/ragger/snapshots/flex/test_tx_simulation_blind_sign_benign/warning/00000.png and b/tests/ragger/snapshots/flex/test_tx_simulation_blind_sign_benign/warning/00000.png differ diff --git a/tests/ragger/snapshots/flex/test_tx_simulation_blind_sign_issue/warning/00000.png b/tests/ragger/snapshots/flex/test_tx_simulation_blind_sign_issue/warning/00000.png index a0f791bbe1..1306c15ef5 100644 Binary files a/tests/ragger/snapshots/flex/test_tx_simulation_blind_sign_issue/warning/00000.png and b/tests/ragger/snapshots/flex/test_tx_simulation_blind_sign_issue/warning/00000.png differ diff --git a/tests/ragger/snapshots/nanosp/test_gating_blind_signing/00000.png b/tests/ragger/snapshots/nanosp/test_gating_blind_signing/00000.png new file mode 100644 index 0000000000..ebda01a292 Binary files /dev/null and b/tests/ragger/snapshots/nanosp/test_gating_blind_signing/00000.png differ diff --git a/tests/ragger/snapshots/nanosp/test_gating_blind_signing/00001.png b/tests/ragger/snapshots/nanosp/test_gating_blind_signing/00001.png new file mode 100644 index 0000000000..9206a0fbc7 Binary files /dev/null and b/tests/ragger/snapshots/nanosp/test_gating_blind_signing/00001.png differ diff --git a/tests/ragger/snapshots/nanosp/test_gating_blind_signing/00002.png b/tests/ragger/snapshots/nanosp/test_gating_blind_signing/00002.png new file mode 100644 index 0000000000..d0dd5c6545 Binary files /dev/null and b/tests/ragger/snapshots/nanosp/test_gating_blind_signing/00002.png differ diff --git a/tests/ragger/snapshots/nanosp/test_gating_blind_signing/00003.png b/tests/ragger/snapshots/nanosp/test_gating_blind_signing/00003.png new file mode 100644 index 0000000000..ac0dde80f3 Binary files /dev/null and b/tests/ragger/snapshots/nanosp/test_gating_blind_signing/00003.png differ diff --git a/tests/ragger/snapshots/nanosp/test_gating_blind_signing/00004.png b/tests/ragger/snapshots/nanosp/test_gating_blind_signing/00004.png new file mode 100644 index 0000000000..061d612150 Binary files /dev/null and b/tests/ragger/snapshots/nanosp/test_gating_blind_signing/00004.png differ diff --git a/tests/ragger/snapshots/nanosp/test_gating_blind_signing/00005.png b/tests/ragger/snapshots/nanosp/test_gating_blind_signing/00005.png new file mode 100644 index 0000000000..6a775522b7 Binary files /dev/null and b/tests/ragger/snapshots/nanosp/test_gating_blind_signing/00005.png differ diff --git a/tests/ragger/snapshots/nanosp/test_gating_blind_signing/00006.png b/tests/ragger/snapshots/nanosp/test_gating_blind_signing/00006.png new file mode 100644 index 0000000000..845abc816a Binary files /dev/null and b/tests/ragger/snapshots/nanosp/test_gating_blind_signing/00006.png differ diff --git a/tests/ragger/snapshots/nanosp/test_gating_blind_signing/00007.png b/tests/ragger/snapshots/nanosp/test_gating_blind_signing/00007.png new file mode 100644 index 0000000000..3a161194e8 Binary files /dev/null and b/tests/ragger/snapshots/nanosp/test_gating_blind_signing/00007.png differ diff --git a/tests/ragger/snapshots/nanosp/test_gating_blind_signing/warning/00000.png b/tests/ragger/snapshots/nanosp/test_gating_blind_signing/warning/00000.png new file mode 100644 index 0000000000..83ea37f2d6 Binary files /dev/null and b/tests/ragger/snapshots/nanosp/test_gating_blind_signing/warning/00000.png differ diff --git a/tests/ragger/snapshots/nanosp/test_gating_blind_signing/warning/00001.png b/tests/ragger/snapshots/nanosp/test_gating_blind_signing/warning/00001.png new file mode 100644 index 0000000000..4943ad331d Binary files /dev/null and b/tests/ragger/snapshots/nanosp/test_gating_blind_signing/warning/00001.png differ diff --git a/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/00000.png b/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/00000.png new file mode 100644 index 0000000000..ebda01a292 Binary files /dev/null and b/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/00000.png differ diff --git a/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/00001.png b/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/00001.png new file mode 100644 index 0000000000..9206a0fbc7 Binary files /dev/null and b/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/00001.png differ diff --git a/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/00002.png b/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/00002.png new file mode 100644 index 0000000000..e2965f5d7b Binary files /dev/null and b/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/00002.png differ diff --git a/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/00003.png b/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/00003.png new file mode 100644 index 0000000000..ac0dde80f3 Binary files /dev/null and b/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/00003.png differ diff --git a/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/00004.png b/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/00004.png new file mode 100644 index 0000000000..e5009fe878 Binary files /dev/null and b/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/00004.png differ diff --git a/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/00005.png b/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/00005.png new file mode 100644 index 0000000000..c34edd729b Binary files /dev/null and b/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/00005.png differ diff --git a/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/00006.png b/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/00006.png new file mode 100644 index 0000000000..845abc816a Binary files /dev/null and b/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/00006.png differ diff --git a/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/00007.png b/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/00007.png new file mode 100644 index 0000000000..3a161194e8 Binary files /dev/null and b/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/00007.png differ diff --git a/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/warning/00000.png b/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/warning/00000.png new file mode 100644 index 0000000000..83ea37f2d6 Binary files /dev/null and b/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/warning/00000.png differ diff --git a/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/warning/00001.png b/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/warning/00001.png new file mode 100644 index 0000000000..4943ad331d Binary files /dev/null and b/tests/ragger/snapshots/nanosp/test_gating_blind_signing_with_proxy/warning/00001.png differ diff --git a/tests/ragger/snapshots/nanox/test_gating_blind_signing/00000.png b/tests/ragger/snapshots/nanox/test_gating_blind_signing/00000.png new file mode 100644 index 0000000000..ebda01a292 Binary files /dev/null and b/tests/ragger/snapshots/nanox/test_gating_blind_signing/00000.png differ diff --git a/tests/ragger/snapshots/nanox/test_gating_blind_signing/00001.png b/tests/ragger/snapshots/nanox/test_gating_blind_signing/00001.png new file mode 100644 index 0000000000..9206a0fbc7 Binary files /dev/null and b/tests/ragger/snapshots/nanox/test_gating_blind_signing/00001.png differ diff --git a/tests/ragger/snapshots/nanox/test_gating_blind_signing/00002.png b/tests/ragger/snapshots/nanox/test_gating_blind_signing/00002.png new file mode 100644 index 0000000000..d0dd5c6545 Binary files /dev/null and b/tests/ragger/snapshots/nanox/test_gating_blind_signing/00002.png differ diff --git a/tests/ragger/snapshots/nanox/test_gating_blind_signing/00003.png b/tests/ragger/snapshots/nanox/test_gating_blind_signing/00003.png new file mode 100644 index 0000000000..ac0dde80f3 Binary files /dev/null and b/tests/ragger/snapshots/nanox/test_gating_blind_signing/00003.png differ diff --git a/tests/ragger/snapshots/nanox/test_gating_blind_signing/00004.png b/tests/ragger/snapshots/nanox/test_gating_blind_signing/00004.png new file mode 100644 index 0000000000..061d612150 Binary files /dev/null and b/tests/ragger/snapshots/nanox/test_gating_blind_signing/00004.png differ diff --git a/tests/ragger/snapshots/nanox/test_gating_blind_signing/00005.png b/tests/ragger/snapshots/nanox/test_gating_blind_signing/00005.png new file mode 100644 index 0000000000..6a775522b7 Binary files /dev/null and b/tests/ragger/snapshots/nanox/test_gating_blind_signing/00005.png differ diff --git a/tests/ragger/snapshots/nanox/test_gating_blind_signing/00006.png b/tests/ragger/snapshots/nanox/test_gating_blind_signing/00006.png new file mode 100644 index 0000000000..845abc816a Binary files /dev/null and b/tests/ragger/snapshots/nanox/test_gating_blind_signing/00006.png differ diff --git a/tests/ragger/snapshots/nanox/test_gating_blind_signing/00007.png b/tests/ragger/snapshots/nanox/test_gating_blind_signing/00007.png new file mode 100644 index 0000000000..3a161194e8 Binary files /dev/null and b/tests/ragger/snapshots/nanox/test_gating_blind_signing/00007.png differ diff --git a/tests/ragger/snapshots/nanox/test_gating_blind_signing/warning/00000.png b/tests/ragger/snapshots/nanox/test_gating_blind_signing/warning/00000.png new file mode 100644 index 0000000000..83ea37f2d6 Binary files /dev/null and b/tests/ragger/snapshots/nanox/test_gating_blind_signing/warning/00000.png differ diff --git a/tests/ragger/snapshots/nanox/test_gating_blind_signing/warning/00001.png b/tests/ragger/snapshots/nanox/test_gating_blind_signing/warning/00001.png new file mode 100644 index 0000000000..4943ad331d Binary files /dev/null and b/tests/ragger/snapshots/nanox/test_gating_blind_signing/warning/00001.png differ diff --git a/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/00000.png b/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/00000.png new file mode 100644 index 0000000000..ebda01a292 Binary files /dev/null and b/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/00000.png differ diff --git a/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/00001.png b/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/00001.png new file mode 100644 index 0000000000..9206a0fbc7 Binary files /dev/null and b/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/00001.png differ diff --git a/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/00002.png b/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/00002.png new file mode 100644 index 0000000000..e2965f5d7b Binary files /dev/null and b/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/00002.png differ diff --git a/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/00003.png b/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/00003.png new file mode 100644 index 0000000000..ac0dde80f3 Binary files /dev/null and b/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/00003.png differ diff --git a/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/00004.png b/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/00004.png new file mode 100644 index 0000000000..e5009fe878 Binary files /dev/null and b/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/00004.png differ diff --git a/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/00005.png b/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/00005.png new file mode 100644 index 0000000000..c34edd729b Binary files /dev/null and b/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/00005.png differ diff --git a/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/00006.png b/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/00006.png new file mode 100644 index 0000000000..845abc816a Binary files /dev/null and b/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/00006.png differ diff --git a/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/00007.png b/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/00007.png new file mode 100644 index 0000000000..3a161194e8 Binary files /dev/null and b/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/00007.png differ diff --git a/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/warning/00000.png b/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/warning/00000.png new file mode 100644 index 0000000000..83ea37f2d6 Binary files /dev/null and b/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/warning/00000.png differ diff --git a/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/warning/00001.png b/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/warning/00001.png new file mode 100644 index 0000000000..4943ad331d Binary files /dev/null and b/tests/ragger/snapshots/nanox/test_gating_blind_signing_with_proxy/warning/00001.png differ diff --git a/tests/ragger/snapshots/stax/test_blind_sign/warning/00000.png b/tests/ragger/snapshots/stax/test_blind_sign/warning/00000.png index e7a78b5be3..f60810337e 100644 Binary files a/tests/ragger/snapshots/stax/test_blind_sign/warning/00000.png and b/tests/ragger/snapshots/stax/test_blind_sign/warning/00000.png differ diff --git a/tests/ragger/snapshots/stax/test_blind_sign_nonce_nonzero/warning/00000.png b/tests/ragger/snapshots/stax/test_blind_sign_nonce_nonzero/warning/00000.png index e7a78b5be3..f60810337e 100644 Binary files a/tests/ragger/snapshots/stax/test_blind_sign_nonce_nonzero/warning/00000.png and b/tests/ragger/snapshots/stax/test_blind_sign_nonce_nonzero/warning/00000.png differ diff --git a/tests/ragger/snapshots/stax/test_blind_sign_nonzero/warning/00000.png b/tests/ragger/snapshots/stax/test_blind_sign_nonzero/warning/00000.png index e7a78b5be3..f60810337e 100644 Binary files a/tests/ragger/snapshots/stax/test_blind_sign_nonzero/warning/00000.png and b/tests/ragger/snapshots/stax/test_blind_sign_nonzero/warning/00000.png differ diff --git a/tests/ragger/snapshots/stax/test_blind_sign_rejected/warning/00000.png b/tests/ragger/snapshots/stax/test_blind_sign_rejected/warning/00000.png index e7a78b5be3..f60810337e 100644 Binary files a/tests/ragger/snapshots/stax/test_blind_sign_rejected/warning/00000.png and b/tests/ragger/snapshots/stax/test_blind_sign_rejected/warning/00000.png differ diff --git a/tests/ragger/snapshots/stax/test_gating_blind_signing/00000.png b/tests/ragger/snapshots/stax/test_gating_blind_signing/00000.png new file mode 100644 index 0000000000..d6b0987496 Binary files /dev/null and b/tests/ragger/snapshots/stax/test_gating_blind_signing/00000.png differ diff --git a/tests/ragger/snapshots/stax/test_gating_blind_signing/00001.png b/tests/ragger/snapshots/stax/test_gating_blind_signing/00001.png new file mode 100644 index 0000000000..3a608b9bd0 Binary files /dev/null and b/tests/ragger/snapshots/stax/test_gating_blind_signing/00001.png differ diff --git a/tests/ragger/snapshots/stax/test_gating_blind_signing/00002.png b/tests/ragger/snapshots/stax/test_gating_blind_signing/00002.png new file mode 100644 index 0000000000..077d6c61f9 Binary files /dev/null and b/tests/ragger/snapshots/stax/test_gating_blind_signing/00002.png differ diff --git a/tests/ragger/snapshots/stax/test_gating_blind_signing/00003.png b/tests/ragger/snapshots/stax/test_gating_blind_signing/00003.png new file mode 100644 index 0000000000..2e84245871 Binary files /dev/null and b/tests/ragger/snapshots/stax/test_gating_blind_signing/00003.png differ diff --git a/tests/ragger/snapshots/stax/test_gating_blind_signing/00004.png b/tests/ragger/snapshots/stax/test_gating_blind_signing/00004.png new file mode 100644 index 0000000000..ceda6a87aa Binary files /dev/null and b/tests/ragger/snapshots/stax/test_gating_blind_signing/00004.png differ diff --git a/tests/ragger/snapshots/stax/test_gating_blind_signing/00005.png b/tests/ragger/snapshots/stax/test_gating_blind_signing/00005.png new file mode 100644 index 0000000000..339db1b45c Binary files /dev/null and b/tests/ragger/snapshots/stax/test_gating_blind_signing/00005.png differ diff --git a/tests/ragger/snapshots/stax/test_gating_blind_signing/warning/00000.png b/tests/ragger/snapshots/stax/test_gating_blind_signing/warning/00000.png new file mode 100644 index 0000000000..d9cd014f44 Binary files /dev/null and b/tests/ragger/snapshots/stax/test_gating_blind_signing/warning/00000.png differ diff --git a/tests/ragger/snapshots/stax/test_gating_blind_signing/warning/00001.png b/tests/ragger/snapshots/stax/test_gating_blind_signing/warning/00001.png new file mode 100644 index 0000000000..f60810337e Binary files /dev/null and b/tests/ragger/snapshots/stax/test_gating_blind_signing/warning/00001.png differ diff --git a/tests/ragger/snapshots/stax/test_gating_blind_signing_with_proxy/00000.png b/tests/ragger/snapshots/stax/test_gating_blind_signing_with_proxy/00000.png new file mode 100644 index 0000000000..d6b0987496 Binary files /dev/null and b/tests/ragger/snapshots/stax/test_gating_blind_signing_with_proxy/00000.png differ diff --git a/tests/ragger/snapshots/stax/test_gating_blind_signing_with_proxy/00001.png b/tests/ragger/snapshots/stax/test_gating_blind_signing_with_proxy/00001.png new file mode 100644 index 0000000000..a75f1e7f59 Binary files /dev/null and b/tests/ragger/snapshots/stax/test_gating_blind_signing_with_proxy/00001.png differ diff --git a/tests/ragger/snapshots/stax/test_gating_blind_signing_with_proxy/00002.png b/tests/ragger/snapshots/stax/test_gating_blind_signing_with_proxy/00002.png new file mode 100644 index 0000000000..fc24a5ef96 Binary files /dev/null and b/tests/ragger/snapshots/stax/test_gating_blind_signing_with_proxy/00002.png differ diff --git a/tests/ragger/snapshots/stax/test_gating_blind_signing_with_proxy/00003.png b/tests/ragger/snapshots/stax/test_gating_blind_signing_with_proxy/00003.png new file mode 100644 index 0000000000..2e84245871 Binary files /dev/null and b/tests/ragger/snapshots/stax/test_gating_blind_signing_with_proxy/00003.png differ diff --git a/tests/ragger/snapshots/stax/test_gating_blind_signing_with_proxy/00004.png b/tests/ragger/snapshots/stax/test_gating_blind_signing_with_proxy/00004.png new file mode 100644 index 0000000000..ceda6a87aa Binary files /dev/null and b/tests/ragger/snapshots/stax/test_gating_blind_signing_with_proxy/00004.png differ diff --git a/tests/ragger/snapshots/stax/test_gating_blind_signing_with_proxy/00005.png b/tests/ragger/snapshots/stax/test_gating_blind_signing_with_proxy/00005.png new file mode 100644 index 0000000000..339db1b45c Binary files /dev/null and b/tests/ragger/snapshots/stax/test_gating_blind_signing_with_proxy/00005.png differ diff --git a/tests/ragger/snapshots/stax/test_gating_blind_signing_with_proxy/warning/00000.png b/tests/ragger/snapshots/stax/test_gating_blind_signing_with_proxy/warning/00000.png new file mode 100644 index 0000000000..d9cd014f44 Binary files /dev/null and b/tests/ragger/snapshots/stax/test_gating_blind_signing_with_proxy/warning/00000.png differ diff --git a/tests/ragger/snapshots/stax/test_gating_blind_signing_with_proxy/warning/00001.png b/tests/ragger/snapshots/stax/test_gating_blind_signing_with_proxy/warning/00001.png new file mode 100644 index 0000000000..f60810337e Binary files /dev/null and b/tests/ragger/snapshots/stax/test_gating_blind_signing_with_proxy/warning/00001.png differ diff --git a/tests/ragger/snapshots/stax/test_sign_parameter_selector/00006.png b/tests/ragger/snapshots/stax/test_sign_parameter_selector/00006.png index e7a78b5be3..f60810337e 100644 Binary files a/tests/ragger/snapshots/stax/test_sign_parameter_selector/00006.png and b/tests/ragger/snapshots/stax/test_sign_parameter_selector/00006.png differ diff --git a/tests/ragger/snapshots/stax/test_tx_simulation_blind_sign_benign/warning/00000.png b/tests/ragger/snapshots/stax/test_tx_simulation_blind_sign_benign/warning/00000.png index e7a78b5be3..f60810337e 100644 Binary files a/tests/ragger/snapshots/stax/test_tx_simulation_blind_sign_benign/warning/00000.png and b/tests/ragger/snapshots/stax/test_tx_simulation_blind_sign_benign/warning/00000.png differ diff --git a/tests/ragger/snapshots/stax/test_tx_simulation_blind_sign_issue/warning/00000.png b/tests/ragger/snapshots/stax/test_tx_simulation_blind_sign_issue/warning/00000.png index e7a78b5be3..f60810337e 100644 Binary files a/tests/ragger/snapshots/stax/test_tx_simulation_blind_sign_issue/warning/00000.png and b/tests/ragger/snapshots/stax/test_tx_simulation_blind_sign_issue/warning/00000.png differ diff --git a/tests/ragger/test_blind_sign.py b/tests/ragger/test_blind_sign.py index f9435f839e..7c07d01ded 100644 --- a/tests/ragger/test_blind_sign.py +++ b/tests/ragger/test_blind_sign.py @@ -18,6 +18,8 @@ import client.response_parser as ResponseParser from client.utils import recover_transaction from client.tx_simu import TxSimu +from client.gating import Gating +from client.proxy_info import ProxyInfo BIP32_PATH = "m/44'/60'/0'/0/0" @@ -70,7 +72,8 @@ def common_blind_sign(scenario_navigator: NavigateWithScenario, test_name: str, app_client: EthAppClient, tx_params: dict, - reject: bool = False): + reject: bool = False, + nb_warnings: int = 1) -> None: try: with app_client.sign(BIP32_PATH, tx_params): if reject: @@ -80,9 +83,9 @@ def common_blind_sign(scenario_navigator: NavigateWithScenario, test_name += "_nonzero" if reject: - scenario_navigator.review_reject_with_warning(test_name=test_name) + scenario_navigator.review_reject_with_warning(test_name=test_name, nb_warnings=nb_warnings) else: - scenario_navigator.review_approve_with_warning(test_name=test_name) + scenario_navigator.review_approve_with_warning(test_name=test_name, nb_warnings=nb_warnings) except ExceptionRAPDU as e: assert reject @@ -101,7 +104,9 @@ def test_blind_sign(navigator: Navigator, test_name: str, reject: bool, amount: float, - simu_params: Optional[TxSimu] = None): + simu_params: Optional[TxSimu] = None, + gating_params: Optional[Gating] = None, + with_proxy: bool = False): if reject and amount > 0.0: pytest.skip() @@ -115,6 +120,30 @@ def test_blind_sign(navigator: Navigator, tx_params = common_tx_params(amount) + if with_proxy: + # Change address to a proxy contract + tx_params["to"] = bytes.fromhex("CcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC") + # Set proxy implementation address + address = bytes.fromhex("6b175474e89094c44da98b954eedeac495271d0f") + if gating_params is not None: + # Override gating address to match proxy implementation + gating_params.address = address + proxy_info = ProxyInfo( + ResponseParser.challenge(app_client.get_challenge().data), + address, + tx_params["chainId"], + tx_params["to"], + selector=None, + ) + response = app_client.provide_proxy_info(proxy_info.serialize()) + assert response.status == StatusWord.OK + + nb_warnings = 1 + if gating_params is not None: + response = app_client.provide_gating(gating_params) + assert response.status == StatusWord.OK + nb_warnings += 1 + if not reject and simu_params is not None: _, tx_hash = app_client.serialize_tx(tx_params) simu_params.tx_hash = tx_hash @@ -127,7 +156,8 @@ def test_blind_sign(navigator: Navigator, test_name, app_client, tx_params, - reject) + reject, + nb_warnings) # Token approval, would require providing the token metadata from the CAL diff --git a/tests/ragger/test_eip712.py b/tests/ragger/test_eip712.py index f0f4208265..d953e43462 100644 --- a/tests/ragger/test_eip712.py +++ b/tests/ragger/test_eip712.py @@ -1,3 +1,5 @@ +# pylint: disable=too-many-lines +# Large test file covering many EIP-712 scenarios import fnmatch import os from pathlib import Path @@ -25,9 +27,9 @@ from client.proxy_info import ProxyInfo from client.gcs import ( - Field, ParamType, ParamRaw, Value, TypeFamily, DataPath, PathTuple, ParamTrustedName, - ParamNFT, ParamDatetime, DatetimeType, ParamTokenAmount, ParamToken, ParamCalldata, - ParamAmount, ContainerPath, PathLeaf, PathLeafType, PathRef, PathArray, TxInfo + Field, Value, TypeFamily, DataPath, PathTuple, ParamTokenAmount, + ContainerPath, PathLeaf, PathLeafType, TxInfo, ParamCalldata, + PathRef, PathArray, ParamRaw ) @@ -497,9 +499,8 @@ def tokens_fixture(request) -> list[dict]: def test_eip712_advanced_missing_token(scenario_navigator: NavigateWithScenario, - test_name: str, tokens: list[dict]): - test_name += f"-{len(tokens[0]) == 0}-{len(tokens[1]) == 0}" + test_name = f"{scenario_navigator.test_name}-{len(tokens[0]) == 0}-{len(tokens[1]) == 0}" data = { "types": { @@ -583,10 +584,9 @@ def filt_tn_types_fixture(request) -> list[TrustedNameType]: def test_eip712_advanced_trusted_name(scenario_navigator: NavigateWithScenario, - test_name: str, trusted_name: tuple, filt_tn_types: list[TrustedNameType]): - test_name += f"_{trusted_name[0].name.lower()}_with" + test_name = f"{scenario_navigator.test_name}_{trusted_name[0].name.lower()}_with" for t in filt_tn_types: test_name += f"_{t.name.lower()}" diff --git a/tests/ragger/test_gating.py b/tests/ragger/test_gating.py new file mode 100644 index 0000000000..526383cff8 --- /dev/null +++ b/tests/ragger/test_gating.py @@ -0,0 +1,40 @@ +from ragger.navigator.navigation_scenario import NavigateWithScenario + +from test_blind_sign import test_blind_sign as blind_sign + +from client.gating import Gating + + +def test_gating_blind_signing(scenario_navigator: NavigateWithScenario) -> None: + """Test the Gating descriptor APDU with a blind signing transaction""" + + descriptor = Gating( + bytes.fromhex("6b175474e89094c44da98b954eedeac495271d0f"), + 1, + "To scan for threats and verify this transaction before signing, use Ledger Multisig.", + "ledger.com/ledger-multisig", + ) + blind_sign(scenario_navigator.navigator, + scenario_navigator, + scenario_navigator.test_name, + False, + 0.0, + gating_params=descriptor) + +def test_gating_blind_signing_with_proxy(scenario_navigator: NavigateWithScenario) -> None: + """Test the Gating descriptor APDU with a blind signing transaction""" + + descriptor = Gating( + bytes.fromhex("Dad77910DbDFdE764fC21FCD4E74D71bBACA6D8D"), + 1, + "To scan for threats and verify this transaction before signing, use Ledger Multisig.", + "ledger.com/ledger-multisig", + ) + + blind_sign(scenario_navigator.navigator, + scenario_navigator, + scenario_navigator.test_name, + False, + 0.0, + gating_params=descriptor, + with_proxy=True) diff --git a/tests/ragger/test_nft.py b/tests/ragger/test_nft.py index 3d22f01285..f796b31022 100644 --- a/tests/ragger/test_nft.py +++ b/tests/ragger/test_nft.py @@ -16,6 +16,7 @@ from client.utils import get_selector_from_data, recover_transaction from client.tx_simu import TxSimu from client.dynamic_networks import DynamicNetwork +from client.gating import Gating BIP32_PATH = "m/44'/60'/0'/0/0" @@ -56,7 +57,8 @@ def common_test_nft(scenario_navigator: NavigateWithScenario, action: Action, reject: bool, plugin_name: str, - simu_params: Optional[TxSimu] = None): + simu_params: Optional[TxSimu] = None, + gating_params: Optional[Gating] = None): global DEVICE_ADDR backend = scenario_navigator.backend app_client = EthAppClient(backend) @@ -79,6 +81,11 @@ def common_test_nft(scenario_navigator: NavigateWithScenario, response = app_client.provide_tx_simulation(simu_params) assert response.status == StatusWord.OK + if gating_params is not None: + gating_params.selector = get_selector_from_data(tx_params["data"]) + response = app_client.provide_gating(gating_params) + assert response.status == StatusWord.OK + # Send Network information (name, ticker, icon) name, ticker, icon = get_network_config(backend.device.type, collec.chain_id) if name and ticker: @@ -100,7 +107,7 @@ def common_test_nft(scenario_navigator: NavigateWithScenario, test_name += f"_{action.fn_name}_{str(collec.chain_id)}" if reject: scenario_navigator.review_reject(test_name=test_name) - elif simu_params is not None: + elif simu_params is not None or gating_params is not None: scenario_navigator.review_approve_with_warning(test_name=test_name) else: scenario_navigator.review_approve(test_name=test_name)