From 3028a32949853cb722b0d0cc78f3185c3dc0725e Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Fri, 1 Nov 2024 12:35:00 +0100 Subject: [PATCH 1/7] Upgrade Ragger tests to use App, and ensure Ragger tests can be run using device. --- doc/test-vectors-ed25519.txt | 122 ---------- doc/test-vectors-secp286k1.txt | 21 -- ragger_tests/__init__.py | 1 + ragger_tests/application_client/__init__.py | 1 + ragger_tests/application_client/app.py | 119 ++++++++++ .../application_client/command_sender.py | 128 ++++++++++ ragger_tests/application_client/curve.py | 118 ++++++++++ .../application_client/instruction_type.py | 20 ++ ragger_tests/application_client/py.typed | 0 .../application_client/request_packer.py | 6 + .../application_client/response_unpacker.py | 219 ++++++++++++++++++ ragger_tests/conftest.py | 24 ++ ragger_tests/requirements.txt | 5 +- ragger_tests/setup.cfg | 3 + .../test-physical-device-nanos-debug.sh | 1 + ragger_tests/test-physical-device-nanos.sh | 1 + .../test-physical-device-nanosplus.sh | 1 + ragger_tests/test-physical-device-nanox.sh | 1 + ...debug.sh => test-simulator-nanos-debug.sh} | 2 +- ...{test-nanos.sh => test-simulator-nanos.sh} | 2 +- ...nosplus.sh => test-simulator-nanosplus.sh} | 2 +- ...{test-nanox.sh => test-simulator-nanox.sh} | 2 +- ragger_tests/test_get_app_version.py | 25 +- ragger_tests/test_get_device_id.py | 14 +- ragger_tests/test_get_device_model.py | 11 +- ragger_tests/test_get_public_key_ed25519.py | 105 ++++----- ragger_tests/test_get_public_key_secp256k1.py | 82 +++---- ragger_tests/test_sign_auth_ed25519.py | 114 +++++---- ragger_tests/test_sign_auth_secp256k1.py | 84 +++---- test/test-all.sh | 1 - test/test-get-application-version.py | 27 --- test/test-get-device-id.py | 27 --- test/test-get-device-model.py | 33 --- test/test-get-public-key-ed25519.py | 82 ------- test/test-get-public-key-secp256k1.py | 79 ------- test/test-sign-auth-ed25519.py | 142 ------------ test/test-sign-auth-secp256k1.py | 139 ----------- 37 files changed, 861 insertions(+), 903 deletions(-) delete mode 100644 doc/test-vectors-ed25519.txt delete mode 100644 doc/test-vectors-secp286k1.txt create mode 100644 ragger_tests/__init__.py create mode 100644 ragger_tests/application_client/__init__.py create mode 100644 ragger_tests/application_client/app.py create mode 100644 ragger_tests/application_client/command_sender.py create mode 100644 ragger_tests/application_client/curve.py create mode 100644 ragger_tests/application_client/instruction_type.py create mode 100644 ragger_tests/application_client/py.typed create mode 100644 ragger_tests/application_client/request_packer.py create mode 100644 ragger_tests/application_client/response_unpacker.py create mode 100755 ragger_tests/test-physical-device-nanos-debug.sh create mode 100755 ragger_tests/test-physical-device-nanos.sh create mode 100755 ragger_tests/test-physical-device-nanosplus.sh create mode 100755 ragger_tests/test-physical-device-nanox.sh rename ragger_tests/{test-nanos-debug.sh => test-simulator-nanos-debug.sh} (63%) rename ragger_tests/{test-nanos.sh => test-simulator-nanos.sh} (63%) rename ragger_tests/{test-nanosplus.sh => test-simulator-nanosplus.sh} (64%) rename ragger_tests/{test-nanox.sh => test-simulator-nanox.sh} (63%) delete mode 100755 test/test-get-application-version.py delete mode 100755 test/test-get-device-id.py delete mode 100755 test/test-get-device-model.py delete mode 100755 test/test-get-public-key-ed25519.py delete mode 100755 test/test-get-public-key-secp256k1.py delete mode 100755 test/test-sign-auth-ed25519.py delete mode 100755 test/test-sign-auth-secp256k1.py diff --git a/doc/test-vectors-ed25519.txt b/doc/test-vectors-ed25519.txt deleted file mode 100644 index 918f6bf7..00000000 --- a/doc/test-vectors-ed25519.txt +++ /dev/null @@ -1,122 +0,0 @@ -#################################################################################################### -🚀 Ed25519 keys with CAP26 derivation paths 🚀 -📚 mnemonic: equip will roof matter pink blind book anxiety banner elbow sun young -🛰️ network: kisharnet (12) - - -~~~~~ 💸 ENTITY_TYPE: ACCOUNT (525) 💸 ~~~~~ - -🗝️ ✍️ KEY_TYPE: TRANSACTION_SIGNING (1460) -🔮 Derivation Path: m/44H/1022H/12H/525H/1460H/0H -🔑 🔐 Private Key: 13e971fb16cb2c816d6b9f12176e9b8ab9af1831d006114d344d119ab2715506 -🔑 🔓 Public Key: 451152a1cef7be603205086d4ebac0a0b78fda2ff4684b9dea5ca9ef003d4e7d - -🔮 Derivation Path: m/44H/1022H/12H/525H/1460H/1H -🔑 🔐 Private Key: ec7634aff9d698d9a5b4001d5eaa878eefc4fc05939dfedcef0112329fd9966a -🔑 🔓 Public Key: 0a4b894208a1f6b1bd7e823b59909f01aae0172b534baa2905b25f1bcbbb4f0a - -🔮 Derivation Path: m/44H/1022H/12H/525H/1460H/2H -🔑 🔐 Private Key: 9e96517567abba3e5492db11bc016450abb4e60406038d6423a87a0ad860a9ce -🔑 🔓 Public Key: 235c27aafa83376d475040a7eb53ea889ae93bda005ef7f445f221f73b43313e - -🔮 Derivation Path: m/44H/1022H/12H/525H/1460H/3H -🔑 🔐 Private Key: e92d352f6846e75fb760c40229de8c3c2b04210b2955129877286cf15893a21e -🔑 🔓 Public Key: cc294e8c43be93012f827cd54a11a2f836e5934c2361d61b5a737adbd42bf030 - - -🗝️ 🛂 KEY_TYPE: AUTHENTICATION_SIGNING (1678) -🔮 Derivation Path: m/44H/1022H/12H/525H/1678H/0H -🔑 🔐 Private Key: 4a39274fd5f320172329ec96e88b658ad9798ea47f292e30f80915f01f3acc48 -🔑 🔓 Public Key: 2612a6865d354ed285baf4877d671276e6cd8cd81e3f1101c35d16853c204fa4 - -🔮 Derivation Path: m/44H/1022H/12H/525H/1678H/1H -🔑 🔐 Private Key: d02147e27720dba12acbddc3d6d4fc43a64cab1dbbdcc3e7e0268c766deeccce -🔑 🔓 Public Key: 2f92b0b43ee39c6c3006b2a5c7cdbdee0c6b6835d76a0dc8da0aeffc741d5c96 - -🔮 Derivation Path: m/44H/1022H/12H/525H/1678H/2H -🔑 🔐 Private Key: 41519644a280fc18191765ee32fdcc7a37ac95012e18c5fb31679222925da1ce -🔑 🔓 Public Key: 3f23bcce53cf2ea14d238f8473aaf3c7ed3f4047fa20158389eabb651766f8d5 - -🔮 Derivation Path: m/44H/1022H/12H/525H/1678H/3H -🔑 🔐 Private Key: b843c520aca4d980f69dcf02a6d3deb50c10bdaa350ed262b49cbb0997fcbd28 -🔑 🔓 Public Key: 5b36d055cdd07129ba0b780cd285661d5dae02831a55b408f84f9b72ba95c0a9 - - -🗝️ 📨 KEY_TYPE: MESSAGE_ENCRYPTION (1391) -🔮 Derivation Path: m/44H/1022H/12H/525H/1391H/0H -🔑 🔐 Private Key: 62e1255e91b6fafdf06d3d6e3cfa660a5dd39aaa6ab7c207e2535c86f597e47f -🔑 🔓 Public Key: d998153796a745c2f733079c791f4ae93eb96a812b39c9ee7a26eca32fa14905 - -🔮 Derivation Path: m/44H/1022H/12H/525H/1391H/1H -🔑 🔐 Private Key: 30d9891cf7436d07f45a3358bbf4b0857388c08d1a7fe9973fd6044fa86a9ce0 -🔑 🔓 Public Key: 94e163e6739fa0c9db3f44c0675f185fdb0f1dddb6d929cc49a199717c0a2da2 - -🔮 Derivation Path: m/44H/1022H/12H/525H/1391H/2H -🔑 🔐 Private Key: 80b136f184159c7873321a73e6523be68d428440f95efb77fb67b43560cd5401 -🔑 🔓 Public Key: 9bd51ee27f37367ee4c7cf18e5b8e1b40ae808d3da0350de152c9db34de778d3 - -🔮 Derivation Path: m/44H/1022H/12H/525H/1391H/3H -🔑 🔐 Private Key: 215af43054ac6055f86c9986d482a8fe6b0bf70543f6ebe74f69e33424b11282 -🔑 🔓 Public Key: d9d6fc68321ce02c30122c6ff5c1a8068142703f9dac362cff29bfb814a2130f - -******************************************************************************** -******************************************************************************** -******************************************************************************** - - -~~~~~ 🎭 ENTITY_TYPE: IDENTITY (618) 🎭 ~~~~~ - -🗝️ ✍️ KEY_TYPE: TRANSACTION_SIGNING (1460) -🔮 Derivation Path: m/44H/1022H/12H/618H/1460H/0H -🔑 🔐 Private Key: 9c683ba15644596f747bc749fed2657644c2873391f9c874efd32ccacc5adf08 -🔑 🔓 Public Key: cc129e0d00d365f2269cee259923e904b8c46ef5b28aefc18df8ed20ef42a3eb - -🔮 Derivation Path: m/44H/1022H/12H/618H/1460H/1H -🔑 🔐 Private Key: aa45993887e5fe45252db7b34ad26686a4ef165f65ba30206d87d900310ea360 -🔑 🔓 Public Key: f67ac35c37921579b59f77fe05a1012ad8240092c8fed0d8fe1f96206eb99c3e - -🔮 Derivation Path: m/44H/1022H/12H/618H/1460H/2H -🔑 🔐 Private Key: a5b3a586440f996d12ac9f21f61ed0758c13c012e42ed8c9d83e4bf4548e3dd3 -🔑 🔓 Public Key: 02c8074258844ae4b81d80261fc411e95070526e0d92803fe4f343e00dc89ed5 - -🔮 Derivation Path: m/44H/1022H/12H/618H/1460H/3H -🔑 🔐 Private Key: da699f61d6c2a4893d00b1f15158894974fb403a16a865583538f0542e883c54 -🔑 🔓 Public Key: fca4f791866a48cb53a269e420fa6b7f192e98fee5f9e8f9009962ca3d9baeb2 - - -🗝️ 🛂 KEY_TYPE: AUTHENTICATION_SIGNING (1678) -🔮 Derivation Path: m/44H/1022H/12H/618H/1678H/0H -🔑 🔐 Private Key: 7997d39b74a390bc213c566ec016dd9023c4319af9da5194fb87c0d73f1d970f -🔑 🔓 Public Key: f6f2056e3edb8905be1717c1f8f5204242047875ba548c19d42962366800c1d4 - -🔮 Derivation Path: m/44H/1022H/12H/618H/1678H/1H -🔑 🔐 Private Key: fa9c15acc1f46b790acdb060682c8d9fca307f02ba7a1deee4009c4f89cc3ddc -🔑 🔓 Public Key: 960ee0acd88b0e7f1e8cb171139c2e0e7b8d776134a103b36034a6991fcac175 - -🔮 Derivation Path: m/44H/1022H/12H/618H/1678H/2H -🔑 🔐 Private Key: 4aea7c10102b93b173a72c62c6e5b3a19dacbc4e5dee6fc3f32e04a35d012059 -🔑 🔓 Public Key: 07ba2aa69eee065495d8820ef9d5a94b982370e233f04472900cfb5efdb4fa3d - -🔮 Derivation Path: m/44H/1022H/12H/618H/1678H/3H -🔑 🔐 Private Key: 11e570fe1fc5c7a0deba1c672428b0793f45ca091580a50561ab46e50147ed07 -🔑 🔓 Public Key: b4763c9a25d95f32e5ddefc7006ffc4a6570818bf24aeff581ac60cd82a751ba - - -🗝️ 📨 KEY_TYPE: MESSAGE_ENCRYPTION (1391) -🔮 Derivation Path: m/44H/1022H/12H/618H/1391H/0H -🔑 🔐 Private Key: 603ca94347db5edba67c73fa2c75d40f8534efbb6f043e279a62959b799fc55b -🔑 🔓 Public Key: 996626245f999a4c500c394036db43f73abb18f46970066ff124c750dc096360 - -🔮 Derivation Path: m/44H/1022H/12H/618H/1391H/1H -🔑 🔐 Private Key: 8564e2302ef419354a47265b6e5e6bed276b34cb691ef5a73fd6a722052cacda -🔑 🔓 Public Key: afe925e5aabfa04fb10640cad2c1f6739b3dc9fb4ddeba6ff28e90854d45962d - -🔮 Derivation Path: m/44H/1022H/12H/618H/1391H/2H -🔑 🔐 Private Key: 7af1d0b3f6a634891fb502de1bc14d3a06402e96380dfe377d4fb5864922cdf6 -🔑 🔓 Public Key: 1226b881b66c58015e760852c0cb202869b73e29fbe0b3d22d50af0fa606b14a - -🔮 Derivation Path: m/44H/1022H/12H/618H/1391H/3H -🔑 🔐 Private Key: f5ec1d8379d2173975ea693afbd8940820f9d1b82b9f777f02c1ecd4197deab0 -🔑 🔓 Public Key: 7fa1d448ef608a6e1a8533d816367b4fa0d60c39844bb82dbce1ea266105a275 - -#################################################################################################### diff --git a/doc/test-vectors-secp286k1.txt b/doc/test-vectors-secp286k1.txt deleted file mode 100644 index 4742fd43..00000000 --- a/doc/test-vectors-secp286k1.txt +++ /dev/null @@ -1,21 +0,0 @@ -// Derivation path, public key, private key -"m/44H/1022H/0H/0/0H", "03f43fba6541031ef2195f5ba96677354d28147e45b40cde4662bec9162c361f55", "e6aec3c1b9c6b49f154c99708ce4bdb36a01de3f13a832111d7d64e368f939ce", -"m/44H/1022H/0H/0/1H", "0206ea8842365421f48ab84e6b1b197010e5a43a527952b11bc6efe772965e97cc", "7938cc222877aa0f9b4293478bf5733577ceb54cc4834e6e518cbc3847751fd6", -"m/44H/1022H/0H/0/2H", "024f44df0493977fcc5704c00c5c89932d77a9a0b016680e6a931684e160fb8d99", "4e71fa6a8c4612e79b2a9f0f4956f0a8eacf3f19ec56b6d40cc874ac4010f917", -"m/44H/1022H/0H/0/3H", "0388485f6889d7ebcf1cf6f6dafc8ae5d224f9e847fac868c2e006e71ff3383a91", "2ab7a40d7da148a98d200cf2f3a9b6f95c29e637b4d959928f6f315ad2bc0384", -"m/44H/1022H/0H/0/4H", "024128185f801aee4ebe9a70d6290f60051162526551240da1374363b58e2e1e2c", "4c25b3cacca546e8682438e2915698d0ab388abd4e4247d7096814c6ac8f3015", -"m/44H/1022H/0H/0/5H", "03f3f51a028cbed1a2c0c3b1f21abc5354f58e8d5279e817195750b8ddec9334f4", "318ffcfdf4c40964243f018363017c4749f2503fe948d189d83069684207e8c2", -"m/44H/1022H/0H/0/6H", "0383d0721aac0569c37edafe5edd6e2d320822dc23f9207b263b2319837ed1a89d", "34418351c9f02e0b9b9392e1523c14c16cf4edb9df71dfb13b7a6d7ff73be18e", -"m/44H/1022H/0H/0/7H", "03af13461247c39e54fab62597701ab06c67edac7f8de4df1283a2645706c0b153", "f0d67eaa2ff06afd435346ad5e1fd9ad8b892ac643de7bbe07570a152b6b0ffd", -"m/44H/1022H/0H/0/8H", "0226912f5226f4a7c9c80780f32c6ad406c8b471c4929033e5e1756ca248c5a278", "c5e76f69e4ecef5f590387f1f6d179f65736f77098eebe97e7c8d05c43d4cb0b", -"m/44H/1022H/0H/0/9H", "035a9825274e30ce325cc3934b4e23b008097bd97f1b0a0ef57f7dc9a33e5760ed", "a019c18261515c004c170ea2d7da1bad2268a1751cd5233842c6ed4c91fa2e77", -"m/44H/1022H/0H/0/0", "03bc2ec8f3668c869577bf66b7b48f8dee57b833916aa70966fa4a5029b63bb18f", "623048f7bb88a4d162442b88cdd80c85e4d5933ad9e78523a97de769badb9ab2", -"m/44H/1022H/0H/0/1", "03c8a6a5710b5abba09341c24382de3222913120dee5084e887529bf821f3973e2", "e94b6a64f99a1a143ed570bea9cf896ce82d14f861d0103066e835822037fe6b", -"m/44H/1022H/0H/0/2", "02d6b5b132e16160d6337d83408165c49edac7bb0112b1d1b3e96e3f6908f6d0d6", "692824fba987bd09ddd42d8ceea38676ed48309d19dc159c4dd4ec83d2a666a1", -"m/44H/1022H/0H/0/3", "03ce5f85ad86922fbc217806a79d9f4d8d6a268f3822ffed9533a9fff73a4374b7", "9343a365148bdbd0fd8adff8dc1a5f2630b61705141553c5d6d0526da8776f88", -"m/44H/1022H/0H/0/4", "03e2c66201fc7330992d316d847bdbeb561704b70779ce60a4fcff53ffe5b6cb36", "5f288fad35651d1cd3c344d06512593e68ce0c4e6e7f96f1b1fbe337d20b7325", -"m/44H/1022H/0H/0/5", "02df71a292057d1f7cda4fbcd252e43907646610cc191b6f44050683f82a7e63de", "1289c95c78bcbf21a5455654836b946561d3c477916a049601d4e19cfe7507c2", -"m/44H/1022H/0H/0/6", "03d054f1c3d7982994d9581c496f84b6cdf584c8eff0401da82d8c19ad88e8a768", "c038f504e4fe4999dc9355e1c7547ececa2a8990ab63806e9593cbccfe28fa97", -"m/44H/1022H/0H/0/7", "03ccf3b2bd4294d7e7e84268003f1e25c4893a482e28fcf00dfc1ff65679541d50", "24333e864e7143ec355bdc9db1405afc1188b2f8bb812075fec34d5459ef7df8", -"m/44H/1022H/0H/0/8", "0284c067d070bfdb790883cab583f13a0b13f9d52eacdb52a5d8588231ce8c8b89", "53e8f579fc1a845357f1ad6194cce8c8e71ede05b3574177d3a31b92950dde5e", -"m/44H/1022H/0H/0/9", "02e5703b668deebac710118df687296e90da93c19d0db3cce656b6a677ab3e4747", "d27012ba6fe8db796c6753790c381b77a86ce058bdbb0e59880d40efad5bdbc3", diff --git a/ragger_tests/__init__.py b/ragger_tests/__init__.py new file mode 100644 index 00000000..c7df41ce --- /dev/null +++ b/ragger_tests/__init__.py @@ -0,0 +1 @@ +import os, sys; sys.path.append(os.path.dirname(os.path.realpath(__file__))) \ No newline at end of file diff --git a/ragger_tests/application_client/__init__.py b/ragger_tests/application_client/__init__.py new file mode 100644 index 00000000..c7df41ce --- /dev/null +++ b/ragger_tests/application_client/__init__.py @@ -0,0 +1 @@ +import os, sys; sys.path.append(os.path.dirname(os.path.realpath(__file__))) \ No newline at end of file diff --git a/ragger_tests/application_client/app.py b/ragger_tests/application_client/app.py new file mode 100644 index 00000000..4af9d64e --- /dev/null +++ b/ragger_tests/application_client/app.py @@ -0,0 +1,119 @@ +from typing import Generator, Callable, Optional +from contextlib import contextmanager + +from ragger.backend.interface import BackendInterface, RAPDU +from response_unpacker import PK, LedgerModel, Version, ROLAResponseEd25519, ROLAResponseSecp256k1, ROLAResp, unpack_get_version_response +from command_sender import CommandSender, InsType +from request_packer import RequestPacker +from application_client.curve import C + +class App: + def __init__(self, backend: BackendInterface) -> None: + self.sender = CommandSender(backend) + + def access_last_async_response(self) -> Optional[RAPDU]: + return self.sender.get_optional_async_response() + + def get_version(self) -> Version: + rapdu = self.sender.get_version() + return unpack_get_version_response(rapdu.data) + + def get_device_id(self) -> str: + rapdu = self.sender.get_device_id() + return rapdu.data.hex() + + def get_device_model(self) -> LedgerModel: + rapdu = self.sender.get_device_model() + return LedgerModel.unpack(raw=rapdu.data) + + @contextmanager + def __sign_rola( + self, + curve: C, + navigate_path: Callable[[], None], + navigate_sign: Callable[[], None], + path: str, + dapp_def_addr: str, + origin: str, + nonce: bytes + ) -> Generator[RAPDU, None, None]: + rola_challenge = RequestPacker.pack_rola_request( + dapp_def_addr=dapp_def_addr, + origin=origin, + nonce=nonce + ) + global maybe + with self.sender.send_sign_auth( + curve=curve, + navigate_path=navigate_path, + navigate_sign=navigate_sign, + path=path, + rola_challenge=rola_challenge + ) as response: + maybe = response + yield maybe if maybe is not None else self.sender.get_async_response() + + def sign_rola( + self, + curve: C, + path: str, + dapp_def_addr: str, + origin: str, + nonce: bytes, + navigate_path: Callable[[], None] = lambda: None, + navigate_sign: Callable[[], None] = lambda: None + ) -> ROLAResp: + """ + Forms a ROLA Challenge from (`dapp_def_addr`, `origin`, `nonce`) and signs it + using `path`. This will send two APDU requests to the Ledger, one first with + the derivation path, which requires interaction on device, before host (this Python app) + will send the next APDU request with the ROLA challenge. + + :param path: The BIP32 derivation path as a string. + :type path: str + :param dapp_def_addr: The dapp definition address, used to form ROLA challenge. + :type dapp_def_addr: str + :param origin: The origin of the Dapp sending the auth request, used to form ROLA challenge. + :type origin: str + :param nonce: The nonce sent by the Dapp sending the auth request, used to form ROLA challenge. + :type nonce: bytes + :param navigate_path: A closure passed to proceed from having sent the first APDU request. **Pass an empty closure (`lambda: None`) if you are using a physical device**. + :type navigate_path: Optional closure (`Callable`). + :param navigate_sign: A closure passed to confirm signing of the ROLA challenge, use `navigator.navigate_and_compare` to pass in interactions. **Pass an empty closure (`lambda: None`) if you are using a physical device** + :type navigate_sign: Optional closure (`Callable`) + + :return: The a ROLAEd25519Response parse from the APDU response. + :rtype: ROLAEd25519Response + """ + global response + with self.__sign_rola( + curve=curve, + navigate_path=navigate_path, + navigate_sign=navigate_sign, + path=path, + dapp_def_addr=dapp_def_addr, + origin=origin, + nonce=nonce + ) as res: + response = res + return curve.unpack_rola_response(response=response.data) + + def get_public_key( + self, + curve: C, + path: str, + ) -> PK: + """ + Get the public key of the given derivation path. + + :param path: The BIP32 derivation path as a string. + :type path: str + + :return: The public key of the given derivation path. + :rtype: PK + """ + response = self.sender.get_public_key( + curve=curve, + path=path + ) + return curve.unpack_pubkey(response.data) \ No newline at end of file diff --git a/ragger_tests/application_client/command_sender.py b/ragger_tests/application_client/command_sender.py new file mode 100644 index 00000000..53d00f37 --- /dev/null +++ b/ragger_tests/application_client/command_sender.py @@ -0,0 +1,128 @@ +from enum import IntEnum +from typing import Generator, Callable, Optional +from contextlib import contextmanager + +from ragger.backend.interface import BackendInterface, RAPDU +from ragger.bip import pack_derivation_path +from application_client.instruction_type import InsType +from application_client.curve import C + +MAX_APDU_LEN: int = 255 + +CLA: int = 0xAA +CLA2: int = 0xAC + +class P1(IntEnum): + # Parameter 1 for first APDU number. + START = 0x00 + +class P2(IntEnum): + # Parameter 2 for last APDU to receive. + LAST = 0x00 + +class CommandSender: + def __init__(self, backend: BackendInterface) -> None: + self.backend = backend + + def _send_ins( + self, + ins: InsType, + p1: P1 = P1.START, + p2: P2 = P2.LAST, + data: bytes = b"", + cla: int = CLA + ) -> RAPDU: + print(f"🛰️ sending data (exchange): {data.hex()}") + rc = self.backend.exchange(cla=cla, ins=ins, p1=p1, p2=p2, data=data) + if rc is not None: + print(f"🛰️ received data: {rc.data.hex()}") + return rc + + @contextmanager + def _async_send_ins( + self, + navigate: Callable[[], None], + ins: InsType, + p1: P1 = P1.START, + p2: P2 = P2.LAST, + data: bytes = b"", + cla: int = CLA + ) -> Generator[None, None, None]: + print(f"🛰️ sending data (exchange_async): {data.hex()}") + with self.backend.exchange_async(cla=cla, ins=ins, p1=p1, p2=p2, data=data): + navigate() + yield None + + def get_optional_async_response(self) -> Optional[RAPDU]: + return self.backend.last_async_response + + def get_async_response(self) -> RAPDU: + opt = self.get_optional_async_response() + if opt is None: + raise ValueError("No response received") + return opt + + def get_version(self) -> RAPDU: + return self._send_ins( + ins=InsType.GET_APP_VERSION, + ) + + def get_device_id(self) -> RAPDU: + return self._send_ins( + ins=InsType.GET_DEVICE_ID, + ) + + def get_device_model(self) -> RAPDU: + return self._send_ins( + ins=InsType.GET_DEVICE_MODEL, + ) + + def get_public_key( + self, + curve: C, + path: str + ) -> RAPDU: + return self._send_ins( + ins=curve.ins_get_pubkey(), + data=pack_derivation_path(path) + ) + + def _send_derivation_path( + self, + navigate: Callable[[], None], + ins: InsType, + path: str + ): + with self._async_send_ins( + navigate=navigate, + ins=ins, + data=pack_derivation_path(path) + ): + pass + + @contextmanager + def send_sign_auth( + self, + curve: C, + navigate_path: Callable[[], None], + navigate_sign: Callable[[], None], + path: str, + rola_challenge: bytes + ): + ins = curve.ins_sign_rola() + self._send_derivation_path( + navigate=navigate_path, + ins=ins, + path=path + ) + + self.backend._last_async_response = None + + with self._async_send_ins( + navigate=navigate_sign, + ins=ins, + data=rola_challenge, + cla=CLA2 # Continuation + ): + yield self.get_optional_async_response() + \ No newline at end of file diff --git a/ragger_tests/application_client/curve.py b/ragger_tests/application_client/curve.py new file mode 100644 index 00000000..be332908 --- /dev/null +++ b/ragger_tests/application_client/curve.py @@ -0,0 +1,118 @@ +from abc import ABC, abstractmethod +from typing import TypeVar + +from application_client.instruction_type import InsType +from response_unpacker import PK, Curve25519PublicKey, ROLAResponseEd25519, ROLAResponseSecp256k1, ROLAResp, Secp256k1PublicKey + +class Curve(ABC): + + @classmethod + @abstractmethod + def unpack_pubkey(cls, response: bytes) -> PK: + pass + + @classmethod + @abstractmethod + def unpack_rola_response(cls, response: bytes) -> ROLAResp: + pass + + @classmethod + @abstractmethod + def ins_sign_rola(cls) -> InsType: + pass + + @classmethod + @abstractmethod + def ins_sign_tx(cls) -> InsType: + pass + + @classmethod + @abstractmethod + def ins_get_pubkey(cls) -> InsType: + pass + + @classmethod + @abstractmethod + def ins_verify_address(cls) -> InsType: + pass + + @classmethod + @abstractmethod + def ins_sign_pre_auth_hash(cls) -> InsType: + pass + + @classmethod + @abstractmethod + def ins_sign_pre_auth_raw(cls) -> InsType: + pass + +class SECP256K1(Curve): + + @classmethod + def ins_sign_rola(cls) -> InsType: + return InsType.SIGN_AUTH_SECP256K1 + + @classmethod + def ins_sign_tx(cls) -> InsType: + return InsType.SIGN_TX_SECP256K1 + + @classmethod + def ins_get_pubkey(cls) -> InsType: + return InsType.GET_PUB_KEY_SECP256K1 + + @classmethod + def ins_verify_address(cls) -> InsType: + return InsType.VERIFY_ADDRESS_SECP256K1 + + @classmethod + def ins_sign_pre_auth_hash(cls) -> InsType: + return InsType.SIGN_PRE_AUTH_HASH_SECP256K1 + + @classmethod + def ins_sign_pre_auth_raw(cls) -> InsType: + return InsType.SIGN_PRE_AUTH_RAW_SECP256K1 + + @classmethod + def unpack_rola_response(cls, response: bytes) -> ROLAResponseSecp256k1: + return ROLAResponseSecp256k1.unpack_response(response) + + @classmethod + def unpack_pubkey(cls, response: bytes) -> PK: + return Secp256k1PublicKey.unpack(response) + + +class Curve25519(Curve): + + @classmethod + def ins_sign_rola(cls) -> InsType: + return InsType.SIGN_AUTH_ED25519 + + @classmethod + def ins_sign_tx(cls) -> InsType: + return InsType.SIGN_TX_ED25519 + + @classmethod + def ins_get_pubkey(cls) -> InsType: + return InsType.GET_PUB_KEY_ED25519 + + @classmethod + def ins_verify_address(cls) -> InsType: + return InsType.VERIFY_ADDRESS_ED25519 + + @classmethod + def ins_sign_pre_auth_hash(cls) -> InsType: + return InsType.SIGN_PRE_AUTH_HASH_ED25519 + + @classmethod + def ins_sign_pre_auth_raw(cls) -> InsType: + return InsType.SIGN_PRE_AUTH_RAW_ED25519 + + @classmethod + def unpack_rola_response(cls, response: bytes) -> ROLAResponseEd25519: + return ROLAResponseEd25519.unpack_response(response) + + @classmethod + def unpack_pubkey(cls, response: bytes) -> PK: + return Curve25519PublicKey.unpack(response) + +C = TypeVar('C', bound=Curve) \ No newline at end of file diff --git a/ragger_tests/application_client/instruction_type.py b/ragger_tests/application_client/instruction_type.py new file mode 100644 index 00000000..44c9f596 --- /dev/null +++ b/ragger_tests/application_client/instruction_type.py @@ -0,0 +1,20 @@ + +from enum import IntEnum + +class InsType(IntEnum): + GET_APP_VERSION = 0x10 + GET_DEVICE_MODEL = 0x11 + GET_DEVICE_ID = 0x12 + GET_APP_SETTINGS = 0x22 + GET_PUB_KEY_ED25519 = 0x21 + GET_PUB_KEY_SECP256K1 = 0x31 + SIGN_TX_ED25519 = 0x41 + SIGN_TX_SECP256K1 = 0x51 + SIGN_AUTH_ED25519 = 0x61 + SIGN_AUTH_SECP256K1 = 0x71 + VERIFY_ADDRESS_ED25519 = 0x81 + VERIFY_ADDRESS_SECP256K1 = 0x91 + SIGN_PRE_AUTH_HASH_ED25519 = 0xA1 + SIGN_PRE_AUTH_HASH_SECP256K1 = 0xA2 + SIGN_PRE_AUTH_RAW_ED25519 = 0xA3 + SIGN_PRE_AUTH_RAW_SECP256K1 = 0xA4 \ No newline at end of file diff --git a/ragger_tests/application_client/py.typed b/ragger_tests/application_client/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/ragger_tests/application_client/request_packer.py b/ragger_tests/application_client/request_packer.py new file mode 100644 index 00000000..c2bbe44a --- /dev/null +++ b/ragger_tests/application_client/request_packer.py @@ -0,0 +1,6 @@ +class RequestPacker: + @classmethod + def pack_rola_request(self, dapp_def_addr: str, origin: str, nonce: bytes) -> bytes: + addr_length = len(dapp_def_addr).to_bytes(1, 'little').hex() + data = nonce + addr_length + dapp_def_addr.encode('utf-8').hex() + origin.encode('utf-8').hex() + return bytes.fromhex(data) \ No newline at end of file diff --git a/ragger_tests/application_client/response_unpacker.py b/ragger_tests/application_client/response_unpacker.py new file mode 100644 index 00000000..e82460f7 --- /dev/null +++ b/ragger_tests/application_client/response_unpacker.py @@ -0,0 +1,219 @@ +from __future__ import annotations +from dataclasses import dataclass +from enum import IntEnum +from typing import NamedTuple, Tuple, TypeVar +from struct import unpack +from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey +from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicKey, ECDSA, SECP256K1 +from cryptography.hazmat.primitives.asymmetric import ec, utils +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives._serialization import Encoding, PublicFormat +from abc import ABC, abstractmethod + + +PK = TypeVar('PK', bound="IsPublicKey") + +class IsPublicKey(ABC): + @abstractmethod + def verify_signature(self, signature: bytes, hash: bytes) -> bool: + pass + + @abstractmethod + def serialize(self) -> bytes: + pass + + @classmethod + @abstractmethod + def unpack(cls, raw: bytes) -> PK: + pass + +@dataclass +class Curve25519PublicKey(IsPublicKey): + wrapped_key: Ed25519PublicKey + + def serialize(self) -> bytes: + return self.wrapped_key.public_bytes_raw() + + def verify_signature(self, signature: bytes, hash: bytes) -> bool: + try: + self.wrapped_key.verify(signature=signature, data=hash) + return True + except Exception as e: + raise ValueError("Invalid signature: ", e) + + @classmethod + def unpack(cls, raw: bytes) -> Curve25519PublicKey: + wrapped_key = Ed25519PublicKey.from_public_bytes(data=raw) + return Curve25519PublicKey(wrapped_key=wrapped_key) + + +@dataclass +class Secp256k1PublicKey(IsPublicKey): + wrapped_key: EllipticCurvePublicKey + + def serialize(self) -> bytes: + return self.wrapped_key.public_bytes(Encoding.X962, PublicFormat.CompressedPoint) + + def verify_signature(self, signature: bytes, hash: bytes) -> bool: + try: + self.wrapped_key.verify( + signature, + hash, + ec.ECDSA(utils.Prehashed(hashes.SHA256())) + ) + return True + except Exception as e: + raise ValueError("Invalid signature: ", e) + + @classmethod + def unpack(cls, raw: bytes) -> Secp256k1PublicKey: + wrapped_key = ec.EllipticCurvePublicKey.from_encoded_point(curve=SECP256K1(), data=raw) + return Secp256k1PublicKey(wrapped_key=wrapped_key) + + +class Version(NamedTuple): + major: int + minor: int + patch: int + +@dataclass +class ROLAResponse(ABC): + key: PK + hash: bytes + signature: bytes + + def verify_signature(self) -> bool: + return self.key.verify_signature(signature=self.signature, hash=self.hash) + + @classmethod + @abstractmethod + def unpack_key(cls, raw: bytes) -> PK: + pass + + @classmethod + @abstractmethod + def unpack_signature(cls, raw: bytes) -> bytes: + pass + + @classmethod + @abstractmethod + def sig_len(cls) -> int: + pass + + @classmethod + @abstractmethod + def key_len(cls) -> int: + pass + + @classmethod + def hash_len(cls) -> int: + return 32 + + @classmethod + @abstractmethod + def expected_len(cls) -> int: + pass + + @classmethod + @abstractmethod + def unpack_response(cls, response: bytes) -> ROLAResp: + pass + + @classmethod + def raw_unpack_response(cls, response: bytes) -> Tuple[PK, bytes, bytes]: + expected_byte_count = cls.expected_len() + byte_count = len(response) + if byte_count != expected_byte_count: + raise ValueError(f"Invalid length, expected #{expected_byte_count} bytes, got: #{byte_count}") + signature_bytes = response[0:cls.sig_len()] + key_start = cls.sig_len() + key_end = key_start + cls.key_len() + keybytes = response[key_start:key_end] + hash_start = key_end + hash_end = hash_start + cls.hash_len() + hash = response[hash_start:hash_end] + assert len(signature_bytes) == cls.sig_len() + assert len(keybytes) == cls.key_len() + assert len(hash) == cls.hash_len() + + signature = cls.unpack_signature(raw=signature_bytes) + key = cls.unpack_key(keybytes) + + return key, hash, signature + +class ROLAResponseEd25519(ROLAResponse): + key: Curve25519PublicKey + + @classmethod + def unpack_key(cls, raw: bytes) -> Curve25519PublicKey: + return Curve25519PublicKey.unpack(raw=raw) + + @classmethod + def unpack_signature(cls, raw: bytes) -> bytes: + return raw # no parsing needed + + @classmethod + def sig_len(cls) -> int: + return 64 + + @classmethod + def key_len(cls) -> int: + return 32 + + @classmethod + def expected_len(cls) -> int: + return cls.sig_len() + cls.key_len() + cls.hash_len() + + @classmethod + def unpack_response(cls, response: bytes) -> ROLAResp: + (key, hash, signature) = cls.raw_unpack_response(response) + return ROLAResponseEd25519(key=key, hash=hash, signature=signature) + +class ROLAResponseSecp256k1(ROLAResponse): + key: EllipticCurvePublicKey + + @classmethod + def unpack_key(cls, raw: bytes) -> Secp256k1PublicKey: + return Secp256k1PublicKey.unpack(raw=raw) + + @classmethod + def unpack_signature(cls, raw: bytes) -> bytes: + if len(raw) != cls.sig_len(): + raise ValueError("Invalid signature length") + r = int.from_bytes(raw[1:33], byteorder='big', signed=False) + s = int.from_bytes(raw[33:65], byteorder='big', signed=False) + return utils.encode_dss_signature(r, s) + + @classmethod + def unpack_response(cls, response: bytes) -> ROLAResp: + (key, hash, signature) = cls.raw_unpack_response(response) + return ROLAResponseSecp256k1(key=key, hash=hash, signature=signature) + + @classmethod + def expected_len(cls) -> int: + return cls.sig_len() + cls.key_len() + cls.hash_len() + + @classmethod + def sig_len(cls) -> int: + return 65 + + @classmethod + def key_len(cls) -> int: + return 33 + +ROLAResp = TypeVar('ROLAResp', bound=ROLAResponse) + +def unpack_get_version_response(response: bytes) -> Version: + assert len(response) == 3 + major, minor, patch = unpack("BBB", response) + return Version(major=major, minor=minor, patch=patch) + +class LedgerModel(IntEnum): + NANO_S = 0x00 + NANO_S_PLUS = 0x01 + NANO_X = 0x02 + STAX = 0x04 + + def unpack(raw: bytes) -> LedgerModel: + int_val = int.from_bytes(raw, byteorder='big', signed=False) + return LedgerModel(int_val) diff --git a/ragger_tests/conftest.py b/ragger_tests/conftest.py index 4e98c116..6e4e83b6 100644 --- a/ragger_tests/conftest.py +++ b/ragger_tests/conftest.py @@ -1,4 +1,7 @@ from ragger.conftest import configuration +import pytest +from ragger.navigator import NavInsID +from ragger.navigator.navigator import Navigator ########################### ### CONFIGURATION START ### @@ -15,3 +18,24 @@ # Pull all features from the base ragger conftest using the overridden configuration pytest_plugins = ("ragger.conftest.base_conftest",) + + +# Notes : +# 1. Remove this fixture once the pending review screen is removed from the app +# 2. This fixture clears the pending review screen before each test +# 3. The scope should be the same as the one configured by BACKEND_SCOPE in +# ragger/conftest/configuration.py +@pytest.fixture(scope="class", autouse=True) +def clear_pending_review(firmware, navigator: Navigator): + # Press a button to clear the pending review + if firmware.device.startswith("nano"): + if navigator._backend.compare_screen_with_text("Pending"): + print("Clearing pending review") + instructions = [ + NavInsID.BOTH_CLICK, + ] + navigator.navigate(instructions,screen_change_before_first_instruction=False) + else: + print("No pending review to clear") + else: + print("Not Nano") \ No newline at end of file diff --git a/ragger_tests/requirements.txt b/ragger_tests/requirements.txt index 338490c0..11eaa921 100644 --- a/ragger_tests/requirements.txt +++ b/ragger_tests/requirements.txt @@ -1,5 +1,6 @@ pytest -ragger[speculos,ledgerwallet]>=1.11.4 ecdsa>=0.16.1,<0.17.0 -pysha3>=1.0.0,<2.0.0 +safe-pysha3>=1.0.0,<2.0.0 cryptography >= 39.0.2 +tomli>=2.0.1 +ragger[speculos,ledgerwallet,ledgercomm]>=1.21.1 \ No newline at end of file diff --git a/ragger_tests/setup.cfg b/ragger_tests/setup.cfg index 7d0d7e30..30648adc 100644 --- a/ragger_tests/setup.cfg +++ b/ragger_tests/setup.cfg @@ -1,5 +1,8 @@ [tool:pytest] addopts = --strict-markers +filterwarnings = + ignore::DeprecationWarning + [pylint] disable = C0114, # missing-module-docstring diff --git a/ragger_tests/test-physical-device-nanos-debug.sh b/ragger_tests/test-physical-device-nanos-debug.sh new file mode 100755 index 00000000..f383c83f --- /dev/null +++ b/ragger_tests/test-physical-device-nanos-debug.sh @@ -0,0 +1 @@ +pytest -v --tb=short --backend ledgerwallet --device nanos $@ diff --git a/ragger_tests/test-physical-device-nanos.sh b/ragger_tests/test-physical-device-nanos.sh new file mode 100755 index 00000000..f383c83f --- /dev/null +++ b/ragger_tests/test-physical-device-nanos.sh @@ -0,0 +1 @@ +pytest -v --tb=short --backend ledgerwallet --device nanos $@ diff --git a/ragger_tests/test-physical-device-nanosplus.sh b/ragger_tests/test-physical-device-nanosplus.sh new file mode 100755 index 00000000..df7591c2 --- /dev/null +++ b/ragger_tests/test-physical-device-nanosplus.sh @@ -0,0 +1 @@ +pytest -v --tb=short --backend ledgerwallet --device nanosp $@ diff --git a/ragger_tests/test-physical-device-nanox.sh b/ragger_tests/test-physical-device-nanox.sh new file mode 100755 index 00000000..059e7ed9 --- /dev/null +++ b/ragger_tests/test-physical-device-nanox.sh @@ -0,0 +1 @@ +pytest -v --tb=short --backend ledgerwallet --device nanox $@ diff --git a/ragger_tests/test-nanos-debug.sh b/ragger_tests/test-simulator-nanos-debug.sh similarity index 63% rename from ragger_tests/test-nanos-debug.sh rename to ragger_tests/test-simulator-nanos-debug.sh index f566cff1..aeeb45e3 100755 --- a/ragger_tests/test-nanos-debug.sh +++ b/ragger_tests/test-simulator-nanos-debug.sh @@ -1,3 +1,3 @@ mkdir -p ../build/nanos/bin/ cp ../target/nanos/debug/babylon-ledger-app ../build/nanos/bin/app.elf -pytest -v --tb=short --device nanos $@ +pytest -v --backend speculos --tb=short --device nanos $@ diff --git a/ragger_tests/test-nanos.sh b/ragger_tests/test-simulator-nanos.sh similarity index 63% rename from ragger_tests/test-nanos.sh rename to ragger_tests/test-simulator-nanos.sh index dd9e187d..1ef9d7fc 100755 --- a/ragger_tests/test-nanos.sh +++ b/ragger_tests/test-simulator-nanos.sh @@ -1,3 +1,3 @@ mkdir -p ../build/nanos/bin/ cp ../target/nanos/release/babylon-ledger-app ../build/nanos/bin/app.elf -pytest -v --tb=short --device nanos $@ +pytest -v --backend speculos --tb=short --device nanos $@ diff --git a/ragger_tests/test-nanosplus.sh b/ragger_tests/test-simulator-nanosplus.sh similarity index 64% rename from ragger_tests/test-nanosplus.sh rename to ragger_tests/test-simulator-nanosplus.sh index ae7aff29..3bb75a01 100755 --- a/ragger_tests/test-nanosplus.sh +++ b/ragger_tests/test-simulator-nanosplus.sh @@ -1,3 +1,3 @@ mkdir -p ../build/nanos2/bin/ cp ../target/nanosplus/release/babylon-ledger-app ../build/nanos2/bin/app.elf -pytest -v --tb=short --device nanosp $@ +pytest -v --backend speculos --tb=short --device nanosp $@ diff --git a/ragger_tests/test-nanox.sh b/ragger_tests/test-simulator-nanox.sh similarity index 63% rename from ragger_tests/test-nanox.sh rename to ragger_tests/test-simulator-nanox.sh index 4b0ca9bc..d8c69518 100755 --- a/ragger_tests/test-nanox.sh +++ b/ragger_tests/test-simulator-nanox.sh @@ -1,3 +1,3 @@ mkdir -p ../build/nanox/bin/ cp ../target/nanox/release/babylon-ledger-app ../build/nanox/bin/app.elf -pytest -v --tb=short --device nanox $@ +pytest -v --backend speculos --tb=short --device nanox $@ diff --git a/ragger_tests/test_get_app_version.py b/ragger_tests/test_get_app_version.py index 0ad8685b..50757f24 100644 --- a/ragger_tests/test_get_app_version.py +++ b/ragger_tests/test_get_app_version.py @@ -1,6 +1,23 @@ -CLA = 0xAA -INS = 0x10 +from application_client.app import App +from application_client.response_unpacker import Version +from ragger.backend.interface import BackendInterface +def get_version_from_cargo_toml(): + import tomli + import os + from pathlib import Path + DIR = Path(os.path.dirname(os.path.abspath(__file__))) + PARENT_DIR = DIR.parent + CARGO_TOML_DIR = os.path.join(PARENT_DIR, 'Cargo.toml') + global version + with open(CARGO_TOML_DIR, "rb") as f: + data = tomli.load(f) + major, minor, patch = tuple(map(int, data['package']['version'].split('.'))) + version = Version(major=major, minor=minor, patch=patch) + return version -def test_get_version(backend): - assert backend.exchange(cla=CLA, ins=INS).data.hex() == "00071d" +def test_get_version(backend: BackendInterface): + expected = get_version_from_cargo_toml() + app = App(backend) + actual = app.get_version() + assert actual == expected \ No newline at end of file diff --git a/ragger_tests/test_get_device_id.py b/ragger_tests/test_get_device_id.py index 23119d74..7679975f 100644 --- a/ragger_tests/test_get_device_id.py +++ b/ragger_tests/test_get_device_id.py @@ -1,6 +1,10 @@ -CLA = 0xAA -INS = 0x12 +from application_client.app import App +from ragger.backend.interface import BackendInterface - -def test_get_version(backend): - assert backend.exchange(cla=CLA, ins=INS).data.hex() == "41ac202687326a4fc6cb677e9fd92d08b91ce46c669950d58790d4d5e583adc0" +def test_get_device_id(backend: BackendInterface): + app = App(backend) + actual = app.get_device_id() + # The expected blake2b hash of public key at m/44'/365' - of mnemonic + # `"equip will roof matter pink blind book anxiety banner elbow sun young"` + expected = "41ac202687326a4fc6cb677e9fd92d08b91ce46c669950d58790d4d5e583adc0" + assert actual == expected, "Wrong DeviceID, maybe you are running on a Ledger which does not have mnemonic `equip will roof matter pink blind book anxiety banner elbow sun young` setup?" diff --git a/ragger_tests/test_get_device_model.py b/ragger_tests/test_get_device_model.py index b8d1ea93..d602f711 100644 --- a/ragger_tests/test_get_device_model.py +++ b/ragger_tests/test_get_device_model.py @@ -1,6 +1,7 @@ -CLA = 0xAA -INS = 0x11 +from ragger.backend.interface import BackendInterface +from ragger_tests.application_client.app import App +from ragger_tests.application_client.response_unpacker import LedgerModel - -def test_get_device_model(backend): - assert backend.exchange(cla=CLA, ins=INS).data.hex() in ["00", "01", "02", "04"] +def test_get_device_model(backend: BackendInterface): + model = App(backend).get_device_model() + assert model in [LedgerModel.NANO_X, LedgerModel.NANO_S, LedgerModel.NANO_S_PLUS, LedgerModel.STAX] diff --git a/ragger_tests/test_get_public_key_ed25519.py b/ragger_tests/test_get_public_key_ed25519.py index 5399bf23..59c328b7 100644 --- a/ragger_tests/test_get_public_key_ed25519.py +++ b/ragger_tests/test_get_public_key_ed25519.py @@ -1,7 +1,8 @@ -from ragger.bip import pack_derivation_path +from typing import Tuple -CLA = 0xAA -INS = 0x21 +from ragger_tests.application_client.app import App +from ragger_tests.application_client.curve import C, Curve25519 +from ragger.backend.interface import BackendInterface test_vectors = [ ("m/44'/1022'/12'/525'/1460'/0'", "451152a1cef7be603205086d4ebac0a0b78fda2ff4684b9dea5ca9ef003d4e7d"), @@ -30,105 +31,99 @@ ("m/44'/1022'/12'/618'/1391'/3'", "7fa1d448ef608a6e1a8533d816367b4fa0d60c39844bb82dbce1ea266105a275"), ] - -def call_and_check(backend, vector): +def do_test_get_public_key( + curve: C, + backend: BackendInterface, + vector: Tuple[str, str] +): path, expected_pub_key = vector - response = backend.exchange(cla=CLA, ins=INS, data=pack_derivation_path(path)).data - pk = response.hex() - assert pk == expected_pub_key, "Invalid public key\nExpected: " + expected_pub_key + "\nReceived: " + pk + app = App(backend) + response = app.get_public_key( + curve=curve, + path=path, + ) + assert response.serialize().hex() == expected_pub_key -def test_get_public_key_ed25519_0(backend): - call_and_check(backend, test_vectors[0]) +def do_test_get_public_key_ed25519( + backend: BackendInterface, + vector: Tuple[str, str] +): + do_test_get_public_key( + curve=Curve25519, + backend=backend, + vector=vector + ) -def test_get_public_key_ed25519_1(backend): - call_and_check(backend, test_vectors[1]) +def test_get_public_key_ed25519_0(backend): + do_test_get_public_key_ed25519(backend, test_vectors[0]) +def test_get_public_key_ed25519_1(backend): + do_test_get_public_key_ed25519(backend, test_vectors[1]) def test_get_public_key_ed25519_2(backend): - call_and_check(backend, test_vectors[2]) - + do_test_get_public_key_ed25519(backend, test_vectors[2]) def test_get_public_key_ed25519_3(backend): - call_and_check(backend, test_vectors[3]) - + do_test_get_public_key_ed25519(backend, test_vectors[3]) def test_get_public_key_ed25519_4(backend): - call_and_check(backend, test_vectors[4]) - + do_test_get_public_key_ed25519(backend, test_vectors[4]) def test_get_public_key_ed25519_5(backend): - call_and_check(backend, test_vectors[5]) - + do_test_get_public_key_ed25519(backend, test_vectors[5]) def test_get_public_key_ed25519_6(backend): - call_and_check(backend, test_vectors[6]) - + do_test_get_public_key_ed25519(backend, test_vectors[6]) def test_get_public_key_ed25519_7(backend): - call_and_check(backend, test_vectors[7]) - + do_test_get_public_key_ed25519(backend, test_vectors[7]) def test_get_public_key_ed25519_8(backend): - call_and_check(backend, test_vectors[8]) - + do_test_get_public_key_ed25519(backend, test_vectors[8]) def test_get_public_key_ed25519_9(backend): - call_and_check(backend, test_vectors[9]) - + do_test_get_public_key_ed25519(backend, test_vectors[9]) def test_get_public_key_ed25519_10(backend): - call_and_check(backend, test_vectors[10]) - + do_test_get_public_key_ed25519(backend, test_vectors[10]) def test_get_public_key_ed25519_11(backend): - call_and_check(backend, test_vectors[11]) - + do_test_get_public_key_ed25519(backend, test_vectors[11]) def test_get_public_key_ed25519_12(backend): - call_and_check(backend, test_vectors[12]) - + do_test_get_public_key_ed25519(backend, test_vectors[12]) def test_get_public_key_ed25519_13(backend): - call_and_check(backend, test_vectors[13]) - + do_test_get_public_key_ed25519(backend, test_vectors[13]) def test_get_public_key_ed25519_14(backend): - call_and_check(backend, test_vectors[14]) - + do_test_get_public_key_ed25519(backend, test_vectors[14]) def test_get_public_key_ed25519_15(backend): - call_and_check(backend, test_vectors[15]) - + do_test_get_public_key_ed25519(backend, test_vectors[15]) def test_get_public_key_ed25519_16(backend): - call_and_check(backend, test_vectors[16]) - + do_test_get_public_key_ed25519(backend, test_vectors[16]) def test_get_public_key_ed25519_17(backend): - call_and_check(backend, test_vectors[17]) - + do_test_get_public_key_ed25519(backend, test_vectors[17]) def test_get_public_key_ed25519_18(backend): - call_and_check(backend, test_vectors[18]) - + do_test_get_public_key_ed25519(backend, test_vectors[18]) def test_get_public_key_ed25519_19(backend): - call_and_check(backend, test_vectors[19]) - + do_test_get_public_key_ed25519(backend, test_vectors[19]) def test_get_public_key_ed25519_20(backend): - call_and_check(backend, test_vectors[20]) - + do_test_get_public_key_ed25519(backend, test_vectors[20]) def test_get_public_key_ed25519_21(backend): - call_and_check(backend, test_vectors[21]) - + do_test_get_public_key_ed25519(backend, test_vectors[21]) def test_get_public_key_ed25519_22(backend): - call_and_check(backend, test_vectors[22]) - + do_test_get_public_key_ed25519(backend, test_vectors[22]) def test_get_public_key_ed25519_23(backend): - call_and_check(backend, test_vectors[23]) + do_test_get_public_key_ed25519(backend, test_vectors[23]) diff --git a/ragger_tests/test_get_public_key_secp256k1.py b/ragger_tests/test_get_public_key_secp256k1.py index 50dc8fb6..986f9bb4 100644 --- a/ragger_tests/test_get_public_key_secp256k1.py +++ b/ragger_tests/test_get_public_key_secp256k1.py @@ -1,8 +1,9 @@ -from ragger.bip import pack_derivation_path +from typing import Tuple -CLA = 0xAA -INS = 0x31 +from ragger_tests.application_client.curve import SECP256K1 +from ragger.backend.interface import BackendInterface +from ragger_tests.test_get_public_key_ed25519 import do_test_get_public_key test_vectors = [ ("m/44'/1022'/0'/0/0'", "03f43fba6541031ef2195f5ba96677354d28147e45b40cde4662bec9162c361f55"), @@ -27,89 +28,72 @@ ("m/44'/1022'/0'/0/9", "02e5703b668deebac710118df687296e90da93c19d0db3cce656b6a677ab3e4747"), ] - -def call_and_check(backend, vector): - path, expected_pub_key = vector - response = backend.exchange(cla=CLA, ins=INS, data=pack_derivation_path(path)).data - pk = response.hex() - assert pk == expected_pub_key, "Invalid public key\nExpected: " + expected_pub_key + "\nReceived: " + pk - +def do_test_get_public_key_secp256k1( + backend: BackendInterface, + vector: Tuple[str, str] +): + do_test_get_public_key( + curve=SECP256K1, + backend=backend, + vector=vector + ) def test_get_public_key_secp256k1_0(backend): - call_and_check(backend, test_vectors[0]) - + do_test_get_public_key_secp256k1(backend, test_vectors[0]) def test_get_public_key_secp256k1_1(backend): - call_and_check(backend, test_vectors[1]) - + do_test_get_public_key_secp256k1(backend, test_vectors[1]) def test_get_public_key_secp256k1_2(backend): - call_and_check(backend, test_vectors[2]) - + do_test_get_public_key_secp256k1(backend, test_vectors[2]) def test_get_public_key_secp256k1_3(backend): - call_and_check(backend, test_vectors[3]) - + do_test_get_public_key_secp256k1(backend, test_vectors[3]) def test_get_public_key_secp256k1_4(backend): - call_and_check(backend, test_vectors[4]) - + do_test_get_public_key_secp256k1(backend, test_vectors[4]) def test_get_public_key_secp256k1_5(backend): - call_and_check(backend, test_vectors[5]) - + do_test_get_public_key_secp256k1(backend, test_vectors[5]) def test_get_public_key_secp256k1_6(backend): - call_and_check(backend, test_vectors[6]) - + do_test_get_public_key_secp256k1(backend, test_vectors[6]) def test_get_public_key_secp256k1_7(backend): - call_and_check(backend, test_vectors[7]) - + do_test_get_public_key_secp256k1(backend, test_vectors[7]) def test_get_public_key_secp256k1_8(backend): - call_and_check(backend, test_vectors[8]) - + do_test_get_public_key_secp256k1(backend, test_vectors[8]) def test_get_public_key_secp256k1_9(backend): - call_and_check(backend, test_vectors[9]) - + do_test_get_public_key_secp256k1(backend, test_vectors[9]) def test_get_public_key_secp256k1_10(backend): - call_and_check(backend, test_vectors[10]) - + do_test_get_public_key_secp256k1(backend, test_vectors[10]) def test_get_public_key_secp256k1_11(backend): - call_and_check(backend, test_vectors[11]) - + do_test_get_public_key_secp256k1(backend, test_vectors[11]) def test_get_public_key_secp256k1_12(backend): - call_and_check(backend, test_vectors[12]) - + do_test_get_public_key_secp256k1(backend, test_vectors[12]) def test_get_public_key_secp256k1_13(backend): - call_and_check(backend, test_vectors[13]) - + do_test_get_public_key_secp256k1(backend, test_vectors[13]) def test_get_public_key_secp256k1_14(backend): - call_and_check(backend, test_vectors[14]) - + do_test_get_public_key_secp256k1(backend, test_vectors[14]) def test_get_public_key_secp256k1_15(backend): - call_and_check(backend, test_vectors[15]) - + do_test_get_public_key_secp256k1(backend, test_vectors[15]) def test_get_public_key_secp256k1_16(backend): - call_and_check(backend, test_vectors[16]) - + do_test_get_public_key_secp256k1(backend, test_vectors[16]) def test_get_public_key_secp256k1_17(backend): - call_and_check(backend, test_vectors[17]) - + do_test_get_public_key_secp256k1(backend, test_vectors[17]) def test_get_public_key_secp256k1_18(backend): - call_and_check(backend, test_vectors[18]) - + do_test_get_public_key_secp256k1(backend, test_vectors[18]) def test_get_public_key_secp256k1_19(backend): - call_and_check(backend, test_vectors[19]) + do_test_get_public_key_secp256k1(backend, test_vectors[19]) \ No newline at end of file diff --git a/ragger_tests/test_sign_auth_ed25519.py b/ragger_tests/test_sign_auth_ed25519.py index 20cb1bcf..77026678 100644 --- a/ragger_tests/test_sign_auth_ed25519.py +++ b/ragger_tests/test_sign_auth_ed25519.py @@ -1,49 +1,76 @@ -from typing import Generator +from typing import Tuple from pathlib import Path -from ragger.bip import pack_derivation_path from ragger.navigator import NavInsID -from contextlib import contextmanager -from cryptography.hazmat.primitives.asymmetric import ed25519 +from ragger.backend.interface import BackendInterface +from ragger.firmware.structs import Firmware +from ragger.navigator.navigator import Navigator -ROOT_SCREENSHOT_PATH = Path(__file__).parent.resolve() - -CLA1 = 0xAA -CLA2 = 0xAC -INS = 0x61 +from application_client.app import App +from ragger_tests.application_client.curve import C, Curve25519 +ROOT_SCREENSHOT_PATH = Path(__file__).parent.resolve() -def send_derivation_path(backend, path, navigator): - with backend.exchange_async(cla=CLA1, ins=INS, data=pack_derivation_path(path)) as response: +def sign_auth( + curve: C, + path: str, + firmware: Firmware, + backend: BackendInterface, + navigator: Navigator, + test_name: str, + vector: Tuple[str, str, str, str] +): + expected_hash = vector[0] + dapp_def_addr = vector[1] + origin = vector[2] + nonce = vector[3] + + def navigate_path(): navigator.navigate([NavInsID.RIGHT_CLICK]) - -@contextmanager -def send_auth_request(backend, daddr, origin, nonce) -> Generator[None, None, None]: - addr_length = len(daddr).to_bytes(1, 'little').hex() - data = nonce + addr_length + daddr.encode('utf-8').hex() + origin.encode('utf-8').hex() - - with backend.exchange_async(cla=CLA2, ins=INS, data=bytes.fromhex(data)) as response: - yield response - - -def sign_auth_ed25519(firmware, backend, navigator, test_name, vector): - send_derivation_path(backend, "m/44'/1022'/12'/525'/1460'/0'", navigator) - - with send_auth_request(backend, vector[1], vector[2], vector[3]): + def navigate_sign(): if firmware.device.startswith("nano"): - navigator.navigate_and_compare(ROOT_SCREENSHOT_PATH, test_name, - [NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK, - NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK, - NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK, - NavInsID.RIGHT_CLICK, NavInsID.BOTH_CLICK]) - - rc = backend.last_async_response.data - pubkey = ed25519.Ed25519PublicKey.from_public_bytes(bytes(rc[64:96])) - try: - pubkey.verify(bytes(rc[0:64]), bytes(rc[96:128])) - except Exception as e: - print("Invalid signature ", e) - + navigator.navigate_and_compare( + path=ROOT_SCREENSHOT_PATH, + test_case_name=test_name, + instructions=[ + NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK, + NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK, + NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK, + NavInsID.RIGHT_CLICK, NavInsID.BOTH_CLICK + ], + timeout=20, + ) + + app = App(backend) + response = app.sign_rola( + curve=curve, + path=path, + dapp_def_addr=dapp_def_addr, + origin=origin, + nonce=nonce, + navigate_path=navigate_path, + navigate_sign=navigate_sign, + ) + + assert response.hash.hex() == expected_hash + assert response.verify_signature() + +def sign_auth_ed25519( + firmware: Firmware, + backend: BackendInterface, + navigator: Navigator, + test_name: str, + vector: Tuple[str, str, str, str] +): + sign_auth( + curve=Curve25519, + path="m/44'/1022'/12'/525'/1460'/0'", + firmware=firmware, + backend=backend, + navigator=navigator, + test_name=test_name, + vector=vector + ) test_vectors = [ ( @@ -102,38 +129,29 @@ def sign_auth_ed25519(firmware, backend, navigator, test_name, vector): ), ] - def test_sign_auth_ed25519_0(firmware, backend, navigator, test_name): sign_auth_ed25519(firmware, backend, navigator, test_name, test_vectors[0]) - def test_sign_auth_ed25519_1(firmware, backend, navigator, test_name): sign_auth_ed25519(firmware, backend, navigator, test_name, test_vectors[1]) - def test_sign_auth_ed25519_2(firmware, backend, navigator, test_name): sign_auth_ed25519(firmware, backend, navigator, test_name, test_vectors[2]) - def test_sign_auth_ed25519_3(firmware, backend, navigator, test_name): sign_auth_ed25519(firmware, backend, navigator, test_name, test_vectors[3]) - def test_sign_auth_ed25519_4(firmware, backend, navigator, test_name): sign_auth_ed25519(firmware, backend, navigator, test_name, test_vectors[4]) - def test_sign_auth_ed25519_5(firmware, backend, navigator, test_name): sign_auth_ed25519(firmware, backend, navigator, test_name, test_vectors[5]) - def test_sign_auth_ed25519_6(firmware, backend, navigator, test_name): sign_auth_ed25519(firmware, backend, navigator, test_name, test_vectors[6]) - def test_sign_auth_ed25519_7(firmware, backend, navigator, test_name): sign_auth_ed25519(firmware, backend, navigator, test_name, test_vectors[7]) - def test_sign_auth_ed25519_8(firmware, backend, navigator, test_name): - sign_auth_ed25519(firmware, backend, navigator, test_name, test_vectors[8]) + sign_auth_ed25519(firmware, backend, navigator, test_name, test_vectors[8]) \ No newline at end of file diff --git a/ragger_tests/test_sign_auth_secp256k1.py b/ragger_tests/test_sign_auth_secp256k1.py index 908f2437..fc1fbbf9 100644 --- a/ragger_tests/test_sign_auth_secp256k1.py +++ b/ragger_tests/test_sign_auth_secp256k1.py @@ -1,56 +1,32 @@ -from typing import Generator from pathlib import Path -from ragger.bip import pack_derivation_path -from ragger.navigator import NavInsID -from contextlib import contextmanager +from typing import Tuple from cryptography.hazmat.primitives.asymmetric import ec, utils from cryptography.hazmat.primitives import hashes - -ROOT_SCREENSHOT_PATH = Path(__file__).parent.resolve() - -CLA1 = 0xAA -CLA2 = 0xAC -INS = 0x71 - - -def send_derivation_path(backend, path, navigator): - with backend.exchange_async(cla=CLA1, ins=INS, data=pack_derivation_path(path)) as response: - navigator.navigate([NavInsID.RIGHT_CLICK]) - - -@contextmanager -def send_auth_request(backend, daddr, origin, nonce) -> Generator[None, None, None]: - addr_length = len(daddr).to_bytes(1, 'little').hex() - data = nonce + addr_length + daddr.encode('utf-8').hex() + origin.encode('utf-8').hex() - - with backend.exchange_async(cla=CLA2, ins=INS, data=bytes.fromhex(data)) as response: - yield response - - -def sign_auth_secp256k1(firmware, backend, navigator, test_name, vector): - send_derivation_path(backend, "m/44'/1022'/10'/525'/1238'", navigator) - - with send_auth_request(backend, vector[1], vector[2], vector[3]): - if firmware.device.startswith("nano"): - navigator.navigate_and_compare(ROOT_SCREENSHOT_PATH, test_name, - [NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK, - NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK, - NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK, - NavInsID.RIGHT_CLICK, NavInsID.BOTH_CLICK]) - - rc = backend.last_async_response.data - r = int.from_bytes(rc[1:33], byteorder='big', signed=False) - s = int.from_bytes(rc[33:65], byteorder='big', signed=False) - signature = utils.encode_dss_signature(int(r), int(s)) - pubkey = ec.EllipticCurvePublicKey.from_encoded_point(ec.SECP256K1(), bytes(rc[65:98])) - try: - # Note that Prehashed parameter is irrelevant here, we just need to pass something known to the library - pubkey.verify(signature, bytes(rc[98:130]), ec.ECDSA(utils.Prehashed(hashes.SHA256()))) - print("Success") - assert rc[98:130].hex() == vector[0], "Invalid calculated hash\nExpected: " + vector[0] + "\nReceived: " + rc[98:130].hex() - except Exception as e: - print("Invalid signature ", e) - +from ragger.navigator import NavInsID +from ragger.backend.interface import BackendInterface +from ragger.firmware.structs import Firmware +from ragger.navigator.navigator import Navigator +from application_client.app import App +from ragger_tests.application_client.curve import C, SECP256K1 +from ragger_tests.test_sign_auth_ed25519 import sign_auth + + +def sign_auth_secp256k1( + firmware: Firmware, + backend: BackendInterface, + navigator: Navigator, + test_name: str, + vector: Tuple[str, str, str, str] +): + sign_auth( + curve=SECP256K1, + path="m/44'/1022'/10'/525'/1238'", + firmware=firmware, + backend=backend, + navigator=navigator, + test_name=test_name, + vector=vector + ) test_vectors = [ ( @@ -113,34 +89,26 @@ def sign_auth_secp256k1(firmware, backend, navigator, test_name, vector): def test_sign_auth_secp256k1_0(firmware, backend, navigator, test_name): sign_auth_secp256k1(firmware, backend, navigator, test_name, test_vectors[0]) - def test_sign_auth_secp256k1_1(firmware, backend, navigator, test_name): sign_auth_secp256k1(firmware, backend, navigator, test_name, test_vectors[1]) - def test_sign_auth_secp256k1_2(firmware, backend, navigator, test_name): sign_auth_secp256k1(firmware, backend, navigator, test_name, test_vectors[2]) - def test_sign_auth_secp256k1_3(firmware, backend, navigator, test_name): sign_auth_secp256k1(firmware, backend, navigator, test_name, test_vectors[3]) - def test_sign_auth_secp256k1_4(firmware, backend, navigator, test_name): sign_auth_secp256k1(firmware, backend, navigator, test_name, test_vectors[4]) - def test_sign_auth_secp256k1_5(firmware, backend, navigator, test_name): sign_auth_secp256k1(firmware, backend, navigator, test_name, test_vectors[5]) - def test_sign_auth_secp256k1_6(firmware, backend, navigator, test_name): sign_auth_secp256k1(firmware, backend, navigator, test_name, test_vectors[6]) - def test_sign_auth_secp256k1_7(firmware, backend, navigator, test_name): sign_auth_secp256k1(firmware, backend, navigator, test_name, test_vectors[7]) - def test_sign_auth_secp256k1_8(firmware, backend, navigator, test_name): sign_auth_secp256k1(firmware, backend, navigator, test_name, test_vectors[8]) diff --git a/test/test-all.sh b/test/test-all.sh index a07e76f4..c1f10017 100755 --- a/test/test-all.sh +++ b/test/test-all.sh @@ -1,5 +1,4 @@ # Run all non-interactive tests (Debug binaries) -python3 -m test-get-application-version python3 -m test-get-device-model python3 -m test-get-device-id python3 -m test-get-public-key-ed25519 diff --git a/test/test-get-application-version.py b/test/test-get-application-version.py deleted file mode 100755 index 5d18c583..00000000 --- a/test/test-get-application-version.py +++ /dev/null @@ -1,27 +0,0 @@ -# Test GetAppVersion instruction - -import sys -import os - -# disable printing stack trace -sys.tracebacklimit = 0 - -from ledgerblue.comm import getDongle -from ledgerblue.commTCP import getDongle as getDongleTCP - -if os.environ.get('USE_SPECULOS') is not None: - dongle = getDongleTCP(debug=False) -else: - dongle = getDongle(False) - -instructionClass = "AA" -instructionCode = "10" -p1 = "00" -p2 = "00" -dataLength = "00" - -print("Testing", "GetAppVersion", instructionCode, end=" ") -response = dongle.exchange(bytes.fromhex(instructionClass + instructionCode + p1 + p2 + dataLength)) - -assert response.hex() == '00071d', "Invalid version\nReceived:" + response.hex() -print("Success") diff --git a/test/test-get-device-id.py b/test/test-get-device-id.py deleted file mode 100755 index 2cf945c0..00000000 --- a/test/test-get-device-id.py +++ /dev/null @@ -1,27 +0,0 @@ -# Test GetDeviceId instruction - -import sys -import os - -# disable printing stack trace -sys.tracebacklimit = 0 - -from ledgerblue.comm import getDongle -from ledgerblue.commTCP import getDongle as getDongleTCP - -if os.environ.get('USE_SPECULOS') is not None: - dongle = getDongleTCP(debug=False) -else: - dongle = getDongle(False) - -instructionClass = "AA" -instructionCode = "12" -p1 = "00" -p2 = "00" -dataLength = "00" - -print("Testing", "GetDeviceId", instructionCode, end=" ") -response = dongle.exchange(bytes.fromhex(instructionClass + instructionCode + p1 + p2 + dataLength)) - -assert response.hex() == '41ac202687326a4fc6cb677e9fd92d08b91ce46c669950d58790d4d5e583adc0', "Invalid device ID\nReceived:" + response.hex() -print("Success") diff --git a/test/test-get-device-model.py b/test/test-get-device-model.py deleted file mode 100755 index 016ba5c0..00000000 --- a/test/test-get-device-model.py +++ /dev/null @@ -1,33 +0,0 @@ -# Test GetDeviceModel instruction - -import sys -import os - -# disable printing stack trace -sys.tracebacklimit = 0 - -from ledgerblue.comm import getDongle -from ledgerblue.commTCP import getDongle as getDongleTCP - -if os.environ.get('USE_SPECULOS') is not None: - dongle = getDongleTCP(debug=False) -else: - dongle = getDongle(False) - -instructionClass = "AA" -instructionCode = "11" -p1 = "00" -p2 = "00" -dataLength = "00" - -print("Testing", "GetDeviceModel", instructionCode, end=" ") -response = dongle.exchange(bytes.fromhex(instructionClass + instructionCode + p1 + p2 + dataLength)) - -if response.hex() == '00': - print("Model: Nano S") -elif response.hex() == '01': - print("Model: Nano S Plus") -elif response.hex() == '02': - print("Model: Nano X") -else: - print("Unknown model " + reponse.hex()) diff --git a/test/test-get-public-key-ed25519.py b/test/test-get-public-key-ed25519.py deleted file mode 100755 index 3c0c23b9..00000000 --- a/test/test-get-public-key-ed25519.py +++ /dev/null @@ -1,82 +0,0 @@ -# Test GetPubKeyEd25519 instruction -# WARNING: Requires device configured for development (see root README.md) - -import sys -import os - -from ledgerblue.comm import getDongle -from ledgerblue.commTCP import getDongle as getDongleTCP - - -# -------------------------------------------------------------------------------------------- -# Encode absolute BIP32 path into hex string representation -# -------------------------------------------------------------------------------------------- -def encodeBip32(path): - elements = path.replace('H', "'").replace('"', "'").split('/') - result = (len(elements) - 1).to_bytes(1, 'little').hex() - for i in range(1, len(elements)): - num = 0x80000000 if elements[i].endswith("'") else 0 - num += int(elements[i].replace("'", "")) - result += num.to_bytes(4, 'big').hex() - return result - - -# -------------------------------------------------------------------------------------------- -# -# -------------------------------------------------------------------------------------------- - -def call_and_check(path, expected_pub_key): - data = encodeBip32(path) - data_length = int(len(data) / 2).to_bytes(1, 'little').hex() - response = dongle.exchange(bytes.fromhex(instructionClass + instructionCode + p1 + p2 + data_length + data)) - pk = response.hex() - assert pk == expected_pub_key, "Invalid public key\nExpected: " + expected_pub_key + "\nReceived: " + pk - - -# -------------------------------------------------------------------------------------------- -# disable printing stack trace -sys.tracebacklimit = 0 - -if os.environ.get('USE_SPECULOS') is not None: - dongle = getDongleTCP(debug=False) -else: - dongle = getDongle(False) - -instructionClass = "AA" -instructionCode = "21" -p1 = "00" -p2 = "00" - -test_vectors = [ - ("m/44H/1022H/12H/525H/1460H/0H", "451152a1cef7be603205086d4ebac0a0b78fda2ff4684b9dea5ca9ef003d4e7d"), - ("m/44H/1022H/12H/525H/1460H/1H", "0a4b894208a1f6b1bd7e823b59909f01aae0172b534baa2905b25f1bcbbb4f0a"), - ("m/44H/1022H/12H/525H/1460H/2H", "235c27aafa83376d475040a7eb53ea889ae93bda005ef7f445f221f73b43313e"), - ("m/44H/1022H/12H/525H/1460H/3H", "cc294e8c43be93012f827cd54a11a2f836e5934c2361d61b5a737adbd42bf030"), - ("m/44H/1022H/12H/525H/1678H/0H", "2612a6865d354ed285baf4877d671276e6cd8cd81e3f1101c35d16853c204fa4"), - ("m/44H/1022H/12H/525H/1678H/1H", "2f92b0b43ee39c6c3006b2a5c7cdbdee0c6b6835d76a0dc8da0aeffc741d5c96"), - ("m/44H/1022H/12H/525H/1678H/2H", "3f23bcce53cf2ea14d238f8473aaf3c7ed3f4047fa20158389eabb651766f8d5"), - ("m/44H/1022H/12H/525H/1678H/3H", "5b36d055cdd07129ba0b780cd285661d5dae02831a55b408f84f9b72ba95c0a9"), - ("m/44H/1022H/12H/525H/1391H/0H", "d998153796a745c2f733079c791f4ae93eb96a812b39c9ee7a26eca32fa14905"), - ("m/44H/1022H/12H/525H/1391H/1H", "94e163e6739fa0c9db3f44c0675f185fdb0f1dddb6d929cc49a199717c0a2da2"), - ("m/44H/1022H/12H/525H/1391H/2H", "9bd51ee27f37367ee4c7cf18e5b8e1b40ae808d3da0350de152c9db34de778d3"), - ("m/44H/1022H/12H/525H/1391H/3H", "d9d6fc68321ce02c30122c6ff5c1a8068142703f9dac362cff29bfb814a2130f"), - ("m/44H/1022H/12H/618H/1460H/0H", "cc129e0d00d365f2269cee259923e904b8c46ef5b28aefc18df8ed20ef42a3eb"), - ("m/44H/1022H/12H/618H/1460H/1H", "f67ac35c37921579b59f77fe05a1012ad8240092c8fed0d8fe1f96206eb99c3e"), - ("m/44H/1022H/12H/618H/1460H/2H", "02c8074258844ae4b81d80261fc411e95070526e0d92803fe4f343e00dc89ed5"), - ("m/44H/1022H/12H/618H/1460H/3H", "fca4f791866a48cb53a269e420fa6b7f192e98fee5f9e8f9009962ca3d9baeb2"), - ("m/44H/1022H/12H/618H/1678H/0H", "f6f2056e3edb8905be1717c1f8f5204242047875ba548c19d42962366800c1d4"), - ("m/44H/1022H/12H/618H/1678H/1H", "960ee0acd88b0e7f1e8cb171139c2e0e7b8d776134a103b36034a6991fcac175"), - ("m/44H/1022H/12H/618H/1678H/2H", "07ba2aa69eee065495d8820ef9d5a94b982370e233f04472900cfb5efdb4fa3d"), - ("m/44H/1022H/12H/618H/1678H/3H", "b4763c9a25d95f32e5ddefc7006ffc4a6570818bf24aeff581ac60cd82a751ba"), - ("m/44H/1022H/12H/618H/1391H/0H", "996626245f999a4c500c394036db43f73abb18f46970066ff124c750dc096360"), - ("m/44H/1022H/12H/618H/1391H/1H", "afe925e5aabfa04fb10640cad2c1f6739b3dc9fb4ddeba6ff28e90854d45962d"), - ("m/44H/1022H/12H/618H/1391H/2H", "1226b881b66c58015e760852c0cb202869b73e29fbe0b3d22d50af0fa606b14a"), - ("m/44H/1022H/12H/618H/1391H/3H", "7fa1d448ef608a6e1a8533d816367b4fa0d60c39844bb82dbce1ea266105a275"), -] - -print("Testing", "GetPubKeyEd25519", instructionCode, end=" ") - -for vector in test_vectors: - call_and_check(vector[0], vector[1]) - -print("Success") diff --git a/test/test-get-public-key-secp256k1.py b/test/test-get-public-key-secp256k1.py deleted file mode 100755 index 4521d6dd..00000000 --- a/test/test-get-public-key-secp256k1.py +++ /dev/null @@ -1,79 +0,0 @@ -# Test GetPubKeySecp256k1 instruction -# WARNING: Requires device configured for development (see root README.md) - -import sys -import os - -from ledgerblue.comm import getDongle -from ledgerblue.commTCP import getDongle as getDongleTCP - - -# -------------------------------------------------------------------------------------------- -# Encode absolute BIP32 path into hex string representation -# -------------------------------------------------------------------------------------------- -def encodeBip32(path): - elements = path.replace('H', "'").replace('"', "'").split('/') - result = (len(elements) - 1).to_bytes(1, 'little').hex() - for i in range(1, len(elements)): - num = 0x80000000 if elements[i].endswith("'") else 0 - num += int(elements[i].replace("'", "")) - result += num.to_bytes(4, 'big').hex() - return result - - -# -------------------------------------------------------------------------------------------- -# -# -------------------------------------------------------------------------------------------- - -def call_and_check(path, expected_pub_key): - data = encodeBip32(path) - data_length = int(len(data) / 2).to_bytes(1, 'little').hex() - - response = dongle.exchange(bytes.fromhex(instructionClass + instructionCode + p1 + p2 + data_length + data)) - pk = response.hex() - assert pk == expected_pub_key, "Invalid public key\nExpected: " + expected_pub_key + "\nReceived: " + pk - - -# -------------------------------------------------------------------------------------------- -# disable printing stack trace -sys.tracebacklimit = 0 - -if os.environ.get('USE_SPECULOS') is not None: - dongle = getDongleTCP(debug=False) -else: - dongle = getDongle(False) - -instructionClass = "AA" -instructionCode = "31" -p1 = "00" -p2 = "00" - -test_vectors = [ - ("m/44H/1022H/0H/0/0H", "03f43fba6541031ef2195f5ba96677354d28147e45b40cde4662bec9162c361f55"), - ("m/44H/1022H/0H/0/1H", "0206ea8842365421f48ab84e6b1b197010e5a43a527952b11bc6efe772965e97cc"), - ("m/44H/1022H/0H/0/2H", "024f44df0493977fcc5704c00c5c89932d77a9a0b016680e6a931684e160fb8d99"), - ("m/44H/1022H/0H/0/3H", "0388485f6889d7ebcf1cf6f6dafc8ae5d224f9e847fac868c2e006e71ff3383a91"), - ("m/44H/1022H/0H/0/4H", "024128185f801aee4ebe9a70d6290f60051162526551240da1374363b58e2e1e2c"), - ("m/44H/1022H/0H/0/5H", "03f3f51a028cbed1a2c0c3b1f21abc5354f58e8d5279e817195750b8ddec9334f4"), - ("m/44H/1022H/0H/0/6H", "0383d0721aac0569c37edafe5edd6e2d320822dc23f9207b263b2319837ed1a89d"), - ("m/44H/1022H/0H/0/7H", "03af13461247c39e54fab62597701ab06c67edac7f8de4df1283a2645706c0b153"), - ("m/44H/1022H/0H/0/8H", "0226912f5226f4a7c9c80780f32c6ad406c8b471c4929033e5e1756ca248c5a278"), - ("m/44H/1022H/0H/0/9H", "035a9825274e30ce325cc3934b4e23b008097bd97f1b0a0ef57f7dc9a33e5760ed"), - ("m/44H/1022H/0H/0/0", "03bc2ec8f3668c869577bf66b7b48f8dee57b833916aa70966fa4a5029b63bb18f"), - ("m/44H/1022H/0H/0/1", "03c8a6a5710b5abba09341c24382de3222913120dee5084e887529bf821f3973e2"), - ("m/44H/1022H/0H/0/2", "02d6b5b132e16160d6337d83408165c49edac7bb0112b1d1b3e96e3f6908f6d0d6"), - ("m/44H/1022H/0H/0/3", "03ce5f85ad86922fbc217806a79d9f4d8d6a268f3822ffed9533a9fff73a4374b7"), - ("m/44H/1022H/0H/0/4", "03e2c66201fc7330992d316d847bdbeb561704b70779ce60a4fcff53ffe5b6cb36"), - ("m/44H/1022H/0H/0/5", "02df71a292057d1f7cda4fbcd252e43907646610cc191b6f44050683f82a7e63de"), - ("m/44H/1022H/0H/0/6", "03d054f1c3d7982994d9581c496f84b6cdf584c8eff0401da82d8c19ad88e8a768"), - ("m/44H/1022H/0H/0/7", "03ccf3b2bd4294d7e7e84268003f1e25c4893a482e28fcf00dfc1ff65679541d50"), - ("m/44H/1022H/0H/0/8", "0284c067d070bfdb790883cab583f13a0b13f9d52eacdb52a5d8588231ce8c8b89"), - ("m/44H/1022H/0H/0/9", "02e5703b668deebac710118df687296e90da93c19d0db3cce656b6a677ab3e4747"), -] - -print("Testing", "GetPubKeySecp256k1", instructionCode, end=" ") - -for vector in test_vectors: - call_and_check(vector[0], vector[1]) - -print("Success") diff --git a/test/test-sign-auth-ed25519.py b/test/test-sign-auth-ed25519.py deleted file mode 100755 index 79c8c683..00000000 --- a/test/test-sign-auth-ed25519.py +++ /dev/null @@ -1,142 +0,0 @@ -# Test SignAuthEd25519 instruction - -import sys -import os - -from ledgerblue.comm import getDongle -from ledgerblue.commTCP import getDongle as getDongleTCP -from cryptography.hazmat.primitives.asymmetric import ed25519 - -# disable printing stack trace -sys.tracebacklimit = 0 - -if os.environ.get('USE_SPECULOS') is not None: - dongle = getDongleTCP(debug=False) -else: - dongle = getDongle(False) - -instructionClass = "AA" -instructionCode = "61" # SignAuthEd25519 -p1 = "00" -p2 = "00" -dataLength = "00" - -print("Testing", "SignAuth", instructionCode) - - -def encode_bip32(path): - elements = path.replace('H', "'").replace('"', "'").split('/') - result = (len(elements) - 1).to_bytes(1, 'little').hex() - for i in range(1, len(elements)): - num = 0x80000000 if elements[i].endswith("'") else 0 - num += int(elements[i].replace("'", "")) - result += num.to_bytes(4, 'big').hex() - return result - - -def send_auth_request(daddr, origin, nonce): - addr_length = len(daddr).to_bytes(1, 'little').hex() - data = nonce + addr_length + daddr.encode('utf-8').hex() + origin.encode('utf-8').hex() - data_length = int(len(data) / 2).to_bytes(1, 'little').hex() - - try: - rc = dongle.exchange(bytes.fromhex("AC" + instructionCode + p1 + p2 + data_length + data)) - except Exception as e: - print("Error sending txn chunk: ", e) - return None - return rc - - -def send_derivation_path(bip_path): - path_data = encode_bip32(bip_path) - data_length = int(len(path_data) / 2).to_bytes(1, 'little').hex() - # print("Sending derivation path: ", bip_path, ", data_len = ", data_length) - - try: - return dongle.exchange(bytes.fromhex(instructionClass + instructionCode + p1 + p2 + data_length + path_data)) - except Exception as e: - print("Error sending derivation path: ", e) - return None - - -test_vectors = [ - ( - "dc47fc69e9e45855addf579f398da0309c878092dd95352b9fe187a7e5a529e2", - "account_tdx_b_1p9dkged3rpzy860ampt5jpmvv3yl4y6f5yppp4tnscdslvt9v3", - "https://dashboard.rdx.works", - "ec5dcb3d1f75627be1021cb8890f0e8ce0c9fe7f2ff55cbdff096b38a32612c9", - ), - ( - "866836f5b9c827ca38fd2bfef94f95ba21933f75a0291c85d3ecfc18b8aa5b2d", - "account_tdx_b_1p8ahenyznrqy2w0tyg00r82rwuxys6z8kmrhh37c7maqpydx7p", - "https://dashboard.rdx.works", - "d7fb740b9ff00657d710dcbeddb2d432e697fc0dd39c60feb7858b17ef0eff58", - ), - ( - "0f41aa92e8c978d7f920ca56daf123a0a0d975eea06ecfb57bec0a0560fb73e3", - "account_tdx_b_1p95nal0nmrqyl5r4phcspg8ahwnamaduzdd3kaklw3vqeavrwa", - "https://dashboard.rdx.works", - "4aaa2ec25c3fe215412b3f005e4c37d518af3a22b4728587cf6dbcf83341e8b3", - ), - ( - "9c8d2622cedb9dc4e53daea398dd178a2ec938d402eeaba41a2ac946b0f4dd57", - "account_tdx_b_1p9dkged3rpzy860ampt5jpmvv3yl4y6f5yppp4tnscdslvt9v3", - "https://stella.swap", - "a10fad201666b4bcf7f707841d58b11740c290e03790b17ed0fec23b3f180e65", - ), - ( - "2c07a4fc72341ae9160a8f9ddf2d0bb8fd9d795ed0d87059a9e5de8321513871", - "account_tdx_b_1p8ahenyznrqy2w0tyg00r82rwuxys6z8kmrhh37c7maqpydx7p", - "https://stella.swap", - "718b0eb060a719492011910258a4b4119d8c95aef34eb9519c9fa7de25f7ac43", - ), - ( - "306b2407e8b675bb22b630efa938249595433975276862e9bfa07f7f94ca84a8", - "account_tdx_b_1p95nal0nmrqyl5r4phcspg8ahwnamaduzdd3kaklw3vqeavrwa", - "https://stella.swap", - "9a4f834aefdc455cb4601337227e1b7e74d60308327564ececf33456509964cd", - ), - ( - "a14942b1dc361c7e153e4d4200f902da1dafa2bd54bc4c0387c779c22a1e454e", - "account_tdx_b_1p9dkged3rpzy860ampt5jpmvv3yl4y6f5yppp4tnscdslvt9v3", - "https://rola.xrd", - "00dca15875839ab1f549445a36c7b5c0dcf7aebfa7d48f945f2aa5cf4aa1a9a3", - ), - ( - "6a13329619caafdf4351d1c8b85b7f523ce2955873f003402be6e1e45cdce4ae", - "account_tdx_b_1p8ahenyznrqy2w0tyg00r82rwuxys6z8kmrhh37c7maqpydx7p", - "https://rola.xrd", - "0a510b2362c9ce19d11c538b2f6a15f62caab6528071eaad5ba8a563a02e01cb", - ), - ( - "f9ec8f328d9aeec55546d1cd78a13cc7967bd52aba3c8e305ed39f82465f395c", - "account_tdx_b_1p95nal0nmrqyl5r4phcspg8ahwnamaduzdd3kaklw3vqeavrwa", - "https://rola.xrd", - "20619c1df905a28e7a76d431f2b59e99dd1a8f386842e1701862e765806a5c47", - ), -] - -for vector in test_vectors: - send_derivation_path("m/44H/1022H/12H/525H/1460H/0H") - rc = send_auth_request(vector[1], vector[2], vector[3]) - - if rc is None: - print("Failed") - else: - # signature = rc[0:64].hex() - # key = rc[64:96].hex() - # hash = rc[96:128].hex() - # print("Success") - # print("Signature:", signature) - # print("Key:", key) - # print("Hash:", hash) - pubkey = ed25519.Ed25519PublicKey.from_public_bytes(bytes(rc[64:96])) - try: - pubkey.verify(bytes(rc[0:64]), bytes(rc[96:128])) - print("Success") - assert rc[96:128].hex() == vector[0], "Invalid calculated hash\nExpected: " + vector[ - 0] + "\nReceived: " + rc[96:128].hex() - except Exception as e: - print("Invalid signature ", e) - -print("All tests successfully passed") diff --git a/test/test-sign-auth-secp256k1.py b/test/test-sign-auth-secp256k1.py deleted file mode 100755 index 9278d0f6..00000000 --- a/test/test-sign-auth-secp256k1.py +++ /dev/null @@ -1,139 +0,0 @@ -# Test SignAuthSecp256k1 instruction - -import sys -import os - -from ledgerblue.comm import getDongle -from ledgerblue.commTCP import getDongle as getDongleTCP -from cryptography.hazmat.primitives.asymmetric import ec, utils -from cryptography.hazmat.primitives import hashes - -# disable printing stack trace -sys.tracebacklimit = 0 - -if os.environ.get('USE_SPECULOS') is not None: - dongle = getDongleTCP(debug=False) -else: - dongle = getDongle(False) - -instructionClass = "AA" -instructionCode = "71" # SignAuthSecp256k1 -p1 = "00" -p2 = "00" -dataLength = "00" - -print("Testing", "SignAuth", instructionCode) - - -def encode_bip32(path): - elements = path.replace('H', "'").replace('"', "'").split('/') - result = (len(elements) - 1).to_bytes(1, 'little').hex() - for i in range(1, len(elements)): - num = 0x80000000 if elements[i].endswith("'") else 0 - num += int(elements[i].replace("'", "")) - result += num.to_bytes(4, 'big').hex() - return result - - -def send_auth_request(daddr, origin, nonce): - addr_length = len(daddr).to_bytes(1, 'little').hex() - data = nonce + addr_length + daddr.encode('utf-8').hex() + origin.encode('utf-8').hex() - data_length = int(len(data) / 2).to_bytes(1, 'little').hex() - - try: - rc = dongle.exchange(bytes.fromhex("AC" + instructionCode + p1 + p2 + data_length + data)) - except Exception as e: - print("Error sending txn chunk: ", e) - return None - return rc - - -def send_derivation_path(bip_path): - path_data = encode_bip32(bip_path) - data_length = int(len(path_data) / 2).to_bytes(1, 'little').hex() - # print("Sending derivation path: ", bip_path, ", data_len = ", data_length) - - try: - return dongle.exchange(bytes.fromhex(instructionClass + instructionCode + p1 + p2 + data_length + path_data)) - except Exception as e: - print("Error sending derivation path: ", e) - return None - - -test_vectors = [ - ( - "dc47fc69e9e45855addf579f398da0309c878092dd95352b9fe187a7e5a529e2", - "account_tdx_b_1p9dkged3rpzy860ampt5jpmvv3yl4y6f5yppp4tnscdslvt9v3", - "https://dashboard.rdx.works", - "ec5dcb3d1f75627be1021cb8890f0e8ce0c9fe7f2ff55cbdff096b38a32612c9", - ), - ( - "866836f5b9c827ca38fd2bfef94f95ba21933f75a0291c85d3ecfc18b8aa5b2d", - "account_tdx_b_1p8ahenyznrqy2w0tyg00r82rwuxys6z8kmrhh37c7maqpydx7p", - "https://dashboard.rdx.works", - "d7fb740b9ff00657d710dcbeddb2d432e697fc0dd39c60feb7858b17ef0eff58", - ), - ( - "0f41aa92e8c978d7f920ca56daf123a0a0d975eea06ecfb57bec0a0560fb73e3", - "account_tdx_b_1p95nal0nmrqyl5r4phcspg8ahwnamaduzdd3kaklw3vqeavrwa", - "https://dashboard.rdx.works", - "4aaa2ec25c3fe215412b3f005e4c37d518af3a22b4728587cf6dbcf83341e8b3", - ), - ( - "9c8d2622cedb9dc4e53daea398dd178a2ec938d402eeaba41a2ac946b0f4dd57", - "account_tdx_b_1p9dkged3rpzy860ampt5jpmvv3yl4y6f5yppp4tnscdslvt9v3", - "https://stella.swap", - "a10fad201666b4bcf7f707841d58b11740c290e03790b17ed0fec23b3f180e65", - ), - ( - "2c07a4fc72341ae9160a8f9ddf2d0bb8fd9d795ed0d87059a9e5de8321513871", - "account_tdx_b_1p8ahenyznrqy2w0tyg00r82rwuxys6z8kmrhh37c7maqpydx7p", - "https://stella.swap", - "718b0eb060a719492011910258a4b4119d8c95aef34eb9519c9fa7de25f7ac43", - ), - ( - "306b2407e8b675bb22b630efa938249595433975276862e9bfa07f7f94ca84a8", - "account_tdx_b_1p95nal0nmrqyl5r4phcspg8ahwnamaduzdd3kaklw3vqeavrwa", - "https://stella.swap", - "9a4f834aefdc455cb4601337227e1b7e74d60308327564ececf33456509964cd", - ), - ( - "a14942b1dc361c7e153e4d4200f902da1dafa2bd54bc4c0387c779c22a1e454e", - "account_tdx_b_1p9dkged3rpzy860ampt5jpmvv3yl4y6f5yppp4tnscdslvt9v3", - "https://rola.xrd", - "00dca15875839ab1f549445a36c7b5c0dcf7aebfa7d48f945f2aa5cf4aa1a9a3", - ), - ( - "6a13329619caafdf4351d1c8b85b7f523ce2955873f003402be6e1e45cdce4ae", - "account_tdx_b_1p8ahenyznrqy2w0tyg00r82rwuxys6z8kmrhh37c7maqpydx7p", - "https://rola.xrd", - "0a510b2362c9ce19d11c538b2f6a15f62caab6528071eaad5ba8a563a02e01cb", - ), - ( - "f9ec8f328d9aeec55546d1cd78a13cc7967bd52aba3c8e305ed39f82465f395c", - "account_tdx_b_1p95nal0nmrqyl5r4phcspg8ahwnamaduzdd3kaklw3vqeavrwa", - "https://rola.xrd", - "20619c1df905a28e7a76d431f2b59e99dd1a8f386842e1701862e765806a5c47", - ), -] - -for vector in test_vectors: - send_derivation_path("m/44H/1022H/10H/525H/1238H") - rc = send_auth_request(vector[1], vector[2], vector[3]) - - if rc is None: - print("Failed") - else: - r = int.from_bytes(rc[1:33], byteorder='big', signed=False) - s = int.from_bytes(rc[33:65], byteorder='big', signed=False) - signature = utils.encode_dss_signature(int(r), int(s)) - pubkey = ec.EllipticCurvePublicKey.from_encoded_point(ec.SECP256K1(), bytes(rc[65:98])) - try: - # Note that Prehashed parameter is irrelevant here, we just need to pass something known to the library - pubkey.verify(signature, bytes(rc[98:130]), ec.ECDSA(utils.Prehashed(hashes.SHA256()))) - print("Success") - assert rc[98:130].hex() == vector[0], "Invalid calculated hash\nExpected: " + vector[0] + "\nReceived: " + rc[98:130].hex() - except Exception as e: - print("Invalid signature ", e) - -print("All tests successfully passed") From 3aad165ad3c2bc66b7f0b984196d43075630a447 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Fri, 1 Nov 2024 20:20:32 +0100 Subject: [PATCH 2/7] Migrate verify-address --- ragger_tests/application_client/app.py | 31 +++++ .../application_client/command_sender.py | 16 +++ .../test-physical-device-nanosplus.sh | 2 +- ragger_tests/test_verify_address_ed25519.py | 118 ++++++++++-------- ragger_tests/test_verify_address_secp256k1.py | 94 ++++++-------- test/test-verify-address-ed25519.py | 71 ----------- test/test-verify-address-secp256k1.py | 75 ----------- 7 files changed, 154 insertions(+), 253 deletions(-) delete mode 100755 test/test-verify-address-ed25519.py delete mode 100755 test/test-verify-address-secp256k1.py diff --git a/ragger_tests/application_client/app.py b/ragger_tests/application_client/app.py index 4af9d64e..ca3462cf 100644 --- a/ragger_tests/application_client/app.py +++ b/ragger_tests/application_client/app.py @@ -25,6 +25,37 @@ def get_device_id(self) -> str: def get_device_model(self) -> LedgerModel: rapdu = self.sender.get_device_model() return LedgerModel.unpack(raw=rapdu.data) + + @contextmanager + def __verify_address( + self, + curve: C, + navigate: Callable[[], None], + path: str, + ) -> Generator[RAPDU, None, None]: + global maybe + with self.sender.send_verify_address( + curve=curve, + navigate=navigate, + path=path, + ) as response: + maybe = response + yield maybe if maybe is not None else self.sender.get_async_response() + + def verify_address( + self, + curve: C, + path: str, + navigate: Callable[[], None] = lambda: None + ) -> str: + global response + with self.__verify_address( + curve=curve, + navigate=navigate, + path=path + ) as res: + response = res + return response.data.decode('utf-8') @contextmanager def __sign_rola( diff --git a/ragger_tests/application_client/command_sender.py b/ragger_tests/application_client/command_sender.py index 53d00f37..9f8a68d2 100644 --- a/ragger_tests/application_client/command_sender.py +++ b/ragger_tests/application_client/command_sender.py @@ -87,6 +87,22 @@ def get_public_key( data=pack_derivation_path(path) ) + @contextmanager + def send_verify_address( + self, + curve: C, + navigate: Callable[[], None], + path: str + ): + ins = curve.ins_verify_address() + with self._async_send_ins( + navigate=navigate, + ins=ins, + data=pack_derivation_path(path) + ): + yield self.get_optional_async_response() + + def _send_derivation_path( self, navigate: Callable[[], None], diff --git a/ragger_tests/test-physical-device-nanosplus.sh b/ragger_tests/test-physical-device-nanosplus.sh index df7591c2..d0b336ce 100755 --- a/ragger_tests/test-physical-device-nanosplus.sh +++ b/ragger_tests/test-physical-device-nanosplus.sh @@ -1 +1 @@ -pytest -v --tb=short --backend ledgerwallet --device nanosp $@ +pytest -v --tb=short --backend ledgerwallet --device nanosp $@ \ No newline at end of file diff --git a/ragger_tests/test_verify_address_ed25519.py b/ragger_tests/test_verify_address_ed25519.py index 97bd0f3f..2fa0a830 100644 --- a/ragger_tests/test_verify_address_ed25519.py +++ b/ragger_tests/test_verify_address_ed25519.py @@ -1,28 +1,60 @@ from pathlib import Path -from ragger.bip import pack_derivation_path +from typing import Tuple from ragger.navigator import NavInsID +from ragger.backend.interface import BackendInterface +from ragger.firmware.structs import Firmware +from ragger.navigator.navigator import Navigator -ROOT_SCREENSHOT_PATH = Path(__file__).parent.resolve() - -CLA1 = 0xAA -CLA2 = 0xAC -INS = 0x81 - +from ragger_tests.application_client.app import App +from ragger_tests.application_client.curve import C, Curve25519 -# -------------------------------------------------------------------------------------------- -# Check single test vector -# -------------------------------------------------------------------------------------------- +ROOT_SCREENSHOT_PATH = Path(__file__).parent.resolve() -def call_and_check(firmware, backend, navigator, test_name, vector): - path, expected_pub_key = vector - with backend.exchange_async(cla=CLA1, ins=INS, data=pack_derivation_path(path)) as response: +def verify_address( + curve: C, + firmware: Firmware, + backend: BackendInterface, + navigator: Navigator, + test_name: str, + vector: Tuple[str, str] +): + path, expected_address = vector + + def navigate(): if firmware.device.startswith("nano"): - navigator.navigate_and_compare(ROOT_SCREENSHOT_PATH, test_name, - [NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK, - NavInsID.RIGHT_CLICK, NavInsID.BOTH_CLICK, ]) - pk = backend.last_async_response.data.decode('utf-8') - assert pk == expected_pub_key, "Invalid address\nExpected: " + expected_pub_key + "\nReceived: " + pk - + navigator.navigate_and_compare( + path=ROOT_SCREENSHOT_PATH, + test_case_name=test_name, + instructions=[ + NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK, + NavInsID.RIGHT_CLICK, NavInsID.BOTH_CLICK + ] + ) + + app = App(backend) + response = app.verify_address( + curve=curve, + path=path, + navigate=navigate + ) + + assert response == expected_address + +def verify_address_ed25519( + firmware: Firmware, + backend: BackendInterface, + navigator: Navigator, + test_name: str, + vector: Tuple[str, str] +): + verify_address( + curve=Curve25519, + firmware=firmware, + backend=backend, + navigator=navigator, + test_name=test_name, + vector=vector + ) test_vectors = [ ("m/44'/1022'/1'/525'/1460'/0'", "account_rdx12939q7jvjc0q9vvqtc87uv6eu334yagtak7k9udekafy66gpvu222n"), @@ -43,66 +75,50 @@ def call_and_check(firmware, backend, navigator, test_name, vector): ("m/44'/1022'/242'/525'/1678'/2'", "account_sim1288duwxpwa7fpxldejl7v8yfqucq8vl04dpn9mpdl44cp3gtxefkel"), ] - def test_verify_address_ed25519_0(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[0]) - + verify_address_ed25519(firmware, backend, navigator, test_name, test_vectors[0]) def test_verify_address_ed25519_1(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[1]) - + verify_address_ed25519(firmware, backend, navigator, test_name, test_vectors[1]) def test_verify_address_ed25519_2(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[2]) - + verify_address_ed25519(firmware, backend, navigator, test_name, test_vectors[2]) def test_verify_address_ed25519_3(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[3]) - + verify_address_ed25519(firmware, backend, navigator, test_name, test_vectors[3]) def test_verify_address_ed25519_4(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[4]) - + verify_address_ed25519(firmware, backend, navigator, test_name, test_vectors[4]) def test_verify_address_ed25519_5(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[5]) - + verify_address_ed25519(firmware, backend, navigator, test_name, test_vectors[5]) def test_verify_address_ed25519_6(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[6]) - + verify_address_ed25519(firmware, backend, navigator, test_name, test_vectors[6]) def test_verify_address_ed25519_7(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[7]) - + verify_address_ed25519(firmware, backend, navigator, test_name, test_vectors[7]) def test_verify_address_ed25519_8(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[8]) - + verify_address_ed25519(firmware, backend, navigator, test_name, test_vectors[8]) def test_verify_address_ed25519_9(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[9]) - + verify_address_ed25519(firmware, backend, navigator, test_name, test_vectors[9]) def test_verify_address_ed25519_10(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[10]) - + verify_address_ed25519(firmware, backend, navigator, test_name, test_vectors[10]) def test_verify_address_ed25519_11(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[11]) - + verify_address_ed25519(firmware, backend, navigator, test_name, test_vectors[11]) def test_verify_address_ed25519_12(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[12]) - + verify_address_ed25519(firmware, backend, navigator, test_name, test_vectors[12]) def test_verify_address_ed25519_13(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[13]) - + verify_address_ed25519(firmware, backend, navigator, test_name, test_vectors[13]) def test_verify_address_ed25519_14(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[14]) - + verify_address_ed25519(firmware, backend, navigator, test_name, test_vectors[14]) def test_verify_address_ed25519_15(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[15]) + verify_address_ed25519(firmware, backend, navigator, test_name, test_vectors[15]) diff --git a/ragger_tests/test_verify_address_secp256k1.py b/ragger_tests/test_verify_address_secp256k1.py index 3ef1f545..e0f2b1b2 100644 --- a/ragger_tests/test_verify_address_secp256k1.py +++ b/ragger_tests/test_verify_address_secp256k1.py @@ -1,27 +1,26 @@ -from pathlib import Path -from ragger.bip import pack_derivation_path -from ragger.navigator import NavInsID - -ROOT_SCREENSHOT_PATH = Path(__file__).parent.resolve() - -CLA1 = 0xAA -CLA2 = 0xAC -INS = 0x91 - - -# -------------------------------------------------------------------------------------------- -# Check single test vector -# -------------------------------------------------------------------------------------------- - -def call_and_check(firmware, backend, navigator, test_name, vector): - path, expected_pub_key = vector - with backend.exchange_async(cla=CLA1, ins=INS, data=pack_derivation_path(path)) as response: - if firmware.device.startswith("nano"): - navigator.navigate_and_compare(ROOT_SCREENSHOT_PATH, test_name, - [NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK, - NavInsID.RIGHT_CLICK, NavInsID.BOTH_CLICK, ]) - pk = backend.last_async_response.data.decode('utf-8') - assert pk == expected_pub_key, "Invalid address\nExpected: " + expected_pub_key + "\nReceived: " + pk +from typing import Tuple +from ragger.backend.interface import BackendInterface +from ragger.firmware.structs import Firmware +from ragger.navigator.navigator import Navigator + +from ragger_tests.application_client.curve import SECP256K1 +from ragger_tests.test_verify_address_ed25519 import verify_address + +def verify_address_secp256k1( + firmware: Firmware, + backend: BackendInterface, + navigator: Navigator, + test_name: str, + vector: Tuple[str, str] +): + verify_address( + curve=SECP256K1, + firmware=firmware, + backend=backend, + navigator=navigator, + test_name=test_name, + vector=vector + ) test_vectors = [ @@ -49,64 +48,49 @@ def call_and_check(firmware, backend, navigator, test_name, vector): def test_verify_address_secp256k1_0(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[0]) - + verify_address_secp256k1(firmware, backend, navigator, test_name, test_vectors[0]) def test_verify_address_secp256k1_1(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[1]) - + verify_address_secp256k1(firmware, backend, navigator, test_name, test_vectors[1]) def test_verify_address_secp256k1_2(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[2]) - + verify_address_secp256k1(firmware, backend, navigator, test_name, test_vectors[2]) def test_verify_address_secp256k1_3(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[3]) - + verify_address_secp256k1(firmware, backend, navigator, test_name, test_vectors[3]) def test_verify_address_secp256k1_4(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[4]) - + verify_address_secp256k1(firmware, backend, navigator, test_name, test_vectors[4]) def test_verify_address_secp256k1_5(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[5]) - + verify_address_secp256k1(firmware, backend, navigator, test_name, test_vectors[5]) def test_verify_address_secp256k1_6(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[6]) - + verify_address_secp256k1(firmware, backend, navigator, test_name, test_vectors[6]) def test_verify_address_secp256k1_7(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[7]) - + verify_address_secp256k1(firmware, backend, navigator, test_name, test_vectors[7]) def test_verify_address_secp256k1_8(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[8]) - + verify_address_secp256k1(firmware, backend, navigator, test_name, test_vectors[8]) def test_verify_address_secp256k1_9(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[9]) - + verify_address_secp256k1(firmware, backend, navigator, test_name, test_vectors[9]) def test_verify_address_secp256k1_10(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[10]) - + verify_address_secp256k1(firmware, backend, navigator, test_name, test_vectors[10]) def test_verify_address_secp256k1_11(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[11]) - + verify_address_secp256k1(firmware, backend, navigator, test_name, test_vectors[11]) def test_verify_address_secp256k1_12(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[12]) - + verify_address_secp256k1(firmware, backend, navigator, test_name, test_vectors[12]) def test_verify_address_secp256k1_13(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[13]) - + verify_address_secp256k1(firmware, backend, navigator, test_name, test_vectors[13]) def test_verify_address_secp256k1_14(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[14]) - + verify_address_secp256k1(firmware, backend, navigator, test_name, test_vectors[14]) def test_verify_address_secp256k1_15(firmware, backend, navigator, test_name): - call_and_check(firmware, backend, navigator, test_name, test_vectors[15]) + verify_address_secp256k1(firmware, backend, navigator, test_name, test_vectors[15]) diff --git a/test/test-verify-address-ed25519.py b/test/test-verify-address-ed25519.py deleted file mode 100755 index 19f16c98..00000000 --- a/test/test-verify-address-ed25519.py +++ /dev/null @@ -1,71 +0,0 @@ -# Test GetPubKeyEd25519 instruction -# WARNING: Requires device configured for development (see root README.md) - -import sys -import os - -from ledgerblue.comm import getDongle -from ledgerblue.commTCP import getDongle as getDongleTCP - - -# -------------------------------------------------------------------------------------------- -# Encode absolute BIP32 path into hex string representation -# -------------------------------------------------------------------------------------------- -def encodeBip32(path): - elements = path.replace('H', "'").replace('"', "'").split('/') - result = (len(elements) - 1).to_bytes(1, 'little').hex() - for i in range(1, len(elements)): - num = 0x80000000 if elements[i].endswith("'") else 0 - num += int(elements[i].replace("'", "")) - result += num.to_bytes(4, 'big').hex() - return result - - -# -------------------------------------------------------------------------------------------- -# -# -------------------------------------------------------------------------------------------- - -def call_and_check(path, expected_pub_key): - data = encodeBip32(path) - data_length = int(len(data) / 2).to_bytes(1, 'little').hex() - response = dongle.exchange(bytes.fromhex(instructionClass + instructionCode + p1 + p2 + data_length + data)) - pk = response.decode('utf-8') - assert pk == expected_pub_key, "Invalid address\nExpected: " + expected_pub_key + "\nReceived: " + pk - print(path, " - Success") - - -# -------------------------------------------------------------------------------------------- -# disable printing stack trace -sys.tracebacklimit = 0 - -if os.environ.get('USE_SPECULOS') is not None: - dongle = getDongleTCP(debug=False) -else: - dongle = getDongle(False) - -instructionClass = "AA" -instructionCode = "81" -p1 = "00" -p2 = "00" - -test_vectors = [ - ("m/44H/1022H/1H/525H/1460H/0H", "account_rdx12939q7jvjc0q9vvqtc87uv6eu334yagtak7k9udekafy66gpvu222n"), - ("m/44H/1022H/2H/525H/1460H/1H", "account_tdx_2_12y40zqyxrt27h8qd2q58vsrjhkzjgtj8nrpezuz4zfuq49fwz5a7e0"), - ("m/44H/1022H/10H/525H/1460H/2H", "account_tdx_a_128y22qvaswpshcdxqxnspqa52ywmt0lm3m6d9mlqd6n0emqkdcq2tq"), - ("m/44H/1022H/11H/525H/1460H/3H", "account_tdx_b_12907aun2hnh6aw9fn3e6g22ua3w75amq8yvtdxkmct9sx68d23f8d7"), - ("m/44H/1022H/12H/525H/1460H/3H", "account_tdx_c_12yur4s9ymydfg6xd43t4ygmygl77k57w5n8mkq523shc00fw8wqf6z"), - ("m/44H/1022H/13H/525H/1678H/0H", "account_tdx_d_12yvlfgwsnnajytehql0et9evdtytgpt2xp2whkdn7tscucns0slaq6"), - ("m/44H/1022H/14H/525H/1678H/1H", "account_tdx_e_128czm9huas70jukyvj8dkvpa0n7vthhn8ymchrc97lgg2aee8mmxn4"), - ("m/44H/1022H/32H/525H/1678H/2H", "account_tdx_20_1280naf7htu2fapvpplsdw9najqsm0kfpnsgld2zpwcyzwhc5222yv5"), - ("m/44H/1022H/33H/525H/1678H/2H", "account_tdx_21_12y6nghc9383er7e70fvjawjn20yfvfexa2a4hcqjz5v82zx3uchh98"), - ("m/44H/1022H/34H/525H/1678H/2H", "account_tdx_22_12y67rnvgsyc9rgn8mh5lyjquuxucpvxdpcj005lvn950a8enlx9lwp"), - ("m/44H/1022H/35H/525H/1678H/2H", "account_tdx_23_129uuanuqz6xkd2mrcc0trrt2pddw78kf886xz42ajyt2e3hkvvq396"), - ("m/44H/1022H/36H/525H/1678H/2H", "account_tdx_24_129ghwy8xz5eskevskaltrtqrh80em9ujwvc9tklxlw2pzjca58qfxp"), - ("m/44H/1022H/37H/525H/1678H/2H", "account_tdx_25_1289hhqh8xmv50lvmtuz20y7z24a5ujm0n2ehjq04gj32hr5cj2669c"), - ("m/44H/1022H/240H/525H/1678H/2H", "account_loc1286jt5l6fw3dxwth4uvv299vx8sz072mjg93e47lgqmugsdphu5eem"), - ("m/44H/1022H/241H/525H/1678H/2H", "account_test12952h3g26hjc6qwqpspg4fmmmmunvast7n99nmesfeehxrwtymndkk"), - ("m/44H/1022H/242H/525H/1678H/2H", "account_sim1288duwxpwa7fpxldejl7v8yfqucq8vl04dpn9mpdl44cp3gtxefkel"), -] - -for vector in test_vectors: - call_and_check(vector[0], vector[1]) diff --git a/test/test-verify-address-secp256k1.py b/test/test-verify-address-secp256k1.py deleted file mode 100755 index 6d4d8c60..00000000 --- a/test/test-verify-address-secp256k1.py +++ /dev/null @@ -1,75 +0,0 @@ -# Test GetPubKeyEd25519 instruction -# WARNING: Requires device configured for development (see root README.md) - -import sys -import os - -from ledgerblue.comm import getDongle -from ledgerblue.commTCP import getDongle as getDongleTCP - - -# -------------------------------------------------------------------------------------------- -# Encode absolute BIP32 path into hex string representation -# -------------------------------------------------------------------------------------------- -def encodeBip32(path): - elements = path.replace('H', "'").replace('"', "'").split('/') - result = (len(elements) - 1).to_bytes(1, 'little').hex() - for i in range(1, len(elements)): - num = 0x80000000 if elements[i].endswith("'") else 0 - num += int(elements[i].replace("'", "")) - result += num.to_bytes(4, 'big').hex() - return result - - -# -------------------------------------------------------------------------------------------- -# -# -------------------------------------------------------------------------------------------- - -def call_and_check(path, expected_pub_key): - data = encodeBip32(path) - data_length = int(len(data) / 2).to_bytes(1, 'little').hex() - response = dongle.exchange(bytes.fromhex(instructionClass + instructionCode + p1 + p2 + data_length + data)) - pk = response.decode('utf-8') - assert pk == expected_pub_key, "Invalid address\nExpected: " + expected_pub_key + "\nReceived: " + pk - print(path, " - Success") - - -# -------------------------------------------------------------------------------------------- -# disable printing stack trace -sys.tracebacklimit = 0 - -if os.environ.get('USE_SPECULOS') is not None: - dongle = getDongleTCP(debug=False) -else: - dongle = getDongle(False) - -instructionClass = "AA" -instructionCode = "91" -p1 = "00" -p2 = "00" - -test_vectors = [ - ("m/44H/1022H/0H/0/0H", "account_rdx16x5wz8wmkumuhn49klq0zwgjn9d8xs7n95maxam04vawld2d27jjz0"), - ("m/44H/1022H/0H/0/1H", "account_rdx16y6q3q6ey64j5qvkex3q0yshtln6z2lmyk254xrjcq393rc0hc79wl"), - ("m/44H/1022H/0H/0/2H", "account_rdx16y69pp98nqvh5xt36mcu78m4pvptpu65wa3nsp9qphn3lc4kp8r66k"), - ("m/44H/1022H/0H/0/3H", "account_rdx16x9rxepradk8g4zamprt7gn5vhs5j3v5e49yqt0k2w64a6phgf58qa"), - ("m/44H/1022H/0H/0/4H", "account_rdx16ywu4yg6ka4hd0nmju8cjsls75a98d238na6x0phxll855tjgt4gyk"), - ("m/44H/1022H/0H/0/5H", "account_rdx1685mjfygmz8e9k3x2ee7pt67kqr28lhayug0yuzv4g2v32uakaf9t0"), - ("m/44H/1022H/0H/0/6H", "account_rdx16yz4qrctz6843j79rvffunvdaz6l0kem266ddg45cnvw9x4g7hhuv5"), - ("m/44H/1022H/0H/0/7H", "account_rdx16x743lcavljkrfqs4y2slhwtnj3dyqn0glf4gq96m26ff64kl4rnc2"), - ("m/44H/1022H/0H/0/8H", "account_rdx169lnw3a4dtxy2jggv80dt2vn8tcyypyqjsver6322vx9januj7dwhu"), - ("m/44H/1022H/0H/0/9H", "account_rdx16xss3896jatrp7zcxgtuvsjy3cgs22s3wnv0qqcdp79jdkv6sufl79"), - ("m/44H/1022H/0H/0/0", "account_rdx168cucf0r7h07hhzezrz8uem7lh0xxhgvrlv8htvqwjdxfn2k4jrhtk"), - ("m/44H/1022H/0H/0/1", "account_rdx168vfmhy37elgswtcqwsnjn7hh906s04fwwmdqpmal8l7cc6gvxw4mj"), - ("m/44H/1022H/0H/0/2", "account_rdx16x3fhgxm2kl99tyzf3zg4qj7z9csfayzgenqj52aykvltsmyvkcknl"), - ("m/44H/1022H/0H/0/3", "account_rdx16xp6eppfc7fw0vmnj552hwjdxtwz9ymeynwm5fh5jx6kx76zmhdvan"), - ("m/44H/1022H/0H/0/4", "account_rdx16xjyhlxdjt7smrwawhnvxaw2arndkan6kvywq6us60aqet4p8tpm58"), - ("m/44H/1022H/0H/0/5", "account_rdx169gwv4uq7ftrenpf6z2lxxwq2mcqsplw3ghcs4n0g2a868l88yq4a4"), - ("m/44H/1022H/0H/0/6", "account_rdx1686u3ytx09nuw3c9nyauvx7jzdlcy2xqsz740axumt2k6v4jh00yc9"), - ("m/44H/1022H/0H/0/7", "account_rdx16yvcymlwp2ym4q9dj2ltzzq0df5vltx0xtcyanna78j0mejwf7gnwr"), - ("m/44H/1022H/0H/0/8", "account_rdx16ys0zmzfjfrsjlsjh8rpl8x0zj8jwt0l86up7rnyfwmy0rkd3xc8aw"), - ("m/44H/1022H/0H/0/9", "account_rdx168l3drrzhjnlc9a57hmvt6rykhj5l3hrljpj7juu52rgdcyuey7xft"), -] - -for vector in test_vectors: - call_and_check(vector[0], vector[1]) From 49ea48ddcdb8fe1365a58335837dd14b81b610b5 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Fri, 1 Nov 2024 21:55:52 +0100 Subject: [PATCH 3/7] migrate sign_tx --- ragger_tests/application_client/app.py | 65 ++++--- .../application_client/command_sender.py | 49 +++-- ragger_tests/application_client/curve.py | 12 +- .../application_client/response_unpacker.py | 35 +++- ragger_tests/test_get_app_version.py | 7 +- ragger_tests/test_sign_auth_ed25519.py | 28 +-- ragger_tests/test_sign_tx_ed25519.py | 183 +++++++++++++----- ragger_tests/test_sign_tx_secp256k1.py | 114 ++++------- test/test-sign-tx-ed25519.py | 97 ---------- test/test-sign-tx-secp256k1.py | 102 ---------- 10 files changed, 294 insertions(+), 398 deletions(-) delete mode 100755 test/test-sign-tx-ed25519.py delete mode 100755 test/test-sign-tx-secp256k1.py diff --git a/ragger_tests/application_client/app.py b/ragger_tests/application_client/app.py index ca3462cf..0b51e4da 100644 --- a/ragger_tests/application_client/app.py +++ b/ragger_tests/application_client/app.py @@ -2,7 +2,7 @@ from contextlib import contextmanager from ragger.backend.interface import BackendInterface, RAPDU -from response_unpacker import PK, LedgerModel, Version, ROLAResponseEd25519, ROLAResponseSecp256k1, ROLAResp, unpack_get_version_response +from response_unpacker import PK, LedgerAppSettings, LedgerModel, Version, SignedPayloadEd25519, SignedPayloadSecp256k1, Signed, unpack_get_version_response from command_sender import CommandSender, InsType from request_packer import RequestPacker from application_client.curve import C @@ -22,6 +22,10 @@ def get_device_id(self) -> str: rapdu = self.sender.get_device_id() return rapdu.data.hex() + def get_app_settings(self) -> LedgerAppSettings: + rapdu = self.sender.get_app_settings() + return LedgerAppSettings.unpack(raw=rapdu.data) + def get_device_model(self) -> LedgerModel: rapdu = self.sender.get_device_model() return LedgerModel.unpack(raw=rapdu.data) @@ -58,28 +62,21 @@ def verify_address( return response.data.decode('utf-8') @contextmanager - def __sign_rola( + def __sign_generic( self, - curve: C, + ins: InsType, navigate_path: Callable[[], None], navigate_sign: Callable[[], None], path: str, - dapp_def_addr: str, - origin: str, - nonce: bytes + payload: bytes ) -> Generator[RAPDU, None, None]: - rola_challenge = RequestPacker.pack_rola_request( - dapp_def_addr=dapp_def_addr, - origin=origin, - nonce=nonce - ) global maybe - with self.sender.send_sign_auth( - curve=curve, + with self.sender.send_sign_generic( + ins, navigate_path=navigate_path, navigate_sign=navigate_sign, path=path, - rola_challenge=rola_challenge + payload=payload ) as response: maybe = response yield maybe if maybe is not None else self.sender.get_async_response() @@ -93,7 +90,7 @@ def sign_rola( nonce: bytes, navigate_path: Callable[[], None] = lambda: None, navigate_sign: Callable[[], None] = lambda: None - ) -> ROLAResp: + ) -> Signed: """ Forms a ROLA Challenge from (`dapp_def_addr`, `origin`, `nonce`) and signs it using `path`. This will send two APDU requests to the Ledger, one first with @@ -113,21 +110,43 @@ def sign_rola( :param navigate_sign: A closure passed to confirm signing of the ROLA challenge, use `navigator.navigate_and_compare` to pass in interactions. **Pass an empty closure (`lambda: None`) if you are using a physical device** :type navigate_sign: Optional closure (`Callable`) - :return: The a ROLAEd25519Response parse from the APDU response. - :rtype: ROLAEd25519Response + :return: A signed payload parsed from the APDU response containing `(hash, signature, public_key)` + :rtype: Signed """ + rola_challenge = RequestPacker.pack_rola_request( + dapp_def_addr=dapp_def_addr, + origin=origin, + nonce=nonce + ) global response - with self.__sign_rola( - curve=curve, + with self.__sign_generic( + ins=curve.ins_sign_rola(), navigate_path=navigate_path, navigate_sign=navigate_sign, path=path, - dapp_def_addr=dapp_def_addr, - origin=origin, - nonce=nonce + payload=rola_challenge + ) as res: + response = res + return curve.unpack_signed(response=response.data) + + def sign_tx( + self, + curve: C, + path: str, + txn: bytes, + navigate_path: Callable[[], None] = lambda: None, + navigate_sign: Callable[[], None] = lambda: None + ) -> Signed: + global response + with self.__sign_generic( + ins=curve.ins_sign_tx(), + navigate_path=navigate_path, + navigate_sign=navigate_sign, + path=path, + payload=txn ) as res: response = res - return curve.unpack_rola_response(response=response.data) + return curve.unpack_signed(response=response.data) def get_public_key( self, diff --git a/ragger_tests/application_client/command_sender.py b/ragger_tests/application_client/command_sender.py index 9f8a68d2..ee6d589a 100644 --- a/ragger_tests/application_client/command_sender.py +++ b/ragger_tests/application_client/command_sender.py @@ -9,8 +9,10 @@ MAX_APDU_LEN: int = 255 -CLA: int = 0xAA -CLA2: int = 0xAC +class CommandContination(IntEnum): + Init = 0xAA + Continue = 0xAB + Finalize = 0xAC class P1(IntEnum): # Parameter 1 for first APDU number. @@ -30,7 +32,7 @@ def _send_ins( p1: P1 = P1.START, p2: P2 = P2.LAST, data: bytes = b"", - cla: int = CLA + cla: CommandContination = CommandContination.Init ) -> RAPDU: print(f"🛰️ sending data (exchange): {data.hex()}") rc = self.backend.exchange(cla=cla, ins=ins, p1=p1, p2=p2, data=data) @@ -46,7 +48,7 @@ def _async_send_ins( p1: P1 = P1.START, p2: P2 = P2.LAST, data: bytes = b"", - cla: int = CLA + cla: CommandContination = CommandContination.Init ) -> Generator[None, None, None]: print(f"🛰️ sending data (exchange_async): {data.hex()}") with self.backend.exchange_async(cla=cla, ins=ins, p1=p1, p2=p2, data=data): @@ -77,6 +79,11 @@ def get_device_model(self) -> RAPDU: ins=InsType.GET_DEVICE_MODEL, ) + def get_app_settings(self) -> RAPDU: + return self._send_ins( + ins=InsType.GET_APP_SETTINGS, + ) + def get_public_key( self, curve: C, @@ -117,15 +124,14 @@ def _send_derivation_path( pass @contextmanager - def send_sign_auth( + def send_sign_generic( self, - curve: C, + ins: InsType, navigate_path: Callable[[], None], navigate_sign: Callable[[], None], path: str, - rola_challenge: bytes + payload: bytes ): - ins = curve.ins_sign_rola() self._send_derivation_path( navigate=navigate_path, ins=ins, @@ -133,12 +139,25 @@ def send_sign_auth( ) self.backend._last_async_response = None + num_chunks = len(payload) // 255 + 1 + print(f"🛰️ sending {num_chunks} chunks") - with self._async_send_ins( - navigate=navigate_sign, - ins=ins, - data=rola_challenge, - cla=CLA2 # Continuation - ): - yield self.get_optional_async_response() + for i in range(num_chunks): + chunk = payload[i * 255:(i + 1) * 255] + is_last = i == num_chunks - 1 + + if not is_last: + self._send_ins( + ins=ins, + data=chunk, + cla=CommandContination.Continue + ) + else: + with self._async_send_ins( + navigate=navigate_sign, + ins=ins, + data=chunk, + cla=CommandContination.Finalize + ): + yield self.get_optional_async_response() \ No newline at end of file diff --git a/ragger_tests/application_client/curve.py b/ragger_tests/application_client/curve.py index be332908..3a6c82ba 100644 --- a/ragger_tests/application_client/curve.py +++ b/ragger_tests/application_client/curve.py @@ -2,7 +2,7 @@ from typing import TypeVar from application_client.instruction_type import InsType -from response_unpacker import PK, Curve25519PublicKey, ROLAResponseEd25519, ROLAResponseSecp256k1, ROLAResp, Secp256k1PublicKey +from response_unpacker import PK, Curve25519PublicKey, SignedPayloadEd25519, SignedPayloadSecp256k1, Signed, Secp256k1PublicKey class Curve(ABC): @@ -13,7 +13,7 @@ def unpack_pubkey(cls, response: bytes) -> PK: @classmethod @abstractmethod - def unpack_rola_response(cls, response: bytes) -> ROLAResp: + def unpack_signed(cls, response: bytes) -> Signed: pass @classmethod @@ -73,8 +73,8 @@ def ins_sign_pre_auth_raw(cls) -> InsType: return InsType.SIGN_PRE_AUTH_RAW_SECP256K1 @classmethod - def unpack_rola_response(cls, response: bytes) -> ROLAResponseSecp256k1: - return ROLAResponseSecp256k1.unpack_response(response) + def unpack_signed(cls, response: bytes) -> SignedPayloadSecp256k1: + return SignedPayloadSecp256k1.unpack_response(response) @classmethod def unpack_pubkey(cls, response: bytes) -> PK: @@ -108,8 +108,8 @@ def ins_sign_pre_auth_raw(cls) -> InsType: return InsType.SIGN_PRE_AUTH_RAW_ED25519 @classmethod - def unpack_rola_response(cls, response: bytes) -> ROLAResponseEd25519: - return ROLAResponseEd25519.unpack_response(response) + def unpack_signed(cls, response: bytes) -> SignedPayloadEd25519: + return SignedPayloadEd25519.unpack_response(response) @classmethod def unpack_pubkey(cls, response: bytes) -> PK: diff --git a/ragger_tests/application_client/response_unpacker.py b/ragger_tests/application_client/response_unpacker.py index e82460f7..f50adfd8 100644 --- a/ragger_tests/application_client/response_unpacker.py +++ b/ragger_tests/application_client/response_unpacker.py @@ -77,7 +77,7 @@ class Version(NamedTuple): patch: int @dataclass -class ROLAResponse(ABC): +class SignedPayload(ABC): key: PK hash: bytes signature: bytes @@ -116,7 +116,7 @@ def expected_len(cls) -> int: @classmethod @abstractmethod - def unpack_response(cls, response: bytes) -> ROLAResp: + def unpack_response(cls, response: bytes) -> Signed: pass @classmethod @@ -141,7 +141,7 @@ def raw_unpack_response(cls, response: bytes) -> Tuple[PK, bytes, bytes]: return key, hash, signature -class ROLAResponseEd25519(ROLAResponse): +class SignedPayloadEd25519(SignedPayload): key: Curve25519PublicKey @classmethod @@ -165,11 +165,11 @@ def expected_len(cls) -> int: return cls.sig_len() + cls.key_len() + cls.hash_len() @classmethod - def unpack_response(cls, response: bytes) -> ROLAResp: + def unpack_response(cls, response: bytes) -> Signed: (key, hash, signature) = cls.raw_unpack_response(response) - return ROLAResponseEd25519(key=key, hash=hash, signature=signature) + return SignedPayloadEd25519(key=key, hash=hash, signature=signature) -class ROLAResponseSecp256k1(ROLAResponse): +class SignedPayloadSecp256k1(SignedPayload): key: EllipticCurvePublicKey @classmethod @@ -185,9 +185,9 @@ def unpack_signature(cls, raw: bytes) -> bytes: return utils.encode_dss_signature(r, s) @classmethod - def unpack_response(cls, response: bytes) -> ROLAResp: + def unpack_response(cls, response: bytes) -> Signed: (key, hash, signature) = cls.raw_unpack_response(response) - return ROLAResponseSecp256k1(key=key, hash=hash, signature=signature) + return SignedPayloadSecp256k1(key=key, hash=hash, signature=signature) @classmethod def expected_len(cls) -> int: @@ -201,7 +201,7 @@ def sig_len(cls) -> int: def key_len(cls) -> int: return 33 -ROLAResp = TypeVar('ROLAResp', bound=ROLAResponse) +Signed = TypeVar('SignedPayload', bound=SignedPayload) def unpack_get_version_response(response: bytes) -> Version: assert len(response) == 3 @@ -217,3 +217,20 @@ class LedgerModel(IntEnum): def unpack(raw: bytes) -> LedgerModel: int_val = int.from_bytes(raw, byteorder='big', signed=False) return LedgerModel(int_val) + +class LedgerAppSettings(NamedTuple): + is_verbose_mode_enabled: bool + is_blind_signing_enabled: bool + + def unpack(raw: bytes) -> LedgerAppSettings: + print(f"📦🔮 unpacking LedgerAppSettings from raw: {raw.hex()} ") + is_blind = bool(int.from_bytes(raw[1:2], byteorder='big', signed=False)) + is_verbose = bool(int.from_bytes(raw[0:1], byteorder='big', signed=False)) + print(f"📦🔮 unpacking LedgerAppSettings is_verbose: {is_verbose}, is_blind: {is_blind} ") + + settings = LedgerAppSettings( + is_verbose_mode_enabled=is_verbose, + is_blind_signing_enabled=is_blind + ) + + return settings \ No newline at end of file diff --git a/ragger_tests/test_get_app_version.py b/ragger_tests/test_get_app_version.py index 50757f24..9c8cac8d 100644 --- a/ragger_tests/test_get_app_version.py +++ b/ragger_tests/test_get_app_version.py @@ -20,4 +20,9 @@ def test_get_version(backend: BackendInterface): expected = get_version_from_cargo_toml() app = App(backend) actual = app.get_version() - assert actual == expected \ No newline at end of file + assert actual == expected + +# def test_get_app_settings(backend: BackendInterface): +# app = App(backend) +# settings = app.get_app_settings() +# assert settings.is_blind_signing_enabled == True \ No newline at end of file diff --git a/ragger_tests/test_sign_auth_ed25519.py b/ragger_tests/test_sign_auth_ed25519.py index 77026678..bc11fc89 100644 --- a/ragger_tests/test_sign_auth_ed25519.py +++ b/ragger_tests/test_sign_auth_ed25519.py @@ -135,23 +135,23 @@ def test_sign_auth_ed25519_0(firmware, backend, navigator, test_name): def test_sign_auth_ed25519_1(firmware, backend, navigator, test_name): sign_auth_ed25519(firmware, backend, navigator, test_name, test_vectors[1]) -def test_sign_auth_ed25519_2(firmware, backend, navigator, test_name): - sign_auth_ed25519(firmware, backend, navigator, test_name, test_vectors[2]) +# def test_sign_auth_ed25519_2(firmware, backend, navigator, test_name): +# sign_auth_ed25519(firmware, backend, navigator, test_name, test_vectors[2]) -def test_sign_auth_ed25519_3(firmware, backend, navigator, test_name): - sign_auth_ed25519(firmware, backend, navigator, test_name, test_vectors[3]) +# def test_sign_auth_ed25519_3(firmware, backend, navigator, test_name): +# sign_auth_ed25519(firmware, backend, navigator, test_name, test_vectors[3]) -def test_sign_auth_ed25519_4(firmware, backend, navigator, test_name): - sign_auth_ed25519(firmware, backend, navigator, test_name, test_vectors[4]) +# def test_sign_auth_ed25519_4(firmware, backend, navigator, test_name): +# sign_auth_ed25519(firmware, backend, navigator, test_name, test_vectors[4]) -def test_sign_auth_ed25519_5(firmware, backend, navigator, test_name): - sign_auth_ed25519(firmware, backend, navigator, test_name, test_vectors[5]) +# def test_sign_auth_ed25519_5(firmware, backend, navigator, test_name): +# sign_auth_ed25519(firmware, backend, navigator, test_name, test_vectors[5]) -def test_sign_auth_ed25519_6(firmware, backend, navigator, test_name): - sign_auth_ed25519(firmware, backend, navigator, test_name, test_vectors[6]) +# def test_sign_auth_ed25519_6(firmware, backend, navigator, test_name): +# sign_auth_ed25519(firmware, backend, navigator, test_name, test_vectors[6]) -def test_sign_auth_ed25519_7(firmware, backend, navigator, test_name): - sign_auth_ed25519(firmware, backend, navigator, test_name, test_vectors[7]) +# def test_sign_auth_ed25519_7(firmware, backend, navigator, test_name): +# sign_auth_ed25519(firmware, backend, navigator, test_name, test_vectors[7]) -def test_sign_auth_ed25519_8(firmware, backend, navigator, test_name): - sign_auth_ed25519(firmware, backend, navigator, test_name, test_vectors[8]) \ No newline at end of file +# def test_sign_auth_ed25519_8(firmware, backend, navigator, test_name): +# sign_auth_ed25519(firmware, backend, navigator, test_name, test_vectors[8]) \ No newline at end of file diff --git a/ragger_tests/test_sign_tx_ed25519.py b/ragger_tests/test_sign_tx_ed25519.py index bb65adc0..986b597a 100644 --- a/ragger_tests/test_sign_tx_ed25519.py +++ b/ragger_tests/test_sign_tx_ed25519.py @@ -1,93 +1,174 @@ +from enum import Enum from pathlib import Path -from ragger.bip import pack_derivation_path +from typing import List, Optional from ragger.navigator import NavInsID -from cryptography.hazmat.primitives.asymmetric import ed25519 +from ragger.backend.interface import BackendInterface +from ragger.backend.speculos import SpeculosBackend +from ragger.firmware.structs import Firmware +from ragger.navigator.navigator import Navigator -CLA1 = 0xAA -CLA2 = 0xAC -INS = 0x41 +from ragger_tests.application_client.app import App +from ragger_tests.application_client.curve import C, Curve25519 DATA_PATH = str(Path(__file__).parent.joinpath("data").absolute()) + "/" ROOT_SCREENSHOT_PATH = Path(__file__).parent.resolve() +class BlindSigningSettings(Enum): + DONT_CHECK_SETTINGS = "DONT_CHECK_SETTINGS" + FAIL_IF_OFF = "FAIL_IF_OFF" + FAIL_IF_ON = "FAIL_IF_ON" + SKIP_IF_OFF = "SKIP_IF_OFF" + SKIP_IF_ON = "SKIP_IF_ON" + + def should_check(self) -> bool: + return self != BlindSigningSettings.DONT_CHECK_SETTINGS + + def should_be_off(self) -> bool: + return self == BlindSigningSettings.SKIP_IF_ON or self == BlindSigningSettings.FAIL_IF_ON + + def should_be_on(self) -> bool: + return self == BlindSigningSettings.SKIP_IF_OFF or self == BlindSigningSettings.FAIL_IF_OFF + + def should_fail(self) -> bool: + return self == BlindSigningSettings.FAIL_IF_OFF or self == BlindSigningSettings.FAIL_IF_ON + + def should_skip(self) -> bool: + return self == BlindSigningSettings.SKIP_IF_ON or self == BlindSigningSettings.SKIP_IF_OFF + +def sign_tx( + curve: C, + path: str, + firmware: Firmware, + backend: BackendInterface, + navigator: Navigator, + click_count: int, + txn: bytes, + test_name: str, + blind_signing_settings: BlindSigningSettings +): + clicks: List[NavInsID] = [] -def read_file(file): - with open(DATA_PATH + file, "rb") as f: - return f.read() - - -def send_derivation_path(backend, path, navigator): - with backend.exchange_async(cla=CLA1, ins=INS, data=pack_derivation_path(path)) as response: - navigator.navigate([NavInsID.RIGHT_CLICK]) - - -def send_tx_intent(txn, click_count, backend, navigator, firmware, test_name): - num_chunks = len(txn) // 255 + 1 - if click_count > 0: clicks = [NavInsID.RIGHT_CLICK] * click_count clicks.append(NavInsID.BOTH_CLICK) else: clicks = [NavInsID.RIGHT_CLICK] - for i in range(num_chunks): - chunk = txn[i * 255:(i + 1) * 255] + def navigate_path(): + navigator.navigate([NavInsID.RIGHT_CLICK]) - if i != num_chunks - 1: - cls = 0xAB - backend.exchange(cla=cls, ins=INS, p1=0, p2=0, data=chunk) + def navigate_sign(): + if firmware.device.startswith("nano"): + navigator.navigate_and_compare(ROOT_SCREENSHOT_PATH, test_name, clicks) + + app = App(backend) + + if blind_signing_settings.should_check() and not isinstance(backend, SpeculosBackend): + app_settings = app.get_app_settings() + is_blind_signing_enabled = app_settings.is_blind_signing_enabled + if is_blind_signing_enabled and blind_signing_settings.should_be_off(): + errmsg = "⚙️ ❌ Blind signing is on, but required to be off." + print(errmsg) + if blind_signing_settings.should_fail(): + raise ValueError(errmsg) + elif blind_signing_settings.should_skip(): + print("🙅‍♀️ Skipping test") + return + elif not is_blind_signing_enabled and blind_signing_settings.should_be_on(): + errmsg = "⚙️ ❌ Blind signing is off, but required to be on." + print(errmsg) + if blind_signing_settings.should_fail(): + raise ValueError(errmsg) + elif blind_signing_settings.should_skip(): + print("🙅‍♀️ Skipping test") + return else: - cls = 0xAC - with backend.exchange_async(cla=cls, ins=INS, p1=0, p2=0, data=chunk) as response: - if firmware.device.startswith("nano"): - navigator.navigate_and_compare(ROOT_SCREENSHOT_PATH, test_name, clicks) - return backend.last_async_response.data + enabled_or_not = "ENABLED" if is_blind_signing_enabled else "DISABLED" + print(f"✅ Blind signing is: {enabled_or_not} which it was required to be.") + response = app.sign_tx( + curve=curve, + path=path, + txn=txn, + navigate_path=navigate_path, + navigate_sign=navigate_sign, + ) -def sign_tx_ed25519(firmware, backend, navigator, click_count, file_name, test_name): - send_derivation_path(backend, "m/44'/1022'/12'/525'/1460'/0'", navigator) - txn = read_file(file_name) - - try: - rc = send_tx_intent(txn, click_count, backend, navigator, firmware, test_name) - except Exception as e: - if click_count == 0: - return - - pubkey = ed25519.Ed25519PublicKey.from_public_bytes(bytes(rc[64:96])) - try: - pubkey.verify(bytes(rc[0:64]), bytes(rc[96:128])) - except Exception as e: - print("Invalid signature ", e) + assert response.verify_signature() +def read_file(file: str) -> bytes: + with open(DATA_PATH + file, "rb") as f: + return f.read() -def test_sign_tx_ed25519_call_function(firmware, backend, navigator, test_name): - sign_tx_ed25519(firmware, backend, navigator, 0, "call_function.txn", test_name) +def sign_tx_with_file_name( + curve: C, + path: str, + firmware: Firmware, + backend: BackendInterface, + navigator: Navigator, + click_count: int, + file_name: str, + test_name: str, + blind_signing_settings: BlindSigningSettings +): + txn = read_file(file=file_name) + sign_tx( + curve=curve, + path=path, + firmware=firmware, + backend=backend, + navigator=navigator, + click_count=click_count, + txn=txn, + test_name=test_name, + blind_signing_settings=blind_signing_settings + ) + +def sign_tx_ed25519( + firmware: Firmware, + backend: BackendInterface, + navigator: Navigator, + click_count: int, + file_name: str, + test_name: str, + blind_signing_settings: BlindSigningSettings = BlindSigningSettings.DONT_CHECK_SETTINGS +): + sign_tx_with_file_name( + curve=Curve25519, + path="m/44'/1022'/12'/525'/1460'/0'", + firmware=firmware, + backend=backend, + navigator=navigator, + click_count=click_count, + file_name=file_name, + test_name=test_name, + blind_signing_settings=blind_signing_settings + ) + +# def test_sign_tx_ed25519_call_function(firmware, backend, navigator, test_name): +# sign_tx_ed25519( +# firmware, backend, navigator, 0, "call_function.txn", test_name, +# blind_signing_settings=BlindSigningSettings.SKIP_IF_OFF +# ) def test_sign_tx_ed25519_simple_transfer(firmware, backend, navigator, test_name): sign_tx_ed25519(firmware, backend, navigator, 13, "simple_transfer.txn", test_name) - def test_sign_tx_ed25519_simple_transfer_new_format(firmware, backend, navigator, test_name): sign_tx_ed25519(firmware, backend, navigator, 10, "simple_transfer_new_format.txn", test_name) - def test_sign_tx_ed25519_simple_transfer_nft(firmware, backend, navigator, test_name): sign_tx_ed25519(firmware, backend, navigator, 13, "simple_transfer_nft.txn", test_name) - def test_sign_tx_ed25519_simple_transfer_nft_by_id(firmware, backend, navigator, test_name): sign_tx_ed25519(firmware, backend, navigator, 13, "simple_transfer_nft_by_id.txn", test_name) - def test_sign_tx_ed25519_simple_transfer_nft_new_format(firmware, backend, navigator, test_name): sign_tx_ed25519(firmware, backend, navigator, 13, "simple_transfer_nft_new_format.txn", test_name) - def test_sign_tx_ed25519_simple_transfer_nft_by_id_new_format(firmware, backend, navigator, test_name): sign_tx_ed25519(firmware, backend, navigator, 13, "simple_transfer_nft_by_id_new_format.txn", test_name) - def test_sign_tx_ed25519_simple_transfer_with_multiple_locked_fees(firmware, backend, navigator, test_name): sign_tx_ed25519(firmware, backend, navigator, 10, "simple_transfer_with_multiple_locked_fees.txn", test_name) diff --git a/ragger_tests/test_sign_tx_secp256k1.py b/ragger_tests/test_sign_tx_secp256k1.py index 7ff8af6e..f562af84 100644 --- a/ragger_tests/test_sign_tx_secp256k1.py +++ b/ragger_tests/test_sign_tx_secp256k1.py @@ -1,101 +1,55 @@ -from pathlib import Path -from ragger.bip import pack_derivation_path -from ragger.navigator import NavInsID -from cryptography.hazmat.primitives.asymmetric import ec, utils -from cryptography.hazmat.primitives import hashes - - -CLA1 = 0xAA -CLA2 = 0xAC -INS = 0x51 - -DATA_PATH = str(Path(__file__).parent.joinpath("data").absolute()) + "/" -ROOT_SCREENSHOT_PATH = Path(__file__).parent.resolve() - - -def read_file(file): - with open(DATA_PATH + file, "rb") as f: - return f.read() - - -def send_derivation_path(backend, path, navigator): - with backend.exchange_async(cla=CLA1, ins=INS, data=pack_derivation_path(path)) as response: - navigator.navigate([NavInsID.RIGHT_CLICK]) - - -def send_tx_intent(txn, click_count, backend, navigator, firmware, test_name): - num_chunks = len(txn) // 255 + 1 - - if click_count > 0: - clicks = [NavInsID.RIGHT_CLICK] * click_count - clicks.append(NavInsID.BOTH_CLICK) - else: - clicks = [NavInsID.RIGHT_CLICK] - - for i in range(num_chunks): - chunk = txn[i * 255:(i + 1) * 255] - - if i != num_chunks - 1: - cls = 0xAB - backend.exchange(cla=cls, ins=INS, p1=0, p2=0, data=chunk) - else: - cls = 0xAC - with backend.exchange_async(cla=cls, ins=INS, p1=0, p2=0, data=chunk) as response: - if firmware.device.startswith("nano"): - navigator.navigate_and_compare(ROOT_SCREENSHOT_PATH, test_name, clicks) - return backend.last_async_response.data - - -def sign_tx_secp256k1(firmware, backend, navigator, click_count, file_name, test_name): - send_derivation_path(backend, "m/44'/1022'/10'/525'/1238'", navigator) - txn = read_file(file_name) - - try: - rc = send_tx_intent(txn, click_count, backend, navigator, firmware, test_name) - except Exception as e: - if click_count == 0: - return - - r = int.from_bytes(rc[1:33], byteorder='big', signed=False) - s = int.from_bytes(rc[33:65], byteorder='big', signed=False) - signature = utils.encode_dss_signature(int(r), int(s)) - pubkey = ec.EllipticCurvePublicKey.from_encoded_point(ec.SECP256K1(), bytes(rc[65:98])) - try: - # Note that Prehashed parameter is irrelevant here, we just need to pass something known to the library - pubkey.verify(signature, bytes(rc[98:130]), ec.ECDSA(utils.Prehashed(hashes.SHA256()))) - print("Success") - assert rc[98:130].hex() == vector[0], "Invalid calculated hash\nExpected: " + vector[0] + "\nReceived: " + rc[98:130].hex() - except Exception as e: - print("Invalid signature ", e) - - -def test_sign_tx_secp256k1_call_function(firmware, backend, navigator, test_name): - sign_tx_secp256k1(firmware, backend, navigator, 0, "call_function.txn", test_name) - +from ragger.backend.interface import BackendInterface +from ragger.firmware.structs import Firmware +from ragger.navigator.navigator import Navigator + +from ragger_tests.application_client.curve import SECP256K1 +from ragger_tests.test_sign_tx_ed25519 import BlindSigningSettings, sign_tx_with_file_name + +def sign_tx_secp256k1( + firmware: Firmware, + backend: BackendInterface, + navigator: Navigator, + click_count: int, + file_name: str, + test_name: str, + blind_signing_settings: BlindSigningSettings = BlindSigningSettings.DONT_CHECK_SETTINGS +): + sign_tx_with_file_name( + curve=SECP256K1, + path="m/44'/1022'/10'/525'/1238'", + firmware=firmware, + backend=backend, + navigator=navigator, + click_count=click_count, + file_name=file_name, + test_name=test_name, + blind_signing_settings=blind_signing_settings + ) + + +# def test_sign_tx_secp256k1_call_function(firmware, backend, navigator, test_name): +# sign_tx_secp256k1( +# firmware, backend, navigator, 0, "call_function.txn", test_name, +# blind_signing_settings=BlindSigningSettings.SKIP_IF_OFF +# ) def test_sign_tx_secp256k1_simple_transfer(firmware, backend, navigator, test_name): sign_tx_secp256k1(firmware, backend, navigator, 13, "simple_transfer.txn", test_name) - def test_sign_tx_secp256k1_simple_transfer_new_format(firmware, backend, navigator, test_name): sign_tx_secp256k1(firmware, backend, navigator, 10, "simple_transfer_new_format.txn", test_name) - def test_sign_tx_secp256k1_simple_transfer_nft(firmware, backend, navigator, test_name): sign_tx_secp256k1(firmware, backend, navigator, 13, "simple_transfer_nft.txn", test_name) - def test_sign_tx_secp256k1_simple_transfer_nft_by_id(firmware, backend, navigator, test_name): sign_tx_secp256k1(firmware, backend, navigator, 13, "simple_transfer_nft_by_id.txn", test_name) - def test_sign_tx_secp256k1_simple_transfer_nft_new_format(firmware, backend, navigator, test_name): sign_tx_secp256k1(firmware, backend, navigator, 13, "simple_transfer_nft_new_format.txn", test_name) - def test_sign_tx_secp256k1_simple_transfer_nft_by_id_new_format(firmware, backend, navigator, test_name): sign_tx_secp256k1(firmware, backend, navigator, 13, "simple_transfer_nft_by_id_new_format.txn", test_name) - def test_sign_tx_secp256k1_simple_transfer_with_multiple_locked_fees(firmware, backend, navigator, test_name): sign_tx_secp256k1(firmware, backend, navigator, 10, "simple_transfer_with_multiple_locked_fees.txn", test_name) diff --git a/test/test-sign-tx-ed25519.py b/test/test-sign-tx-ed25519.py deleted file mode 100755 index 651b2bb3..00000000 --- a/test/test-sign-tx-ed25519.py +++ /dev/null @@ -1,97 +0,0 @@ -# Test SignTxEd25519 instruction (mode: do not show digest) - -import sys -import os - -from ledgerblue.comm import getDongle -from ledgerblue.commTCP import getDongle as getDongleTCP -from cryptography.hazmat.primitives.asymmetric import ed25519 - -# disable printing stack trace -sys.tracebacklimit = 0 - -if os.environ.get('USE_SPECULOS') is not None: - dongle = getDongleTCP(debug=False) -else: - dongle = getDongle(False) - -instructionClass = "AA" -instructionCode = "41" -p1 = "00" -p2 = "00" -dataLength = "00" - -print("Testing", "SignTxEd25519", instructionCode) - - -def list_files(): - dir_path = "data" - res = [] - for path in os.listdir(dir_path): - if os.path.isfile(os.path.join(dir_path, path)): - res.append(os.path.join(dir_path, path)) - return res - - -def read_file(file): - print("Reading ", file) - with open(file, "rb") as f: - return f.read() - - -def encode_bip32(path): - elements = path.replace('H', "'").replace('"', "'").split('/') - result = (len(elements) - 1).to_bytes(1, 'little').hex() - for i in range(1, len(elements)): - num = 0x80000000 if elements[i].endswith("'") else 0 - num += int(elements[i].replace("'", "")) - result += num.to_bytes(4, 'big').hex() - return result - - -def send_tx_intent(txn): - num_chunks = len(txn) // 255 + 1 - # print("Sending txn (", len(txn), " bytes, ", num_chunks, " chunk(s))") - for i in range(num_chunks): - chunk = txn[i * 255:(i + 1) * 255] - cls = "AC" if i == num_chunks - 1 else "AB" - data_length = len(chunk).to_bytes(1, 'little').hex() - - # print("Chunk:", i, "data:", chunk.hex(), "len:", data_length, "cls:", cls) - - try: - rc = dongle.exchange(bytes.fromhex(cls + instructionCode + p1 + p2 + data_length + chunk.hex())) - except Exception as e: - print("Error sending txn chunk: ", e) - return None - return rc - - -def send_derivation_path(bip_path): - path_data = encode_bip32(bip_path) - data_length = int(len(path_data) / 2).to_bytes(1, 'little').hex() - # print("Sending derivation path: ", bip_path, ", data_len = ", data_length) - - try: - return dongle.exchange(bytes.fromhex(instructionClass + instructionCode + p1 + p2 + data_length + path_data)) - except Exception as e: - print("Error sending derivation path: ", e) - return None - - -for file_name in list_files(): - if not file_name.endswith(".txn"): - continue - data = read_file(file_name) - send_derivation_path("m/44H/1022H/12H/525H/1460H/0H") - rc = send_tx_intent(data) - - if rc is None: - print("Failed") - else: - pubkey = ed25519.Ed25519PublicKey.from_public_bytes(bytes(rc[64:96])) - try: - pubkey.verify(bytes(rc[0:64]), bytes(rc[96:128])) - print("Success") - except Exception as e: - print("Invalid signature ", e) diff --git a/test/test-sign-tx-secp256k1.py b/test/test-sign-tx-secp256k1.py deleted file mode 100755 index 03df708e..00000000 --- a/test/test-sign-tx-secp256k1.py +++ /dev/null @@ -1,102 +0,0 @@ -# Test SignTxSecp256k1 instruction (mode: do not show digest) - -import sys -import os - -from ledgerblue.comm import getDongle -from ledgerblue.commTCP import getDongle as getDongleTCP -from cryptography.hazmat.primitives.asymmetric import ec, utils -from cryptography.hazmat.primitives import hashes - -# disable printing stack trace -sys.tracebacklimit = 0 - -if os.environ.get('USE_SPECULOS') is not None: - dongle = getDongleTCP(debug=False) -else: - dongle = getDongle(False) - -instructionClass = "AA" -instructionCode = "51" -p1 = "00" -p2 = "00" -dataLength = "00" - -print("Testing", "SignTxSecp256k1", instructionCode) - - -def list_files(): - dir_path = "data" - res = [] - for path in os.listdir(dir_path): - if os.path.isfile(os.path.join(dir_path, path)): - res.append(os.path.join(dir_path, path)) - return res - - -def read_file(file): - print("Reading ", file) - with open(file, "rb") as f: - return f.read() - - -def encode_bip32(path): - elements = path.replace('H', "'").replace('"', "'").split('/') - result = (len(elements) - 1).to_bytes(1, 'little').hex() - for i in range(1, len(elements)): - num = 0x80000000 if elements[i].endswith("'") else 0 - num += int(elements[i].replace("'", "")) - result += num.to_bytes(4, 'big').hex() - return result - - -def send_tx_intent(txn): - num_chunks = len(txn) // 255 + 1 - # print("Sending txn (", len(txn), " bytes, ", num_chunks, " chunk(s))") - for i in range(num_chunks): - chunk = txn[i * 255:(i + 1) * 255] - cls = "AC" if i == num_chunks - 1 else "AB" - data_length = len(chunk).to_bytes(1, 'little').hex() - - # print("Chunk:", i, "data:", chunk.hex(), "len:", data_length, "cls:", cls) - - try: - rc = dongle.exchange(bytes.fromhex(cls + instructionCode + p1 + p2 + data_length + chunk.hex())) - except Exception as e: - print("Error sending txn chunk: ", e) - return None - return rc - - -def send_derivation_path(bip_path): - path_data = encode_bip32(bip_path) - data_length = int(len(path_data) / 2).to_bytes(1, 'little').hex() - # print("Sending derivation path: ", bip_path, ", data_len = ", data_length) - - try: - return dongle.exchange(bytes.fromhex(instructionClass + instructionCode + p1 + p2 + data_length + path_data)) - except Exception as e: - print("Error sending derivation path: ", e) - return None - - -for file_name in list_files(): - if not file_name.endswith(".txn"): - continue - data = read_file(file_name) - send_derivation_path("m/44H/1022H/10H/525H/1238H") - rc = send_tx_intent(data) - - if rc is None: - print("Failed") - else: - r = int.from_bytes(rc[1:33], byteorder='big', signed=False) - s = int.from_bytes(rc[33:65], byteorder='big', signed=False) - signature = utils.encode_dss_signature(int(r), int(s)) - pubkey = ec.EllipticCurvePublicKey.from_encoded_point(ec.SECP256K1(), bytes(rc[65:98])) - try: - # Note that Prehashed parameter is irrelevant here, we just need to pass something known to the library - pubkey.verify(signature, bytes(rc[98:130]), ec.ECDSA(utils.Prehashed(hashes.SHA256()))) - print("Success") - except Exception as e: - print("Invalid signature ", e) From 67b8657620e57130d9f345b3c6fda1aa0a709e25 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Sat, 2 Nov 2024 11:11:53 +0100 Subject: [PATCH 4/7] migrate sign preauth --- ragger_tests/application_client/app.py | 23 ++++ .../application_client/command_sender.py | 3 +- ragger_tests/conftest.py | 2 +- ragger_tests/test_navigation.py | 2 +- ragger_tests/test_sign_auth_ed25519.py | 2 +- .../test_sign_preauth_hash_ed25519.py | 129 ++++++++++-------- .../test_sign_preauth_hash_secp256k1.py | 93 +++---------- ragger_tests/test_sign_tx_ed25519.py | 57 ++++---- ragger_tests/test_verify_address_ed25519.py | 2 +- test/test-sign-preauth-hash-ed25519.py | 76 ----------- test/test-sign-preauth-hash-secp256k1.py | 81 ----------- 11 files changed, 154 insertions(+), 316 deletions(-) delete mode 100755 test/test-sign-preauth-hash-ed25519.py delete mode 100755 test/test-sign-preauth-hash-secp256k1.py diff --git a/ragger_tests/application_client/app.py b/ragger_tests/application_client/app.py index 0b51e4da..a430545c 100644 --- a/ragger_tests/application_client/app.py +++ b/ragger_tests/application_client/app.py @@ -1,3 +1,4 @@ +import hashlib from typing import Generator, Callable, Optional from contextlib import contextmanager @@ -147,6 +148,28 @@ def sign_tx( ) as res: response = res return curve.unpack_signed(response=response.data) + + def sign_preauth_hash( + self, + curve: C, + path: str, + message_to_hash: bytes, + navigate_path: Callable[[], None] = lambda: None, + navigate_sign: Callable[[], None] = lambda: None + ) -> Signed: + hash_calculator = hashlib.blake2b(digest_size=32) + hash_calculator.update(message_to_hash) + hash = hash_calculator.digest() + global response + with self.__sign_generic( + ins=curve.ins_sign_pre_auth_hash(), + navigate_path=navigate_path, + navigate_sign=navigate_sign, + path=path, + payload=hash + ) as res: + response = res + return curve.unpack_signed(response=response.data) def get_public_key( self, diff --git a/ragger_tests/application_client/command_sender.py b/ragger_tests/application_client/command_sender.py index ee6d589a..24e0c1eb 100644 --- a/ragger_tests/application_client/command_sender.py +++ b/ragger_tests/application_client/command_sender.py @@ -137,10 +137,9 @@ def send_sign_generic( ins=ins, path=path ) - self.backend._last_async_response = None num_chunks = len(payload) // 255 + 1 - print(f"🛰️ sending {num_chunks} chunks") + print(f"🛰️ sending #{num_chunks} chunks") for i in range(num_chunks): chunk = payload[i * 255:(i + 1) * 255] diff --git a/ragger_tests/conftest.py b/ragger_tests/conftest.py index 6e4e83b6..5a5c8e00 100644 --- a/ragger_tests/conftest.py +++ b/ragger_tests/conftest.py @@ -28,7 +28,7 @@ @pytest.fixture(scope="class", autouse=True) def clear_pending_review(firmware, navigator: Navigator): # Press a button to clear the pending review - if firmware.device.startswith("nano"): + if firmware.is_nano: if navigator._backend.compare_screen_with_text("Pending"): print("Clearing pending review") instructions = [ diff --git a/ragger_tests/test_navigation.py b/ragger_tests/test_navigation.py index 2fb30b45..6575737e 100644 --- a/ragger_tests/test_navigation.py +++ b/ragger_tests/test_navigation.py @@ -11,7 +11,7 @@ def test_dashboard_navigation(firmware, backend, navigator, test_name): - if firmware.device.startswith("nano"): + if firmware.is_nano: navigator.navigate_and_compare(ROOT_SCREENSHOT_PATH, test_name, [NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK, ], screen_change_before_first_instruction=False) diff --git a/ragger_tests/test_sign_auth_ed25519.py b/ragger_tests/test_sign_auth_ed25519.py index bc11fc89..e90f47c1 100644 --- a/ragger_tests/test_sign_auth_ed25519.py +++ b/ragger_tests/test_sign_auth_ed25519.py @@ -28,7 +28,7 @@ def navigate_path(): navigator.navigate([NavInsID.RIGHT_CLICK]) def navigate_sign(): - if firmware.device.startswith("nano"): + if firmware.is_nano: navigator.navigate_and_compare( path=ROOT_SCREENSHOT_PATH, test_case_name=test_name, diff --git a/ragger_tests/test_sign_preauth_hash_ed25519.py b/ragger_tests/test_sign_preauth_hash_ed25519.py index ee304fe5..c514f3a1 100644 --- a/ragger_tests/test_sign_preauth_hash_ed25519.py +++ b/ragger_tests/test_sign_preauth_hash_ed25519.py @@ -1,59 +1,84 @@ -from typing import Generator from pathlib import Path -from ragger.bip import pack_derivation_path from ragger.navigator import NavInsID -from contextlib import contextmanager -from cryptography.hazmat.primitives.asymmetric import ed25519 -import hashlib - -ROOT_SCREENSHOT_PATH = Path(__file__).parent.resolve() +from ragger.navigator import NavInsID +from ragger.backend.interface import BackendInterface +from ragger.firmware.structs import Firmware +from ragger.navigator.navigator import Navigator +from ragger.backend.speculos import SpeculosBackend -CLA1 = 0xAA -CLA2 = 0xAC -INS = 0xA1 +from ragger_tests.application_client.app import App +from ragger_tests.application_client.curve import C, Curve25519 +from ragger_tests.test_sign_tx_ed25519 import BlindSigningSettings +ROOT_SCREENSHOT_PATH = Path(__file__).parent.resolve() -def enable_blind_signing(navigator, test_name): +def enable_blind_signing(navigator: Navigator): print("Enable blind signing") - navigator.navigate([NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK, NavInsID.BOTH_CLICK, # Settings - NavInsID.RIGHT_CLICK, NavInsID.BOTH_CLICK, # Blind signing - NavInsID.RIGHT_CLICK, NavInsID.BOTH_CLICK, # Enable - NavInsID.LEFT_CLICK, NavInsID.LEFT_CLICK], # Main screen - screen_change_before_first_instruction=False) - - -def send_derivation_path(backend, path, navigator): - with backend.exchange_async(cla=CLA1, ins=INS, data=pack_derivation_path(path)) as response: + navigator.navigate( + instructions=[ + NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK, NavInsID.BOTH_CLICK, # Settings + NavInsID.RIGHT_CLICK, NavInsID.BOTH_CLICK, # Blind signing + NavInsID.RIGHT_CLICK, NavInsID.BOTH_CLICK, # Enable + NavInsID.LEFT_CLICK, NavInsID.LEFT_CLICK # Main screen + ], + screen_change_before_first_instruction=False + ) + +def sign_preauth_hash( + curve: C, + path: str, + firmware: Firmware, + backend: BackendInterface, + navigator: Navigator, + test_name: str, + message_to_hash: bytes +): + if isinstance(backend, SpeculosBackend): + enable_blind_signing(navigator) + elif BlindSigningSettings.SKIP_IF_OFF.should_abort_execution_due_to_blind_sign(backend): + return + + def navigate_path(): navigator.navigate([NavInsID.RIGHT_CLICK]) - -@contextmanager -def send_preauth_hash_request(backend, vector) -> Generator[None, None, None]: - hash_calculator = hashlib.blake2b(digest_size=32) - hash_calculator.update(vector) - data = hash_calculator.hexdigest() - - with backend.exchange_async(cla=CLA2, ins=INS, data=bytes.fromhex(data)) as response: - yield response - - -def sign_preauth_hash_ed25519(firmware, backend, navigator, test_name, vector): - enable_blind_signing(navigator, test_name) - send_derivation_path(backend, "m/44'/1022'/12'/525'/1460'/0'", navigator) - - with send_preauth_hash_request(backend, vector): - if firmware.device.startswith("nano"): - navigator.navigate_and_compare(ROOT_SCREENSHOT_PATH, test_name, - [NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK, - NavInsID.RIGHT_CLICK, NavInsID.BOTH_CLICK]) - - rc = backend.last_async_response.data - pubkey = ed25519.Ed25519PublicKey.from_public_bytes(bytes(rc[64:96])) - try: - pubkey.verify(bytes(rc[0:64]), bytes(rc[96:128])) - except Exception as e: - print("Invalid signature ", e) - + def navigate_sign(): + if firmware.is_nano: + navigator.navigate_and_compare( + path=ROOT_SCREENSHOT_PATH, + test_case_name=test_name, + instructions=[ + NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK, + NavInsID.RIGHT_CLICK, NavInsID.BOTH_CLICK + ] + ) + + app = App(backend) + response = app.sign_preauth_hash( + curve=curve, + path=path, + message_to_hash=message_to_hash, + navigate_path=navigate_path, + navigate_sign=navigate_sign, + ) + + assert response.verify_signature() + +def sign_preauth_hash_ed25519( + firmware: Firmware, + backend: BackendInterface, + navigator: Navigator, + test_name: str, + message_to_hash: bytes +): + sign_preauth_hash( + curve=Curve25519, + path="m/44'/1022'/12'/525'/1460'/0'", + firmware=firmware, + backend=backend, + navigator=navigator, + test_name=test_name, + message_to_hash=message_to_hash + ) def test_sign_preauth_hash_ed25519_0(firmware, backend, navigator, test_name): sign_preauth_hash_ed25519(firmware, backend, navigator, test_name, b'0') @@ -61,34 +86,26 @@ def test_sign_preauth_hash_ed25519_0(firmware, backend, navigator, test_name): def test_sign_preauth_hash_ed25519_1(firmware, backend, navigator, test_name): sign_preauth_hash_ed25519(firmware, backend, navigator, test_name, b'1') - def test_sign_preauth_hash_ed25519_2(firmware, backend, navigator, test_name): sign_preauth_hash_ed25519(firmware, backend, navigator, test_name, b'2') - def test_sign_preauth_hash_ed25519_3(firmware, backend, navigator, test_name): sign_preauth_hash_ed25519(firmware, backend, navigator, test_name, b'3') - def test_sign_preauth_hash_ed25519_4(firmware, backend, navigator, test_name): sign_preauth_hash_ed25519(firmware, backend, navigator, test_name, b'4') - def test_sign_preauth_hash_ed25519_5(firmware, backend, navigator, test_name): sign_preauth_hash_ed25519(firmware, backend, navigator, test_name, b'5') - def test_sign_preauth_hash_ed25519_6(firmware, backend, navigator, test_name): sign_preauth_hash_ed25519(firmware, backend, navigator, test_name, b'6') - def test_sign_preauth_hash_ed25519_7(firmware, backend, navigator, test_name): sign_preauth_hash_ed25519(firmware, backend, navigator, test_name, b'7') - def test_sign_preauth_hash_ed25519_8(firmware, backend, navigator, test_name): sign_preauth_hash_ed25519(firmware, backend, navigator, test_name, b'8') - def test_sign_preauth_hash_ed25519_9(firmware, backend, navigator, test_name): sign_preauth_hash_ed25519(firmware, backend, navigator, test_name, b'9') diff --git a/ragger_tests/test_sign_preauth_hash_secp256k1.py b/ragger_tests/test_sign_preauth_hash_secp256k1.py index ba4aaf7c..f1d21956 100644 --- a/ragger_tests/test_sign_preauth_hash_secp256k1.py +++ b/ragger_tests/test_sign_preauth_hash_secp256k1.py @@ -1,66 +1,25 @@ -from typing import Generator -from pathlib import Path -from ragger.bip import pack_derivation_path -from ragger.navigator import NavInsID -from contextlib import contextmanager -from cryptography.hazmat.primitives.asymmetric import ec, utils -from cryptography.hazmat.primitives import hashes -import hashlib - -ROOT_SCREENSHOT_PATH = Path(__file__).parent.resolve() - -CLA1 = 0xAA -CLA2 = 0xAC -INS = 0xA2 - - -def enable_blind_signing(navigator, test_name): - print("Enable blind signing") - navigator.navigate([NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK, NavInsID.BOTH_CLICK, # Settings - NavInsID.RIGHT_CLICK, NavInsID.BOTH_CLICK, # Blind signing - NavInsID.RIGHT_CLICK, NavInsID.BOTH_CLICK, # Enable - NavInsID.LEFT_CLICK, NavInsID.LEFT_CLICK], # Main screen - screen_change_before_first_instruction=False) - - -def send_derivation_path(backend, path, navigator): - with backend.exchange_async(cla=CLA1, ins=INS, data=pack_derivation_path(path)) as response: - navigator.navigate([NavInsID.RIGHT_CLICK]) - - -@contextmanager -def send_preauth_hash_request(backend, vector) -> Generator[None, None, None]: - hash_calculator = hashlib.blake2b(digest_size=32) - hash_calculator.update(vector) - data = hash_calculator.hexdigest() - - with backend.exchange_async(cla=CLA2, ins=INS, data=bytes.fromhex(data)) as response: - yield response - - -def sign_preauth_hash_secp256k1(firmware, backend, navigator, test_name, vector): - enable_blind_signing(navigator, test_name) - send_derivation_path(backend, "m/44'/1022'/10'/525'/1238'", navigator) - - with send_preauth_hash_request(backend, vector): - if firmware.device.startswith("nano"): - navigator.navigate_and_compare(ROOT_SCREENSHOT_PATH, test_name, - [NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK, - NavInsID.RIGHT_CLICK, NavInsID.BOTH_CLICK]) - - rc = backend.last_async_response.data - r = int.from_bytes(rc[1:33], byteorder='big', signed=False) - s = int.from_bytes(rc[33:65], byteorder='big', signed=False) - signature = utils.encode_dss_signature(int(r), int(s)) - pubkey = ec.EllipticCurvePublicKey.from_encoded_point(ec.SECP256K1(), bytes(rc[65:98])) - try: - # Note that Prehashed parameter is irrelevant here, we just need to pass something known to the library - pubkey.verify(signature, bytes(rc[98:130]), ec.ECDSA(utils.Prehashed(hashes.SHA256()))) - print("Success") - assert rc[98:130].hex() == vector[0], "Invalid calculated hash\nExpected: " + vector[0] + "\nReceived: " + rc[98:130].hex() - except Exception as e: - print("Invalid signature ", e) - +from ragger.backend.interface import BackendInterface +from ragger.firmware.structs import Firmware +from ragger.navigator.navigator import Navigator +from ragger_tests.application_client.curve import SECP256K1 +from ragger_tests.test_sign_preauth_hash_ed25519 import sign_preauth_hash + +def sign_preauth_hash_secp256k1( + firmware: Firmware, + backend: BackendInterface, + navigator: Navigator, + test_name: str, + message_to_hash: bytes +): + sign_preauth_hash( + curve=SECP256K1, + path="m/44'/1022'/10'/525'/1238'", + firmware=firmware, + backend=backend, + navigator=navigator, + test_name=test_name, + message_to_hash=message_to_hash + ) def test_sign_preauth_hash_secp256k1_0(firmware, backend, navigator, test_name): sign_preauth_hash_secp256k1(firmware, backend, navigator, test_name, b'0') @@ -68,34 +27,26 @@ def test_sign_preauth_hash_secp256k1_0(firmware, backend, navigator, test_name): def test_sign_preauth_hash_secp256k1_1(firmware, backend, navigator, test_name): sign_preauth_hash_secp256k1(firmware, backend, navigator, test_name, b'1') - def test_sign_preauth_hash_secp256k1_2(firmware, backend, navigator, test_name): sign_preauth_hash_secp256k1(firmware, backend, navigator, test_name, b'2') - def test_sign_preauth_hash_secp256k1_3(firmware, backend, navigator, test_name): sign_preauth_hash_secp256k1(firmware, backend, navigator, test_name, b'3') - def test_sign_preauth_hash_secp256k1_4(firmware, backend, navigator, test_name): sign_preauth_hash_secp256k1(firmware, backend, navigator, test_name, b'4') - def test_sign_preauth_hash_secp256k1_5(firmware, backend, navigator, test_name): sign_preauth_hash_secp256k1(firmware, backend, navigator, test_name, b'5') - def test_sign_preauth_hash_secp256k1_6(firmware, backend, navigator, test_name): sign_preauth_hash_secp256k1(firmware, backend, navigator, test_name, b'6') - def test_sign_preauth_hash_secp256k1_7(firmware, backend, navigator, test_name): sign_preauth_hash_secp256k1(firmware, backend, navigator, test_name, b'7') - def test_sign_preauth_hash_secp256k1_8(firmware, backend, navigator, test_name): sign_preauth_hash_secp256k1(firmware, backend, navigator, test_name, b'8') - def test_sign_preauth_hash_secp256k1_9(firmware, backend, navigator, test_name): sign_preauth_hash_secp256k1(firmware, backend, navigator, test_name, b'9') diff --git a/ragger_tests/test_sign_tx_ed25519.py b/ragger_tests/test_sign_tx_ed25519.py index 986b597a..bdb99ac1 100644 --- a/ragger_tests/test_sign_tx_ed25519.py +++ b/ragger_tests/test_sign_tx_ed25519.py @@ -1,6 +1,6 @@ from enum import Enum from pathlib import Path -from typing import List, Optional +from typing import List from ragger.navigator import NavInsID from ragger.backend.interface import BackendInterface from ragger.backend.speculos import SpeculosBackend @@ -35,6 +35,32 @@ def should_fail(self) -> bool: def should_skip(self) -> bool: return self == BlindSigningSettings.SKIP_IF_ON or self == BlindSigningSettings.SKIP_IF_OFF + def should_abort_execution_due_to_blind_sign(self, backend: BackendInterface) -> bool: + if self.should_check() and not isinstance(backend, SpeculosBackend): + app = App(backend) + app_settings = app.get_app_settings() + is_blind_signing_enabled = app_settings.is_blind_signing_enabled + if is_blind_signing_enabled and self.should_be_off(): + errmsg = "⚙️ ❌ Blind signing is on, but required to be off." + print(errmsg) + if self.should_fail(): + raise ValueError(errmsg) + elif self.should_skip(): + print("🙅‍♀️ Skipping test") + return True + elif not is_blind_signing_enabled and self.should_be_on(): + errmsg = "⚙️ ❌ Blind signing is off, but required to be on." + print(errmsg) + if self.should_fail(): + raise ValueError(errmsg) + elif self.should_skip(): + print("🙅‍♀️ Skipping test") + return True + else: + enabled_or_not = "ENABLED" if is_blind_signing_enabled else "DISABLED" + print(f"✅ Blind signing is: {enabled_or_not} which it was required to be.") + return False + def sign_tx( curve: C, path: str, @@ -58,34 +84,13 @@ def navigate_path(): navigator.navigate([NavInsID.RIGHT_CLICK]) def navigate_sign(): - if firmware.device.startswith("nano"): + if firmware.is_nano: navigator.navigate_and_compare(ROOT_SCREENSHOT_PATH, test_name, clicks) - app = App(backend) - - if blind_signing_settings.should_check() and not isinstance(backend, SpeculosBackend): - app_settings = app.get_app_settings() - is_blind_signing_enabled = app_settings.is_blind_signing_enabled - if is_blind_signing_enabled and blind_signing_settings.should_be_off(): - errmsg = "⚙️ ❌ Blind signing is on, but required to be off." - print(errmsg) - if blind_signing_settings.should_fail(): - raise ValueError(errmsg) - elif blind_signing_settings.should_skip(): - print("🙅‍♀️ Skipping test") - return - elif not is_blind_signing_enabled and blind_signing_settings.should_be_on(): - errmsg = "⚙️ ❌ Blind signing is off, but required to be on." - print(errmsg) - if blind_signing_settings.should_fail(): - raise ValueError(errmsg) - elif blind_signing_settings.should_skip(): - print("🙅‍♀️ Skipping test") - return - else: - enabled_or_not = "ENABLED" if is_blind_signing_enabled else "DISABLED" - print(f"✅ Blind signing is: {enabled_or_not} which it was required to be.") + if blind_signing_settings.should_abort_execution_due_to_blind_sign(backend): + return + app = App(backend) response = app.sign_tx( curve=curve, path=path, diff --git a/ragger_tests/test_verify_address_ed25519.py b/ragger_tests/test_verify_address_ed25519.py index 2fa0a830..f116a26d 100644 --- a/ragger_tests/test_verify_address_ed25519.py +++ b/ragger_tests/test_verify_address_ed25519.py @@ -21,7 +21,7 @@ def verify_address( path, expected_address = vector def navigate(): - if firmware.device.startswith("nano"): + if firmware.is_nano: navigator.navigate_and_compare( path=ROOT_SCREENSHOT_PATH, test_case_name=test_name, diff --git a/test/test-sign-preauth-hash-ed25519.py b/test/test-sign-preauth-hash-ed25519.py deleted file mode 100755 index 4f523b91..00000000 --- a/test/test-sign-preauth-hash-ed25519.py +++ /dev/null @@ -1,76 +0,0 @@ -# Test SignTxEd25519 instruction (mode: do not show digest) - -import sys -import os -import hashlib - -from ledgerblue.comm import getDongle -from ledgerblue.commTCP import getDongle as getDongleTCP -from cryptography.hazmat.primitives.asymmetric import ed25519 - -# disable printing stack trace -sys.tracebacklimit = 0 - -if os.environ.get('USE_SPECULOS') is not None: - dongle = getDongleTCP(debug=False) -else: - dongle = getDongle(False) - -instructionClass = "AA" -instructionCode = "A1" -p1 = "00" -p2 = "00" -dataLength = "00" - -print("Testing", "SignPreAuthHashEd25519", instructionCode) - -test_hash_inputs = [b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9'] - -def encode_bip32(path): - elements = path.replace('H', "'").replace('"', "'").split('/') - result = (len(elements) - 1).to_bytes(1, 'little').hex() - for i in range(1, len(elements)): - num = 0x80000000 if elements[i].endswith("'") else 0 - num += int(elements[i].replace("'", "")) - result += num.to_bytes(4, 'big').hex() - return result - - -def send_preauth_hash(data): - cls = "AC" - data_length = len(data).to_bytes(1, 'little').hex() - # print("Sending data_len = ", data_length, " len = ", len(data)) - - try: - return dongle.exchange(bytes.fromhex(cls + instructionCode + p1 + p2 + data_length + data.hex())) - except Exception as exception: - print("Error sending txn chunk: ", exception) - return None - -def send_derivation_path(bip_path): - path_data = encode_bip32(bip_path) - data_length = int(len(path_data) / 2).to_bytes(1, 'little').hex() - # print("Sending derivation path: ", bip_path, ", data_len = ", data_length) - - try: - return dongle.exchange(bytes.fromhex(instructionClass + instructionCode + p1 + p2 + data_length + path_data)) - except Exception as exception: - print("Error sending derivation path: ", exception) - return None - - -for inp in test_hash_inputs: - hash_calculator = hashlib.blake2b(digest_size=32) - hash_calculator.update(inp) - send_derivation_path("m/44H/1022H/12H/525H/1460H/0H") - rc = send_preauth_hash(hash_calculator.digest()) - - if rc is None: - print("Failed") - else: - pubkey = ed25519.Ed25519PublicKey.from_public_bytes(bytes(rc[64:96])) - try: - pubkey.verify(bytes(rc[0:64]), bytes(rc[96:128])) - print("Success") - except Exception as e: - print("Invalid signature ", e) diff --git a/test/test-sign-preauth-hash-secp256k1.py b/test/test-sign-preauth-hash-secp256k1.py deleted file mode 100755 index 58a257ec..00000000 --- a/test/test-sign-preauth-hash-secp256k1.py +++ /dev/null @@ -1,81 +0,0 @@ -# Test SignTxEd25519 instruction (mode: do not show digest) - -import sys -import os -import hashlib - -from ledgerblue.comm import getDongle -from ledgerblue.commTCP import getDongle as getDongleTCP -from cryptography.hazmat.primitives.asymmetric import ec, utils -from cryptography.hazmat.primitives import hashes - -# disable printing stack trace -sys.tracebacklimit = 0 - -if os.environ.get('USE_SPECULOS') is not None: - dongle = getDongleTCP(debug=False) -else: - dongle = getDongle(False) - -instructionClass = "AA" -instructionCode = "A2" -p1 = "00" -p2 = "00" -dataLength = "00" - -print("Testing", "SignPreAuthHashSecp256k1", instructionCode) - -test_hash_inputs = [b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9'] - -def encode_bip32(path): - elements = path.replace('H', "'").replace('"', "'").split('/') - result = (len(elements) - 1).to_bytes(1, 'little').hex() - for i in range(1, len(elements)): - num = 0x80000000 if elements[i].endswith("'") else 0 - num += int(elements[i].replace("'", "")) - result += num.to_bytes(4, 'big').hex() - return result - - -def send_preauth_hash(data): - cls = "AC" - data_length = len(data).to_bytes(1, 'little').hex() - # print("Sending data_len = ", data_length, " len = ", len(data)) - - try: - return dongle.exchange(bytes.fromhex(cls + instructionCode + p1 + p2 + data_length + data.hex())) - except Exception as exception: - print("Error sending txn chunk: ", exception) - return None - -def send_derivation_path(bip_path): - path_data = encode_bip32(bip_path) - data_length = int(len(path_data) / 2).to_bytes(1, 'little').hex() - # print("Sending derivation path: ", bip_path, ", data_len = ", data_length) - - try: - return dongle.exchange(bytes.fromhex(instructionClass + instructionCode + p1 + p2 + data_length + path_data)) - except Exception as exception: - print("Error sending derivation path: ", exception) - return None - - -for inp in test_hash_inputs: - hash_calculator = hashlib.blake2b(digest_size=32) - hash_calculator.update(inp) - send_derivation_path("m/44H/1022H/10H/525H/1238H") - rc = send_preauth_hash(hash_calculator.digest()) - - if rc is None: - print("Failed") - else: - r = int.from_bytes(rc[1:33], byteorder='big', signed=False) - s = int.from_bytes(rc[33:65], byteorder='big', signed=False) - signature = utils.encode_dss_signature(int(r), int(s)) - pubkey = ec.EllipticCurvePublicKey.from_encoded_point(ec.SECP256K1(), bytes(rc[65:98])) - try: - # Note that Prehashed parameter is irrelevant here, we just need to pass something known to the library - pubkey.verify(signature, bytes(rc[98:130]), ec.ECDSA(utils.Prehashed(hashes.SHA256()))) - print("Success") - except Exception as e: - print("Invalid signature ", e) From 794502eda396c5b85324551f5a3e3c8b8ebc4be9 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Sun, 3 Nov 2024 09:39:56 +0100 Subject: [PATCH 5/7] remove test folder, wip ragger tests for preauth raw --- .gitignore | 1 + ragger_tests/application_client/app.py | 19 +++ {test => ragger_tests}/data/README.md | 0 {test => ragger_tests}/data/access_rule.txn | Bin .../data/address_allocation.txn | Bin {test => ragger_tests}/data/call_method.txn | Bin .../data/checked_childless_subintent.si | Bin .../data/create_access_controller.txn | Bin .../data/create_account.txn | Bin ..._fungible_resource_with_initial_supply.txn | Bin ...ngible_resource_with_no_initial_supply.txn | Bin .../data/create_identity.txn | Bin ..._fungible_resource_with_initial_supply.txn | Bin ...ngible_resource_with_no_initial_supply.txn | Bin .../data/create_validator.txn | Bin {test => ragger_tests}/data/metadata.txn | Bin {test => ragger_tests}/data/mint_fungible.txn | Bin .../data/mint_non_fungible.txn | Bin .../data/publish_package.txn | Bin .../data/resource_auth_zone.txn | Bin .../data/resource_recall.txn | Bin .../data/resource_recall_nonfungibles.txn | Bin .../data/resource_worktop.txn | Bin {test => ragger_tests}/data/royalty.txn | Bin .../data/simple_invalid_transfer.txn | Bin .../data/subintent_vector_0.si | Bin .../data/subintent_vector_1.si | Bin .../data/subintent_vector_2.si | Bin .../data/subintent_vector_3.si | Bin .../data/subintent_vector_4.si | Bin .../data/subintent_vector_5.si | Bin .../data/subintent_vector_6.si | Bin {test => ragger_tests}/data/values.txn | Bin ragger_tests/test_sign_preauth_raw_ed25519.py | 126 ++++++++++++++++++ sbor/src/si_test_data_gen.rs | 3 +- sbor/src/tx_intent_test_data_gen.rs | 2 +- test/data/call_function.txn | Bin 145 -> 0 bytes test/data/simple_transfer.txn | Bin 379 -> 0 bytes test/data/simple_transfer_new_format.txn | Bin 346 -> 0 bytes test/data/simple_transfer_nft.txn | Bin 389 -> 0 bytes test/data/simple_transfer_nft_by_id.txn | Bin 403 -> 0 bytes .../simple_transfer_nft_by_id_new_format.txn | Bin 370 -> 0 bytes test/data/simple_transfer_nft_new_format.txn | Bin 356 -> 0 bytes ...ple_transfer_with_multiple_locked_fees.txn | Bin 450 -> 0 bytes test/pull-image.sh | 2 - test/run-speculos-ns.sh | 7 - test/run-speculos-nsp.sh | 7 - test/test-all.sh | 5 - test/test-sign-preauth-raw-ed25519.py | 97 -------------- test/test-sign-preauth-raw-secp256k1.py | 102 -------------- 50 files changed, 149 insertions(+), 222 deletions(-) rename {test => ragger_tests}/data/README.md (100%) rename {test => ragger_tests}/data/access_rule.txn (100%) rename {test => ragger_tests}/data/address_allocation.txn (100%) rename {test => ragger_tests}/data/call_method.txn (100%) rename {test => ragger_tests}/data/checked_childless_subintent.si (100%) rename {test => ragger_tests}/data/create_access_controller.txn (100%) rename {test => ragger_tests}/data/create_account.txn (100%) rename {test => ragger_tests}/data/create_fungible_resource_with_initial_supply.txn (100%) rename {test => ragger_tests}/data/create_fungible_resource_with_no_initial_supply.txn (100%) rename {test => ragger_tests}/data/create_identity.txn (100%) rename {test => ragger_tests}/data/create_non_fungible_resource_with_initial_supply.txn (100%) rename {test => ragger_tests}/data/create_non_fungible_resource_with_no_initial_supply.txn (100%) rename {test => ragger_tests}/data/create_validator.txn (100%) rename {test => ragger_tests}/data/metadata.txn (100%) rename {test => ragger_tests}/data/mint_fungible.txn (100%) rename {test => ragger_tests}/data/mint_non_fungible.txn (100%) rename {test => ragger_tests}/data/publish_package.txn (100%) rename {test => ragger_tests}/data/resource_auth_zone.txn (100%) rename {test => ragger_tests}/data/resource_recall.txn (100%) rename {test => ragger_tests}/data/resource_recall_nonfungibles.txn (100%) rename {test => ragger_tests}/data/resource_worktop.txn (100%) rename {test => ragger_tests}/data/royalty.txn (100%) rename {test => ragger_tests}/data/simple_invalid_transfer.txn (100%) rename {test => ragger_tests}/data/subintent_vector_0.si (100%) rename {test => ragger_tests}/data/subintent_vector_1.si (100%) rename {test => ragger_tests}/data/subintent_vector_2.si (100%) rename {test => ragger_tests}/data/subintent_vector_3.si (100%) rename {test => ragger_tests}/data/subintent_vector_4.si (100%) rename {test => ragger_tests}/data/subintent_vector_5.si (100%) rename {test => ragger_tests}/data/subintent_vector_6.si (100%) rename {test => ragger_tests}/data/values.txn (100%) create mode 100644 ragger_tests/test_sign_preauth_raw_ed25519.py delete mode 100644 test/data/call_function.txn delete mode 100644 test/data/simple_transfer.txn delete mode 100644 test/data/simple_transfer_new_format.txn delete mode 100644 test/data/simple_transfer_nft.txn delete mode 100644 test/data/simple_transfer_nft_by_id.txn delete mode 100644 test/data/simple_transfer_nft_by_id_new_format.txn delete mode 100644 test/data/simple_transfer_nft_new_format.txn delete mode 100644 test/data/simple_transfer_with_multiple_locked_fees.txn delete mode 100755 test/pull-image.sh delete mode 100755 test/run-speculos-ns.sh delete mode 100755 test/run-speculos-nsp.sh delete mode 100755 test/test-all.sh delete mode 100755 test/test-sign-preauth-raw-ed25519.py delete mode 100755 test/test-sign-preauth-raw-secp256k1.py diff --git a/.gitignore b/.gitignore index a4485669..ee12c15d 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ app.json # Python __pycache__/ snapshots-tmp/ +**/venv/* # Binaries *.elf diff --git a/ragger_tests/application_client/app.py b/ragger_tests/application_client/app.py index a430545c..06621f7e 100644 --- a/ragger_tests/application_client/app.py +++ b/ragger_tests/application_client/app.py @@ -149,6 +149,25 @@ def sign_tx( response = res return curve.unpack_signed(response=response.data) + def sign_preauth_raw( + self, + curve: C, + path: str, + txn: bytes, + navigate_path: Callable[[], None] = lambda: None, + navigate_sign: Callable[[], None] = lambda: None + ) -> Signed: + global response + with self.__sign_generic( + ins=curve.ins_sign_pre_auth_raw(), + navigate_path=navigate_path, + navigate_sign=navigate_sign, + path=path, + payload=txn + ) as res: + response = res + return curve.unpack_signed(response=response.data) + def sign_preauth_hash( self, curve: C, diff --git a/test/data/README.md b/ragger_tests/data/README.md similarity index 100% rename from test/data/README.md rename to ragger_tests/data/README.md diff --git a/test/data/access_rule.txn b/ragger_tests/data/access_rule.txn similarity index 100% rename from test/data/access_rule.txn rename to ragger_tests/data/access_rule.txn diff --git a/test/data/address_allocation.txn b/ragger_tests/data/address_allocation.txn similarity index 100% rename from test/data/address_allocation.txn rename to ragger_tests/data/address_allocation.txn diff --git a/test/data/call_method.txn b/ragger_tests/data/call_method.txn similarity index 100% rename from test/data/call_method.txn rename to ragger_tests/data/call_method.txn diff --git a/test/data/checked_childless_subintent.si b/ragger_tests/data/checked_childless_subintent.si similarity index 100% rename from test/data/checked_childless_subintent.si rename to ragger_tests/data/checked_childless_subintent.si diff --git a/test/data/create_access_controller.txn b/ragger_tests/data/create_access_controller.txn similarity index 100% rename from test/data/create_access_controller.txn rename to ragger_tests/data/create_access_controller.txn diff --git a/test/data/create_account.txn b/ragger_tests/data/create_account.txn similarity index 100% rename from test/data/create_account.txn rename to ragger_tests/data/create_account.txn diff --git a/test/data/create_fungible_resource_with_initial_supply.txn b/ragger_tests/data/create_fungible_resource_with_initial_supply.txn similarity index 100% rename from test/data/create_fungible_resource_with_initial_supply.txn rename to ragger_tests/data/create_fungible_resource_with_initial_supply.txn diff --git a/test/data/create_fungible_resource_with_no_initial_supply.txn b/ragger_tests/data/create_fungible_resource_with_no_initial_supply.txn similarity index 100% rename from test/data/create_fungible_resource_with_no_initial_supply.txn rename to ragger_tests/data/create_fungible_resource_with_no_initial_supply.txn diff --git a/test/data/create_identity.txn b/ragger_tests/data/create_identity.txn similarity index 100% rename from test/data/create_identity.txn rename to ragger_tests/data/create_identity.txn diff --git a/test/data/create_non_fungible_resource_with_initial_supply.txn b/ragger_tests/data/create_non_fungible_resource_with_initial_supply.txn similarity index 100% rename from test/data/create_non_fungible_resource_with_initial_supply.txn rename to ragger_tests/data/create_non_fungible_resource_with_initial_supply.txn diff --git a/test/data/create_non_fungible_resource_with_no_initial_supply.txn b/ragger_tests/data/create_non_fungible_resource_with_no_initial_supply.txn similarity index 100% rename from test/data/create_non_fungible_resource_with_no_initial_supply.txn rename to ragger_tests/data/create_non_fungible_resource_with_no_initial_supply.txn diff --git a/test/data/create_validator.txn b/ragger_tests/data/create_validator.txn similarity index 100% rename from test/data/create_validator.txn rename to ragger_tests/data/create_validator.txn diff --git a/test/data/metadata.txn b/ragger_tests/data/metadata.txn similarity index 100% rename from test/data/metadata.txn rename to ragger_tests/data/metadata.txn diff --git a/test/data/mint_fungible.txn b/ragger_tests/data/mint_fungible.txn similarity index 100% rename from test/data/mint_fungible.txn rename to ragger_tests/data/mint_fungible.txn diff --git a/test/data/mint_non_fungible.txn b/ragger_tests/data/mint_non_fungible.txn similarity index 100% rename from test/data/mint_non_fungible.txn rename to ragger_tests/data/mint_non_fungible.txn diff --git a/test/data/publish_package.txn b/ragger_tests/data/publish_package.txn similarity index 100% rename from test/data/publish_package.txn rename to ragger_tests/data/publish_package.txn diff --git a/test/data/resource_auth_zone.txn b/ragger_tests/data/resource_auth_zone.txn similarity index 100% rename from test/data/resource_auth_zone.txn rename to ragger_tests/data/resource_auth_zone.txn diff --git a/test/data/resource_recall.txn b/ragger_tests/data/resource_recall.txn similarity index 100% rename from test/data/resource_recall.txn rename to ragger_tests/data/resource_recall.txn diff --git a/test/data/resource_recall_nonfungibles.txn b/ragger_tests/data/resource_recall_nonfungibles.txn similarity index 100% rename from test/data/resource_recall_nonfungibles.txn rename to ragger_tests/data/resource_recall_nonfungibles.txn diff --git a/test/data/resource_worktop.txn b/ragger_tests/data/resource_worktop.txn similarity index 100% rename from test/data/resource_worktop.txn rename to ragger_tests/data/resource_worktop.txn diff --git a/test/data/royalty.txn b/ragger_tests/data/royalty.txn similarity index 100% rename from test/data/royalty.txn rename to ragger_tests/data/royalty.txn diff --git a/test/data/simple_invalid_transfer.txn b/ragger_tests/data/simple_invalid_transfer.txn similarity index 100% rename from test/data/simple_invalid_transfer.txn rename to ragger_tests/data/simple_invalid_transfer.txn diff --git a/test/data/subintent_vector_0.si b/ragger_tests/data/subintent_vector_0.si similarity index 100% rename from test/data/subintent_vector_0.si rename to ragger_tests/data/subintent_vector_0.si diff --git a/test/data/subintent_vector_1.si b/ragger_tests/data/subintent_vector_1.si similarity index 100% rename from test/data/subintent_vector_1.si rename to ragger_tests/data/subintent_vector_1.si diff --git a/test/data/subintent_vector_2.si b/ragger_tests/data/subintent_vector_2.si similarity index 100% rename from test/data/subintent_vector_2.si rename to ragger_tests/data/subintent_vector_2.si diff --git a/test/data/subintent_vector_3.si b/ragger_tests/data/subintent_vector_3.si similarity index 100% rename from test/data/subintent_vector_3.si rename to ragger_tests/data/subintent_vector_3.si diff --git a/test/data/subintent_vector_4.si b/ragger_tests/data/subintent_vector_4.si similarity index 100% rename from test/data/subintent_vector_4.si rename to ragger_tests/data/subintent_vector_4.si diff --git a/test/data/subintent_vector_5.si b/ragger_tests/data/subintent_vector_5.si similarity index 100% rename from test/data/subintent_vector_5.si rename to ragger_tests/data/subintent_vector_5.si diff --git a/test/data/subintent_vector_6.si b/ragger_tests/data/subintent_vector_6.si similarity index 100% rename from test/data/subintent_vector_6.si rename to ragger_tests/data/subintent_vector_6.si diff --git a/test/data/values.txn b/ragger_tests/data/values.txn similarity index 100% rename from test/data/values.txn rename to ragger_tests/data/values.txn diff --git a/ragger_tests/test_sign_preauth_raw_ed25519.py b/ragger_tests/test_sign_preauth_raw_ed25519.py new file mode 100644 index 00000000..bca5cc43 --- /dev/null +++ b/ragger_tests/test_sign_preauth_raw_ed25519.py @@ -0,0 +1,126 @@ +import os +from pathlib import Path +from ragger.backend.interface import BackendInterface +from ragger.firmware.structs import Firmware +from ragger.navigator.navigator import Navigator +from ragger.navigator import NavInsID + +from ragger_tests.application_client.app import App +from ragger_tests.application_client.curve import C, Curve25519 +from ragger_tests.test_sign_tx_ed25519 import BlindSigningSettings + +DATA_PATH = str(Path(__file__).parent.joinpath("data").absolute()) + "/" +ROOT_SCREENSHOT_PATH = Path(__file__).parent.resolve() + + +def sign_preauth_raw( + curve: C, + path: str, + firmware: Firmware, + backend: BackendInterface, + navigator: Navigator, + txn: bytes, + test_name: str, + blind_signing_settings: BlindSigningSettings +): + + def navigate_path(): + navigator.navigate_and_compare( + path=ROOT_SCREENSHOT_PATH, + test_case_name=test_name, + instructions=[ + NavInsID.RIGHT_CLICK + ], + screen_change_before_first_instruction=True, + snap_start_idx=0 + ) + + def navigate_sign(): + if firmware.is_nano: + navigator.navigate_and_compare( + path=ROOT_SCREENSHOT_PATH, + test_case_name=test_name, + instructions=[ + NavInsID.RIGHT_CLICK, NavInsID.RIGHT_CLICK, + NavInsID.RIGHT_CLICK, NavInsID.BOTH_CLICK + ], + screen_change_before_first_instruction=True, + snap_start_idx=1 + ) + + # if blind_signing_settings.should_abort_execution_due_to_blind_sign(backend): + # return + + app = App(backend) + response = app.sign_preauth_raw( + curve=curve, + path=path, + txn=txn, + navigate_path=navigate_path, + navigate_sign=navigate_sign, + ) + + assert response.verify_signature() + +def read_file(file: str) -> bytes: + with open(DATA_PATH + file, "rb") as f: + return f.read() + + +def sign_preauth_raw_with_file_name( + curve: C, + path: str, + firmware: Firmware, + backend: BackendInterface, + navigator: Navigator, + file_name: str, + test_name: str, + blind_signing_settings: BlindSigningSettings +): + print(f"🎃 file_name: '{file_name}'") + txn = read_file(file=file_name) + sign_preauth_raw( + curve=curve, + path=path, + firmware=firmware, + backend=backend, + navigator=navigator, + txn=txn, + test_name=test_name, + blind_signing_settings=blind_signing_settings + ) + +def sign_preauth_raw_ed25519( + firmware: Firmware, + backend: BackendInterface, + navigator: Navigator, + file_name: str, + test_name: str, + blind_signing_settings: BlindSigningSettings = BlindSigningSettings.DONT_CHECK_SETTINGS +): + sign_preauth_raw_with_file_name( + curve=Curve25519, + path="m/44'/1022'/12'/525'/1460'/0'", + firmware=firmware, + backend=backend, + navigator=navigator, + file_name=file_name, + test_name=test_name, + blind_signing_settings=blind_signing_settings + ) + +def list_files(): + dir_path = "data" + res = [] + for path in os.listdir(dir_path): + if os.path.isfile(os.path.join(dir_path, path)): + res.append(os.path.join(path)) + return res + +def test_sign_preauth_raw_ed25519_all(firmware, backend, navigator): + for file_name in list_files(): + if not file_name.endswith(".si"): + continue + sign_preauth_raw_ed25519(firmware, backend, navigator, file_name, test_name=file_name) + + diff --git a/sbor/src/si_test_data_gen.rs b/sbor/src/si_test_data_gen.rs index 742ab2d3..d1e17f81 100644 --- a/sbor/src/si_test_data_gen.rs +++ b/sbor/src/si_test_data_gen.rs @@ -46,7 +46,8 @@ pub mod tests { #[test] pub fn generate_binaries() { for blob in BLOBS { - let mut file = std::fs::File::create(format!("../test/data/{}.si", blob.name)).unwrap(); + let mut file = + std::fs::File::create(format!("../ragger_tests/data/{}.si", blob.name)).unwrap(); file.write_all(blob.data).unwrap(); } } diff --git a/sbor/src/tx_intent_test_data_gen.rs b/sbor/src/tx_intent_test_data_gen.rs index b45525d8..c680cf33 100644 --- a/sbor/src/tx_intent_test_data_gen.rs +++ b/sbor/src/tx_intent_test_data_gen.rs @@ -135,7 +135,7 @@ pub mod tests { pub fn generate_binaries() { for blob in BLOBS { let mut file = - std::fs::File::create(format!("../test/data/{}.txn", blob.name)).unwrap(); + std::fs::File::create(format!("../ragger_tests/data/{}.txn", blob.name)).unwrap(); file.write_all(blob.data).unwrap(); } } diff --git a/test/data/call_function.txn b/test/data/call_function.txn deleted file mode 100644 index b8af8ea35461b80c83d3a03a893a3777371bcaf1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 145 zcmeZOVq{TdXaB^-00mqxm?11qRv@j!$f&@s@VPN5&+$1^-4et7j~V7o*?TFfB>UZw zj48c2HZ`#)mwGcYa4<6{C^0&)G%)bG2VI)5Ifd!q2^&q7!Wok}_4U{eoqsnas(J4k nQ(Ycjr<~H%f}+g462HXUR364OMMfUB;u4@hx`G0O5(5JO8q+G+ diff --git a/test/data/simple_transfer.txn b/test/data/simple_transfer.txn deleted file mode 100644 index 4bb3e50c42d0901ef51d24ca4ede81ce6180eddb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 379 zcmeZOVq{TdXaB^-00mqxm?11qRv@j!$f&@s@VPN5&+$1^-4et7j~V7o*?TFfB>UZw zj48c2HZ`#)mwGcYa4<6{D6u#)H!vKOXm-=Ltjt|uuitd){j$ncA3t`g*6p?FQLl}9 zIFpAXCqFqmJ}os>k+GG5A>jR^Yfq%4VRl1lVvH%zEXhbIN-S4oYG8=H@lJO8^|wLd z#c}0no+0J`n7=MqwJ@UZw zj48c2HZ`#)mwGcYa4<6{C^0)SH!vKOXm-=Ltjt|uuitd){j$ncA3t`g*6p?FQLl}9 zIFm;-CqFqmJ}oshJ~1yPzC5!eBc&*@T#>nzf#FC*_w|oaj4+2l=>~?_Wyuo940{s4 zE|c~Fv%XJR)vFmV^31RY$OUTT$qhX5eH9y;Mg}IbwSxV@GBZPKX^L;%UZw zj48c2HZ`#)mwGcYa4<6{D6u#)H!vKOXm-=Ltjt|uuitd){j$ncA3t`g*6p?FQLl}9 zIFpAXCqFqmJ}os>k+GG5A>jR^Yfq%4VRl1lB8(9$&n(GEDM~Dl&&$t?Pbg{Cj8yOz4KLBPET@a36DrgQDuBeYC(Q+ aW=VX0QG8-jeo=`cVUZw zj48c2HZ`#)mwGcYa4<6{D6u#)H!vKOXm-=Ltjt|uuitd){j$ncA3t`g*6p?FQLl}9 zIFpAXCqFqmJ}os>k+GG5A>jR^Yfq%4VRl1lB8(9$&n(GEDM~Dl&&$t?Pbg{Cj8yOz4KLBPET@a36DrgQDuBeYC(Q+W=VX0QG8-j Seo=`cV3JMHL3=9Bx(0ASd diff --git a/test/data/simple_transfer_nft_by_id_new_format.txn b/test/data/simple_transfer_nft_by_id_new_format.txn deleted file mode 100644 index 5afedb0020247c6b57c9bcd4d63f4717c58d7b5a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 370 zcmeZOVq{TdXaB^-00mqxm?11qRv@j!$f&@s@VPN5&+$1^-4et7j~V7o*?TFfB>UZw zj48c2HZ`#)mwGcYa4<6{C^0)SH!vKOXm-=Ltjt|uuitd){j$ncA3t`g*6p?FQLl}9 zIFm;?CqFqmJ}oshJ~1yPzC5!eBc&*@JU%Z!FFvg_FFi9UC$(6Sxs`z-;Qga(Po$(_ z&Vten46}5$30uyV;8~YYs5mX*il^k?7|Fg)!CP;=)7No!E3`8+LX|SY7)&q*Gb0ma z`oaEVnVF%rG{v{>aY!vcJEH}IZo=Ok+&f>D<@6+%mhgy_6jjEjq!#2CXO_h07sV$g TUZw zj48c2HZ`#)mwGcYa4<6{C^0)SH!vKOXm-=Ltjt|uuitd){j$ncA3t`g*6p?FQLl}9 zIFm;?CqFqmJ}oshJ~1yPzC5!eBc&*@JU%Z!FFvg_FFi9UC$(6Sxs`z-;Qga(Po$(_ z&Vten46}5$30uyV;8~YYs5mX*il^k?7|Fg)!CP;=)7No!E3`8)LX|SY7)%UIgtP(e zJK>j}dl20^2C$DsRjAP bnI-Z0Me&JA`9&p)jEx{?C@3%}F)#oCCE#pd diff --git a/test/data/simple_transfer_with_multiple_locked_fees.txn b/test/data/simple_transfer_with_multiple_locked_fees.txn deleted file mode 100644 index 23115ca7d3a07028e385d0b99d21aadfb1fef76b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 450 zcmeZOVq{TdXaB^-00mqxm?11qRv@j!$f&@s@VPN5&+$1^-4et7j~V7o*?TFfB>UZw zj48c2HZ`#)mwGcYa4<6{D6u*+H!vKOXm-=Ltjt|uuitd){j$ncA3t`g*6p?FQLl}9 zIFpAXCqFqmJ}os>k+GG5p(1IC<8lF*)i9bEW6Cp2GE#~X%N3a#7-E+tOB^%oN&LD@ z+6T<~K4n#}X1vHV!yX_PXbn$p;EC_6*kI Date: Mon, 4 Nov 2024 14:42:47 +0100 Subject: [PATCH 6/7] ragger test for subintent (raw ) ed25519 --- .../checked_childless_subintent.si/00000.png | Bin 0 -> 660 bytes .../checked_childless_subintent.si/00001.png | Bin 0 -> 702 bytes .../checked_childless_subintent.si/00002.png | Bin 0 -> 410 bytes .../checked_childless_subintent.si/00003.png | Bin 0 -> 301 bytes .../checked_childless_subintent.si/00004.png | Bin 0 -> 301 bytes .../checked_childless_subintent.si/00005.png | Bin 0 -> 301 bytes .../nanosp/subintent_vector_0.si/00000.png | Bin 0 -> 684 bytes .../nanosp/subintent_vector_0.si/00001.png | Bin 0 -> 694 bytes .../nanosp/subintent_vector_0.si/00002.png | Bin 0 -> 410 bytes .../nanosp/subintent_vector_0.si/00003.png | Bin 0 -> 301 bytes .../nanosp/subintent_vector_0.si/00004.png | Bin 0 -> 301 bytes .../nanosp/subintent_vector_0.si/00005.png | Bin 0 -> 301 bytes .../nanosp/subintent_vector_1.si/00000.png | Bin 0 -> 635 bytes .../nanosp/subintent_vector_1.si/00001.png | Bin 0 -> 657 bytes .../nanosp/subintent_vector_1.si/00002.png | Bin 0 -> 410 bytes .../nanosp/subintent_vector_1.si/00003.png | Bin 0 -> 301 bytes .../nanosp/subintent_vector_1.si/00004.png | Bin 0 -> 301 bytes .../nanosp/subintent_vector_1.si/00005.png | Bin 0 -> 301 bytes .../nanosp/subintent_vector_2.si/00000.png | Bin 0 -> 675 bytes .../nanosp/subintent_vector_2.si/00001.png | Bin 0 -> 705 bytes .../nanosp/subintent_vector_2.si/00002.png | Bin 0 -> 410 bytes .../nanosp/subintent_vector_2.si/00003.png | Bin 0 -> 301 bytes .../nanosp/subintent_vector_2.si/00004.png | Bin 0 -> 301 bytes .../nanosp/subintent_vector_2.si/00005.png | Bin 0 -> 301 bytes .../nanosp/subintent_vector_3.si/00000.png | Bin 0 -> 700 bytes .../nanosp/subintent_vector_3.si/00001.png | Bin 0 -> 677 bytes .../nanosp/subintent_vector_3.si/00002.png | Bin 0 -> 410 bytes .../nanosp/subintent_vector_3.si/00003.png | Bin 0 -> 301 bytes .../nanosp/subintent_vector_3.si/00004.png | Bin 0 -> 301 bytes .../nanosp/subintent_vector_3.si/00005.png | Bin 0 -> 301 bytes .../nanosp/subintent_vector_4.si/00000.png | Bin 0 -> 677 bytes .../nanosp/subintent_vector_4.si/00001.png | Bin 0 -> 675 bytes .../nanosp/subintent_vector_4.si/00002.png | Bin 0 -> 410 bytes .../nanosp/subintent_vector_4.si/00003.png | Bin 0 -> 301 bytes .../nanosp/subintent_vector_4.si/00004.png | Bin 0 -> 301 bytes .../nanosp/subintent_vector_4.si/00005.png | Bin 0 -> 301 bytes .../nanosp/subintent_vector_5.si/00000.png | Bin 0 -> 681 bytes .../nanosp/subintent_vector_5.si/00001.png | Bin 0 -> 694 bytes .../nanosp/subintent_vector_5.si/00002.png | Bin 0 -> 410 bytes .../nanosp/subintent_vector_5.si/00003.png | Bin 0 -> 301 bytes .../nanosp/subintent_vector_5.si/00004.png | Bin 0 -> 301 bytes .../nanosp/subintent_vector_5.si/00005.png | Bin 0 -> 301 bytes .../nanosp/subintent_vector_6.si/00000.png | Bin 0 -> 657 bytes .../nanosp/subintent_vector_6.si/00001.png | Bin 0 -> 711 bytes .../nanosp/subintent_vector_6.si/00002.png | Bin 0 -> 410 bytes .../nanosp/subintent_vector_6.si/00003.png | Bin 0 -> 301 bytes .../nanosp/subintent_vector_6.si/00004.png | Bin 0 -> 301 bytes .../nanosp/subintent_vector_6.si/00005.png | Bin 0 -> 301 bytes ragger_tests/test_sign_preauth_raw_ed25519.py | 17 ++++++++--------- 49 files changed, 8 insertions(+), 9 deletions(-) create mode 100644 ragger_tests/snapshots/nanosp/checked_childless_subintent.si/00000.png create mode 100644 ragger_tests/snapshots/nanosp/checked_childless_subintent.si/00001.png create mode 100644 ragger_tests/snapshots/nanosp/checked_childless_subintent.si/00002.png create mode 100644 ragger_tests/snapshots/nanosp/checked_childless_subintent.si/00003.png create mode 100644 ragger_tests/snapshots/nanosp/checked_childless_subintent.si/00004.png create mode 100644 ragger_tests/snapshots/nanosp/checked_childless_subintent.si/00005.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_0.si/00000.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_0.si/00001.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_0.si/00002.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_0.si/00003.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_0.si/00004.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_0.si/00005.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_1.si/00000.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_1.si/00001.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_1.si/00002.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_1.si/00003.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_1.si/00004.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_1.si/00005.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_2.si/00000.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_2.si/00001.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_2.si/00002.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_2.si/00003.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_2.si/00004.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_2.si/00005.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_3.si/00000.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_3.si/00001.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_3.si/00002.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_3.si/00003.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_3.si/00004.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_3.si/00005.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_4.si/00000.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_4.si/00001.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_4.si/00002.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_4.si/00003.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_4.si/00004.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_4.si/00005.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_5.si/00000.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_5.si/00001.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_5.si/00002.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_5.si/00003.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_5.si/00004.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_5.si/00005.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_6.si/00000.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_6.si/00001.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_6.si/00002.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_6.si/00003.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_6.si/00004.png create mode 100644 ragger_tests/snapshots/nanosp/subintent_vector_6.si/00005.png diff --git a/ragger_tests/snapshots/nanosp/checked_childless_subintent.si/00000.png b/ragger_tests/snapshots/nanosp/checked_childless_subintent.si/00000.png new file mode 100644 index 0000000000000000000000000000000000000000..4b6ddd3a72b735c33395909b53333c8ad1aff48d GIT binary patch literal 660 zcmV;F0&D$=P)Q@-F?c(;XroBBGm2-{)82BBG!!B&7fV000000000000000+xbe- z(psZmYpp42r$*NXrB7|ifbw;|6LlS1_tskH^3UaGpX%0Ht(6Dh1^1Tt^zPn$C@ePb zc4r(I!*M;G^2BYCxdS+KKDW`_85i{r@hKIf^1NNMgrAezaGmC%@)a_7fVgVi47p2g zv8|^HUua{vVSp$4BhvvMv&4N}q^oi(#=%Hq<-o1B@eZJf0j@dYSv*1TF?Rsvo|yO! zL!a`#T`=0hv&M7--HnG|rU3u|{5z&=LBk7*CzE!LN-tQyByY6S?4dxXoKMp^@x52~ zHHP!ugNunR+H22jPC z@M`j6O7u5XbJ;!6J0VTa182$1P2T+ zG){M7b`gE@cLmLi-Q#BNfB@QAbaM3#Gq#d^2hi)w{!!2nFod-#p7DxBe=wkp;g|ENK6{FO2|}=MZDI!E@Cp}>}3mx#`|ab0K+_v^$E*Z6!msvpYcR?$_YwXeLZ6cf<0e z(Z$npl9l5q=z!r0me{bo_zF9BfIDichjvO@ zv7>JlJJ5+-cY$x5kDU{MG|S!B#dDQMNnVU{tvuk>4*CFzyTGH#_*;-g@HRd`(mLjz zVPvkYs}^Hkc(gG}s3kspm?ne}^6!|r3v$E4;>DABglT;pa;u|^belIM6dgUgN78M6 zyL+za#xHJ4lm$jcV1Y%U{_XaF?j(t0m!U-;EV(5G%ErPkMqh+BM=@ zJAI!K?UIrXW3&%0n?2X26Ynnka%p9_m6MR8>88(#%Ifm@#|KQ$!IPfj1lAMe=@Uew zUz8I@=0JU8X+~wEt@G@<(?lpSTfEnlZQgcCH4#iQlVap?N2`HN6+K9xx7nxX6!xJ#{2b*asaJ!O}+t<807*qoM6N<$f*k%%-~a#s literal 0 HcmV?d00001 diff --git a/ragger_tests/snapshots/nanosp/checked_childless_subintent.si/00002.png b/ragger_tests/snapshots/nanosp/checked_childless_subintent.si/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..8c60fc0d6766e73e9ecd04c2f15255ada8973e12 GIT binary patch literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|>x2ba4!+nDh2VbkSi29*4kN zC;e{xpLE09eny2!Lo2(+rZ?65gr+AkIoUV?HNn7vpkG$|Zf$!WJMZpY`PXbc9-2ob zoK4LP-F~V}Kd3XarceDY|G|@23JsW3HrVU0s9!5xa5eJ!^PdmP6Q-SISk3YE_Sc!s zA|*>crNzIlukBWOQ?v5Z*Lknp^rD%Qde-+H%xy2S+ebzEoEqVPjvHn-yyz6G~lU|$YL^IFY zo*e#f>CH8vyG-*loIjVge`I{|?dt)5tw({J=c|}bUVnOir_Wx`?68c~kD*8QSgFi5 zJ)Ptg>MAhj=978JRyuI!{>5zf|Lx9>- z-yQ!a7Kw|8eKlCn>33kls_$VEQ%&yHOqv2z4F$V?T3R>MzTV|#{ru058O3(`izc4B zDEIzVMSiT+On2*P(O*l~&otG`e#({c{!@g~#(+yH*{X-SwY|5vrTn*k+Oykb@#BbH z+Uo9IOn;Ogh0k`r|7NAq5 z_dHp2)BmdC9XY|fh||YsDV@F978JRyuBVM)TAK55Ky#> z^Sk||@|TA@%MvbmdCaYR-+iXBH8n68qz3|CJS!_b2UUcWtdgT|lWwyNj z^n9|qThYa<{)UX1@qBD=|N2+Wn_tyy?N`00{0HBPdzZhL|G2s%R55Oah}h+Cy3x0$ p3)q!6>OT~{x-pp@=6Z$$7v(N!El*ai?Fj>kdAj978JRyuBVM)TAK55Ky#> z^Sk||@|TA@%MvbmdCaYR-+iXBH8n68qz3|CJS!_b2UUcWtdgT|lWwyNj z^n9|qThYa<{)UX1@qBD=|N2+Wn_tyy?N`00{0HBPdzZhL|G2s%R55Oah}h+Cy3x0$ p3)q!6>OT~{x-pp@=6Z$$7v(N!El*ai?Fj>kdAj>kpzgsq!a)E00000000000002sI3Fom zT5I&Q)|#R=G`lt_y=zM@DBtHZQP;Ki-dY=6{=R&jQ{7stwekRb;NBAN_Tj@pVX@h- zJLABZj_VneC#FSa25{)}bfdX59_kO`T`ET9`E|__{+-mO8#GUq?~s`R;#(V@kh`Rd z9sR2C7i~;84e&&NWIDiOmYCN?S}KoXd>Co0Tv*i(t^pJ=z%^(5EuJ8Fn>B#aA57fC z(7Sx>5RA5P)RjB`HY(%UkNV;AD|G-5B2QS^GSA)V{%<;w*tGGqOSxLw%Q6fIrPQNvD`||# zH9|6U&`*b=AtYc}HMLe7Zrkk71}eFg0ruR3tDU1dVuvy$U_fmP4Ns}qX&$UNfM<}{ zTecgDJFjmO&9)(~=_zf&<{EIM7xc<|1CMQdB{*k5*a381dKB?1g)L27Cc2z*C8CD4 z?SL~2Jx^-$%ApAW0N^(3y`Y^2%P_qBUIzIu5#s5fZMy1eO()U*?OJFGW8QPosx^^9ELOri<#|#^%NhFOJWVUHozO2v z19s}{qPun{InpX&yCd%$UK}5jDQmAow7)4o1MEyRaOt;{ef;5;_091&Ep|L{@3)lq z;#$ujCj+K^8|~EJQL(4!XgwlPCba3~qe{|O2mk;8000000000000000oa7ta5w`S+ S)w?kO0000n1T literal 0 HcmV?d00001 diff --git a/ragger_tests/snapshots/nanosp/subintent_vector_0.si/00001.png b/ragger_tests/snapshots/nanosp/subintent_vector_0.si/00001.png new file mode 100644 index 0000000000000000000000000000000000000000..5cf0c20fa88bd2f23e90c2c10fbabb77cfbd8e0a GIT binary patch literal 694 zcmV;n0!jUeP)vDkiRbHKIvL(DFp-Y0!M3D+rBOy4v5WG z9*hIS9O?-1h0|i?3_$w#X-?x{T$FEywSBX`J(g>p;5w<96Uwgm9aheOVXuid#35wzecwIb3r=2_dn`YP0v~S>=EGlaU)0LZc z@LYk-+x2ba4!+nDh2VbkSi29*4kN zC;e{xpLE09eny2!Lo2(+rZ?65gr+AkIoUV?HNn7vpkG$|Zf$!WJMZpY`PXbc9-2ob zoK4LP-F~V}Kd3XarceDY|G|@23JsW3HrVU0s9!5xa5eJ!^PdmP6Q-SISk3YE_Sc!s zA|*>crNzIlukBWOQ?v5Z*Lknp^rD%Qde-+H%xy2S+ebzEoEqVPjvHn-yyz6G~lU|$YL^IFY zo*e#f>CH8vyG-*loIjVge`I{|?dt)5tw({J=c|}bUVnOir_Wx`?68c~kD*8QSgFi5 zJ)Ptg>MAhj=978JRyuI!{>5zf|Lx9>- z-yQ!a7Kw|8eKlCn>33kls_$VEQ%&yHOqv2z4F$V?T3R>MzTV|#{ru058O3(`izc4B zDEIzVMSiT+On2*P(O*l~&otG`e#({c{!@g~#(+yH*{X-SwY|5vrTn*k+Oykb@#BbH z+Uo9IOn;Ogh0k`r|7NAq5 z_dHp2)BmdC9XY|fh||YsDV@F978JRyuBVM)TAK55Ky#> z^Sk||@|TA@%MvbmdCaYR-+iXBH8n68qz3|CJS!_b2UUcWtdgT|lWwyNj z^n9|qThYa<{)UX1@qBD=|N2+Wn_tyy?N`00{0HBPdzZhL|G2s%R55Oah}h+Cy3x0$ p3)q!6>OT~{x-pp@=6Z$$7v(N!El*ai?Fj>kdAj978JRyuBVM)TAK55Ky#> z^Sk||@|TA@%MvbmdCaYR-+iXBH8n68qz3|CJS!_b2UUcWtdgT|lWwyNj z^n9|qThYa<{)UX1@qBD=|N2+Wn_tyy?N`00{0HBPdzZhL|G2s%R55Oah}h+Cy3x0$ p3)q!6>OT~{x-pp@=6Z$$7v(N!El*ai?Fj>kdAj0)+jEP)jeMWxv<}9L@`w3Uib;80KJrA*lOuDT=Be^KWR3u{)w(y-A!%Yq zXBFMhj=50EyYbN5Ob6kxuY2Yk;J^nE4GO zpX$0@XY_?vo#_m1K8S`R8fBeD>`O-z) zF>NcNM9U=X14j9ik(i%&+3QDHpN!fiU7~Hpt-tm|Qa&L7!M2lP-J6)3&$n#NoYY>H zKV1wq1CY{E@rrvUbggMgq{@9{40ZyblO)?YoJhsJNJsjzGa33NH<1mvoQlw`4@)Gh z0n_=h`wug!|nfl{S`u}i_&O&rw008)B=UkA#c0~?AbhXSA5{{n83O|yGtlN%Q z@Vy=)l1s^Yw3Nw-B#V~(xhd@M&_Cg8@bA0pmFLnA!DC1pGS^^AI7^p(tpd|Cv(ORt z^+c>|XCnTXZ?WX+Dw`vqP0(_4(w$i;^#V~m8*Xn-x3yH6~L$A1JYDFFZg0000000000Z0BQ%N-3rN znR8wud#m(Op>3*d84!QY=R&Px{a#AxhWzjH+9zE~$vMjjcp*niO|99xKoWDa#yjVM zQJmE2#1}4$l`BBfwx?EF4#q|PMon$aT6-Bks)_tgj*9D)r{YgoxdPN)>sly>q?&F0 zs>qHuiknXGh2ybu06f)F*LBid?$4c(si_phcbFqrrGDo=Na$d_YTWn7W5) zQ)S+6GTO#>E7KKJBM)z;0RR9(*n4WjTef??n3j^RD!1XEtBBqAkxQ8N{#x->jCw!Gza;HS7*2Tf$B62aWyZT^o)}xIM%w?zI3ta% zhtc~00PwGQ^e$-RQdA%1aXubb*~NI=@>C?5uG`M_PN(WWJcsWdt{D4NZ0B_bM!90= zUO$olgkx7g(mlM%lt)vo=jdW5tTU?EltZr~ zR7ST(YSOXN4}aEo4S=&5OCACR?k7%;w9UC^DWGzD1&p3p1)Oja6L6D5%fH_Us|9(A rmOA_D3;+NC00000000000E_qnUCIjQ=66FY00000NkvXXu0mjf;ixX3 literal 0 HcmV?d00001 diff --git a/ragger_tests/snapshots/nanosp/subintent_vector_1.si/00002.png b/ragger_tests/snapshots/nanosp/subintent_vector_1.si/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..8c60fc0d6766e73e9ecd04c2f15255ada8973e12 GIT binary patch literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|>x2ba4!+nDh2VbkSi29*4kN zC;e{xpLE09eny2!Lo2(+rZ?65gr+AkIoUV?HNn7vpkG$|Zf$!WJMZpY`PXbc9-2ob zoK4LP-F~V}Kd3XarceDY|G|@23JsW3HrVU0s9!5xa5eJ!^PdmP6Q-SISk3YE_Sc!s zA|*>crNzIlukBWOQ?v5Z*Lknp^rD%Qde-+H%xy2S+ebzEoEqVPjvHn-yyz6G~lU|$YL^IFY zo*e#f>CH8vyG-*loIjVge`I{|?dt)5tw({J=c|}bUVnOir_Wx`?68c~kD*8QSgFi5 zJ)Ptg>MAhj=978JRyuI!{>5zf|Lx9>- z-yQ!a7Kw|8eKlCn>33kls_$VEQ%&yHOqv2z4F$V?T3R>MzTV|#{ru058O3(`izc4B zDEIzVMSiT+On2*P(O*l~&otG`e#({c{!@g~#(+yH*{X-SwY|5vrTn*k+Oykb@#BbH z+Uo9IOn;Ogh0k`r|7NAq5 z_dHp2)BmdC9XY|fh||YsDV@F978JRyuBVM)TAK55Ky#> z^Sk||@|TA@%MvbmdCaYR-+iXBH8n68qz3|CJS!_b2UUcWtdgT|lWwyNj z^n9|qThYa<{)UX1@qBD=|N2+Wn_tyy?N`00{0HBPdzZhL|G2s%R55Oah}h+Cy3x0$ p3)q!6>OT~{x-pp@=6Z$$7v(N!El*ai?Fj>kdAj978JRyuBVM)TAK55Ky#> z^Sk||@|TA@%MvbmdCaYR-+iXBH8n68qz3|CJS!_b2UUcWtdgT|lWwyNj z^n9|qThYa<{)UX1@qBD=|N2+Wn_tyy?N`00{0HBPdzZhL|G2s%R55Oah}h+Cy3x0$ p3)q!6>OT~{x-pp@=6Z$$7v(N!El*ai?Fj>kdAj4Xgz!SY%W^WtbeK?eF zw)UVOfa$cJPI=^Ny%;Cu53^T_NqOBqvP8$p(R7_=SNRn(cYyiUx+m02 z(!_3!D*8o(>0Ad-T#w8Jh?r&W>%&Tww*msA^py=+?ZzA+F&%ug7|#-Mg4g5#NqaE! z3?qB>*v=WT@T@W2M6Gf2VVV#^$iHKy78J=*S|(DlkplQ)jOTTycCXFm8Pqg#cdje$vdDkMa~r5__Eff2ZFdX(qqa@m4>kL1>_p!lp^9KOq@CJgSkax zQ%}$X;zjK1Syw9ASBk)79Vc!*mZZP^Bjp$xIWL0K;h{=(-F}KIF^yR}n1Y9L#Q+Z+ zTL~eAd@Sop_ylCgQCc&z5V#6myra5FHIkg&>eDl+HBz?$qj2L|%cyHKR^P>@ZfDCy zp~j})ghwpf4OF#5E%r^_-nE!YvPw^l^#vGR_uZgm7EiRp&erv0sL?eq(;kdgvRrxs zr0oDVg3Fc#_c32+{C^cwB`IvimEoGj1=xJ!kF@MbgXyz;N-YQwdHe4rts2_Jcio~x za0&k%(M>Q?)##WDdV}a$BENhPLI@#*5JCtcgb+dqA%u`e`2~*Xnw<^sLNEXT002ov JPDHLkV1j^RJ0Ji6 literal 0 HcmV?d00001 diff --git a/ragger_tests/snapshots/nanosp/subintent_vector_2.si/00001.png b/ragger_tests/snapshots/nanosp/subintent_vector_2.si/00001.png new file mode 100644 index 0000000000000000000000000000000000000000..5e0774104e6d8855cfa43331fb154645fdbe1d51 GIT binary patch literal 705 zcmV;y0zUnTP) zOYfcjY^^O(o3cJLO7Gf|1?BhoT&U}@aqqpeDSuzieX4u!tu+~d7jm}LyQ4e1I7DoY z_FxF?M3EE!P3r_hh1xDV6gtH*<#>9jn4h9lmj0C==28RSLtzrSNK} z*HaZiOS6b@BUHVPzc+4GBPgaG0wRyl3Bji@+&D+mow(aXp%@Skqj*A+VyVi~=%T&{ zr@ZY_7?eV?Dwkqv1*J=##xh;OTu*|Fo4o$X0jW9I&GVkHY?K;(7egW6hSv6ov_)!Q zEoz>WN~Da;?dl%aZJB9P9nN|p6)vAn0001ggj_ACP**W}WepWdG;X#G9+KT&vsAyB zY#k;j5b07V(UecBgUDkgU5wFH9v1OEK)PhgsUkd4)THcUq3(vLX zvy@;pCL(sTL{pw0<`U+cI`9CYDXJ?us92k&aO};<@C>ybS;V n00000000000000005W_5PU{9x2ba4!+nDh2VbkSi29*4kN zC;e{xpLE09eny2!Lo2(+rZ?65gr+AkIoUV?HNn7vpkG$|Zf$!WJMZpY`PXbc9-2ob zoK4LP-F~V}Kd3XarceDY|G|@23JsW3HrVU0s9!5xa5eJ!^PdmP6Q-SISk3YE_Sc!s zA|*>crNzIlukBWOQ?v5Z*Lknp^rD%Qde-+H%xy2S+ebzEoEqVPjvHn-yyz6G~lU|$YL^IFY zo*e#f>CH8vyG-*loIjVge`I{|?dt)5tw({J=c|}bUVnOir_Wx`?68c~kD*8QSgFi5 zJ)Ptg>MAhj=978JRyuI!{>5zf|Lx9>- z-yQ!a7Kw|8eKlCn>33kls_$VEQ%&yHOqv2z4F$V?T3R>MzTV|#{ru058O3(`izc4B zDEIzVMSiT+On2*P(O*l~&otG`e#({c{!@g~#(+yH*{X-SwY|5vrTn*k+Oykb@#BbH z+Uo9IOn;Ogh0k`r|7NAq5 z_dHp2)BmdC9XY|fh||YsDV@F978JRyuBVM)TAK55Ky#> z^Sk||@|TA@%MvbmdCaYR-+iXBH8n68qz3|CJS!_b2UUcWtdgT|lWwyNj z^n9|qThYa<{)UX1@qBD=|N2+Wn_tyy?N`00{0HBPdzZhL|G2s%R55Oah}h+Cy3x0$ p3)q!6>OT~{x-pp@=6Z$$7v(N!El*ai?Fj>kdAj978JRyuBVM)TAK55Ky#> z^Sk||@|TA@%MvbmdCaYR-+iXBH8n68qz3|CJS!_b2UUcWtdgT|lWwyNj z^n9|qThYa<{)UX1@qBD=|N2+Wn_tyy?N`00{0HBPdzZhL|G2s%R55Oah}h+Cy3x0$ p3)q!6>OT~{x-pp@=6Z$$7v(N!El*ai?Fj>kdAj_YP)e>rB^LkK?ct@WhUm`}V4?eiSn| zuX1o6B+Xen%J{@(F>?i2WPdKF?O>c7FWk3MCiS*F^2F|wQFF?2)A$``t^oI|sUF%P zxnxUs75kx+=9&|H;&{v)0K_eKT^H}DTuQPr%DJ-PR9kokNZbh?4aTz|li*{{0Lj%c z_YNc9+PZ2o=HgywR6|$c=F2o8gphy7NG`~Ri(nLL54WVEJb10ly+`H&ZPa=sz>nfR z7uL7-?!0n?GDP2OrCB8j#PY}7&n~elS(^(we+*)lkd%?HL}F!4m&VTj zoGH7|BXfliLdc)SMloO@MduvSt^W zG2lR>C+IcQ2EA7~m<+*{NV}0NkAdCm>6l|t6TP4V;C2kZ)q+YlGO+DN#@)Eo77lko zy40I=i5OV{)5WqUs(TannJ&kavg=!SMOz(_FqjN+r=F_>eMLqB>~NBl&bxrq8|#ke zg79QWqfJcfZk;e{+TDmZraE02KJnY?6?)1z9mO`p0;w8gb+dqA%qY@ i2qA9ytz@7FprHlUbrC`cA%qY@2qA&pC@Je_tN^ROg&iN;&{<^lX{6MO-{wDmF_w z7zcqltw$(dxGYw#08QPW;Db-Cw>-X}eCLUU933@cZFIclPZdP+*O zt-FdI=)hbvz!%QP$_YT+GS~HGrOK@Y8)F?S2eR7698hBhxHlQE1qp(;$pJN`W9BEF zCU#(W$n>n6cH`6ELo{t3=+e zK+xr`cL8$?)hosueL@H!AIK+A};Bz`&e0G;CTduP!GiJ?v&?1$157nBv3XkND|z&9pwh6qbDz z(HsJ^fxZ#aKLw^puWyLMrpSQxm^Bj+WYIWDY6kkIMRWx2ba4!+nDh2VbkSi29*4kN zC;e{xpLE09eny2!Lo2(+rZ?65gr+AkIoUV?HNn7vpkG$|Zf$!WJMZpY`PXbc9-2ob zoK4LP-F~V}Kd3XarceDY|G|@23JsW3HrVU0s9!5xa5eJ!^PdmP6Q-SISk3YE_Sc!s zA|*>crNzIlukBWOQ?v5Z*Lknp^rD%Qde-+H%xy2S+ebzEoEqVPjvHn-yyz6G~lU|$YL^IFY zo*e#f>CH8vyG-*loIjVge`I{|?dt)5tw({J=c|}bUVnOir_Wx`?68c~kD*8QSgFi5 zJ)Ptg>MAhj=978JRyuI!{>5zf|Lx9>- z-yQ!a7Kw|8eKlCn>33kls_$VEQ%&yHOqv2z4F$V?T3R>MzTV|#{ru058O3(`izc4B zDEIzVMSiT+On2*P(O*l~&otG`e#({c{!@g~#(+yH*{X-SwY|5vrTn*k+Oykb@#BbH z+Uo9IOn;Ogh0k`r|7NAq5 z_dHp2)BmdC9XY|fh||YsDV@F978JRyuBVM)TAK55Ky#> z^Sk||@|TA@%MvbmdCaYR-+iXBH8n68qz3|CJS!_b2UUcWtdgT|lWwyNj z^n9|qThYa<{)UX1@qBD=|N2+Wn_tyy?N`00{0HBPdzZhL|G2s%R55Oah}h+Cy3x0$ p3)q!6>OT~{x-pp@=6Z$$7v(N!El*ai?Fj>kdAj978JRyuBVM)TAK55Ky#> z^Sk||@|TA@%MvbmdCaYR-+iXBH8n68qz3|CJS!_b2UUcWtdgT|lWwyNj z^n9|qThYa<{)UX1@qBD=|N2+Wn_tyy?N`00{0HBPdzZhL|G2s%R55Oah}h+Cy3x0$ p3)q!6>OT~{x-pp@=6Z$$7v(N!El*ai?Fj>kdAjVK#9i{?38m1I5UVqleqR|3Eg8fn=eh_Xgb+dqA%qY@2qA=!?R+dz z$vMlPDWxT{UDhq5@~K~QKzyFhg<8k@J?C5v`TO#EmmgdzT7sO^M z55|FG4s|N=h0|i?3_#@j6sK`8Zqg6xQ&~;c$8yUPTqiAam2y*jhLtlw?X~I+aY#zF zt*Z)lv}3Lr;0woN2X?iMdw@g@aBDE0B@zU0vj<4Zi>YTA z`4rn$!RS}+b*3^@3J)Kq2_c01JH~TCxKpXu#b62Q;AL3#VMa$ijQY1?U*sHkvBCpM zq{I=FKdpw23022;xLF}CVuAG!-2;Ni?!IHn3jAK3-U?&hHo5OZvhtQxgp(k%N=kP( z@1L@QH4;soh#}S60TI8|`cy>MFvwQ(%cw6BSuUi4>g|#nyBveRwFJ%34Ea)ROd%)| zK^i)jirC8h1Ui#?Lqmfzp!(9HmW*u)anlvPRK$>VjGplpO)$ko2qEM}cP>cXn&MZd zqTnC@2lm5wI!J9exzj$75enu*s;^E~il$j;J?hHXSe1L-Xj+W3<|1ZAO&0$ebp3wY zg=f1NB1WBg6I5^*833Ga>T-n3i<&&K+Ez~^Zp5AMQbw;be4`#-f<^{lVfLnJLKfm{ zW8r*40+l1l*Wo_dm5L~I+b*51^<6+zQ6CWCp?8>;aVaV(J-& z-j&a?!RQP3I>QXL#+wh*0000!j*(iBpF_$dc0jq4IfsVw$44ud_LYY+Q*@mx)buL6 zHDEn7l+3WYbov*L1C};b>|~<*&R*egrf-sWLzT2FKc%k7i}n1(!QR8=cc)mp2zUv{>Rp<}DX z)?*F;0Dw1R_PZc;S7D88<4TrO)ZNcIN=gT$iBQJby8V_XQB%!iWhS2zTS~g}&%6wC z!_SQ;8T?P!>5=(-f)$0>&(NiC$WIoUEVW&lrw9u#!_}Mo49Lv0%*d5LoeAOSv3v-* zMSG004o3h002ov JPDHLkV1k^8J2n6S literal 0 HcmV?d00001 diff --git a/ragger_tests/snapshots/nanosp/subintent_vector_4.si/00002.png b/ragger_tests/snapshots/nanosp/subintent_vector_4.si/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..8c60fc0d6766e73e9ecd04c2f15255ada8973e12 GIT binary patch literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|>x2ba4!+nDh2VbkSi29*4kN zC;e{xpLE09eny2!Lo2(+rZ?65gr+AkIoUV?HNn7vpkG$|Zf$!WJMZpY`PXbc9-2ob zoK4LP-F~V}Kd3XarceDY|G|@23JsW3HrVU0s9!5xa5eJ!^PdmP6Q-SISk3YE_Sc!s zA|*>crNzIlukBWOQ?v5Z*Lknp^rD%Qde-+H%xy2S+ebzEoEqVPjvHn-yyz6G~lU|$YL^IFY zo*e#f>CH8vyG-*loIjVge`I{|?dt)5tw({J=c|}bUVnOir_Wx`?68c~kD*8QSgFi5 zJ)Ptg>MAhj=978JRyuI!{>5zf|Lx9>- z-yQ!a7Kw|8eKlCn>33kls_$VEQ%&yHOqv2z4F$V?T3R>MzTV|#{ru058O3(`izc4B zDEIzVMSiT+On2*P(O*l~&otG`e#({c{!@g~#(+yH*{X-SwY|5vrTn*k+Oykb@#BbH z+Uo9IOn;Ogh0k`r|7NAq5 z_dHp2)BmdC9XY|fh||YsDV@F978JRyuBVM)TAK55Ky#> z^Sk||@|TA@%MvbmdCaYR-+iXBH8n68qz3|CJS!_b2UUcWtdgT|lWwyNj z^n9|qThYa<{)UX1@qBD=|N2+Wn_tyy?N`00{0HBPdzZhL|G2s%R55Oah}h+Cy3x0$ p3)q!6>OT~{x-pp@=6Z$$7v(N!El*ai?Fj>kdAj978JRyuBVM)TAK55Ky#> z^Sk||@|TA@%MvbmdCaYR-+iXBH8n68qz3|CJS!_b2UUcWtdgT|lWwyNj z^n9|qThYa<{)UX1@qBD=|N2+Wn_tyy?N`00{0HBPdzZhL|G2s%R55Oah}h+Cy3x0$ p3)q!6>OT~{x-pp@=6Z$$7v(N!El*ai?Fj>kdAj-U>JvrLSy=YB$CJiRs{;F`gyj1RoOvB<;n_ zJB)m)`*zNVg=Z(z4b&PpU#1Bmg#0@)Ye71F^#qEzW_YPAPMbYF!(cSc$Z+=<%-LnbC4ZZ z44^kMo=R7eYrWdjU`rdw++J8uWU-`}mk(=Iv-aq0up<+e3nxoy#X7RVf)>j|BfV~A zp?o53u&ET0#lo)d&iXwS9?b3?@GMiO zCOEtwFZ?+31`i;F5b_pQf-nv{Ab3VfI>isL5j|284+k>g$VDI+_VbBn9FY>xuotc= z0VP-lmmENeX{wwRVJ;7li*m&UB9+eNVx%tnL@t*fgIR{t?dGGQ*IfJ6KI3ll5)Br-|Jsz3!Ccq3}DbTmju#QzNt{ zY1OvwDt4lS?uHJ&&>kxdfT-17*QFOKw-RiO(pO#()i%ZeN!P(WW4sr{2|gwUNZP=< zXBb&(+bU;_E6*yUI%=6OK1>rr2q7b6;PPT15+0{QXhPahe)l*Kv`R?JfE>T#%0u&j zb!25wt00kUj_Y8hb#rX#p{fRFudA^-qNzucp-xCObw99o2&7X`KG;y{0FJ0tXcH^b zu70s9-cQhv@C2TjOB8J5iWgJt?_R!4A1!O&z2m0ppuj|{b{+E)vkC+ay>)Qd;vJ~_ z{qXs>6-ft_@1SWNrWop={5sxX<)~ZwF9z(k!7MfsJXwd_>=*kD@Ye zS$l6nuFXhMEHHkSi8JH2xw~Om^-VbN&D^jnd>-VRp%gC=8uz`+;dP41Aca|EmN zy-XY6X#-Hi&hbFylPlm#$j8+{A%qY@2qA<{9 literal 0 HcmV?d00001 diff --git a/ragger_tests/snapshots/nanosp/subintent_vector_5.si/00002.png b/ragger_tests/snapshots/nanosp/subintent_vector_5.si/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..8c60fc0d6766e73e9ecd04c2f15255ada8973e12 GIT binary patch literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|>x2ba4!+nDh2VbkSi29*4kN zC;e{xpLE09eny2!Lo2(+rZ?65gr+AkIoUV?HNn7vpkG$|Zf$!WJMZpY`PXbc9-2ob zoK4LP-F~V}Kd3XarceDY|G|@23JsW3HrVU0s9!5xa5eJ!^PdmP6Q-SISk3YE_Sc!s zA|*>crNzIlukBWOQ?v5Z*Lknp^rD%Qde-+H%xy2S+ebzEoEqVPjvHn-yyz6G~lU|$YL^IFY zo*e#f>CH8vyG-*loIjVge`I{|?dt)5tw({J=c|}bUVnOir_Wx`?68c~kD*8QSgFi5 zJ)Ptg>MAhj=978JRyuI!{>5zf|Lx9>- z-yQ!a7Kw|8eKlCn>33kls_$VEQ%&yHOqv2z4F$V?T3R>MzTV|#{ru058O3(`izc4B zDEIzVMSiT+On2*P(O*l~&otG`e#({c{!@g~#(+yH*{X-SwY|5vrTn*k+Oykb@#BbH z+Uo9IOn;Ogh0k`r|7NAq5 z_dHp2)BmdC9XY|fh||YsDV@F978JRyuBVM)TAK55Ky#> z^Sk||@|TA@%MvbmdCaYR-+iXBH8n68qz3|CJS!_b2UUcWtdgT|lWwyNj z^n9|qThYa<{)UX1@qBD=|N2+Wn_tyy?N`00{0HBPdzZhL|G2s%R55Oah}h+Cy3x0$ p3)q!6>OT~{x-pp@=6Z$$7v(N!El*ai?Fj>kdAj978JRyuBVM)TAK55Ky#> z^Sk||@|TA@%MvbmdCaYR-+iXBH8n68qz3|CJS!_b2UUcWtdgT|lWwyNj z^n9|qThYa<{)UX1@qBD=|N2+Wn_tyy?N`00{0HBPdzZhL|G2s%R55Oah}h+Cy3x0$ p3)q!6>OT~{x-pp@=6Z$$7v(N!El*ai?Fj>kdAj>d2!)-=?8N;q$-T@&Glp;w(PDciIp5R0ih$xzr7eXJLI@#*5JCtcgb+dqInGy# zme!j5TWd{GyJUT2RMz^E8_MtVov7=vac`}ukpEwP&Z%y#)mm`^USPD$+TL9KC@?pB zd2k*$=1`APp13SBR{$dW)1Ag(T+~0zS}7*wv3%qS?vo>PO7m3t9WqydIclnh7?Lh_ zbXUQFcFavDcw#&<0}ye`T-U`qDv#o9jB>6#aH<_V10?1I9|hxGB9q|XoB@*FG4mNl z)?!;V8GYeZXOy8{`0&FtA%qYz#yRwyqYP_D3P;B;YFj#f<0c&6woB4*Msqrjqi&bj z=uBO5h{PCcvwlQ;zIzX7BH`nUT|ZG+r!Q)a|^E|olQ_F#XY)L;Z*g-Mh8HFQ8x82nZUwG zryLpN(`G6mgpfDc|1QWpl+2fWj(k}>==NHnj4Y2Iyup$u7~Sv9zp^A!70D(owTdKX z&gFl6cJ2Z4n$z#-?@WXMrh6&3^b~dipt2d5U@EW=j8aKu7PI@U(7oIo#!ZfV6@lF| zb19MJQlu3QVmxgni3B4(U=Mwka*nLI9MN-P-8}bM3Nbmd0?hXT(}%@~%wCDYe1nwb rGa-Z!LI@#*5JCtcgb+dq`7nL~1i|YFn)vWl00000NkvXXu0mjfq~j_X literal 0 HcmV?d00001 diff --git a/ragger_tests/snapshots/nanosp/subintent_vector_6.si/00001.png b/ragger_tests/snapshots/nanosp/subintent_vector_6.si/00001.png new file mode 100644 index 0000000000000000000000000000000000000000..474f2fb06d21dbc8f55fddb9ffcb85be5d5e18d3 GIT binary patch literal 711 zcmV;&0yzDNP)6sfePnDlx10=m; zW(*^r>bk>ZjD?RjW-!zXFAk;&A%y%pl4n8q@e5c2=CNi*it7O*ODSe5+~}o|xB>~$ z^o5xOX;stw2z+FgfW7k~G4EokFlg0d5S@-{f188epnK_=601F`$~=A29m%yeXsXPC zqJ$ssr;eS?)&#YX6<}U?{nWD|)(M(673P+LXZq2LR_$mnRk^dBp&9yB>SHopD{b9l zi9&B3JY~_nxu0&pQHSXXl=s={`Da8dO?!9j{rdMT=-x5NnMP_2OUjc6#d3IWzvwU& z^uvRUTwMKD%OGK>S*X9yc(rtuX&d6EnlieBf!*Nk6G902*PJ>FDo3(h(=*39gDR$< zETJn#Jg1b21J*cv*h?5ey8H1kj}em^r#$pMIe9RT--SZD8ZE_p%f zB0oTYhSGX={gyKQV3Hs>uKC;y^P>NPmaz4><}Q|fl8l{j5El+I;6AQ*{OQTeRKKTW tT1(3iLI@#*5JCtcgb+dqA%u`W!#C2_6C=waU5x+$002ovPDHLkV1g4NOuGO8 literal 0 HcmV?d00001 diff --git a/ragger_tests/snapshots/nanosp/subintent_vector_6.si/00002.png b/ragger_tests/snapshots/nanosp/subintent_vector_6.si/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..8c60fc0d6766e73e9ecd04c2f15255ada8973e12 GIT binary patch literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|>x2ba4!+nDh2VbkSi29*4kN zC;e{xpLE09eny2!Lo2(+rZ?65gr+AkIoUV?HNn7vpkG$|Zf$!WJMZpY`PXbc9-2ob zoK4LP-F~V}Kd3XarceDY|G|@23JsW3HrVU0s9!5xa5eJ!^PdmP6Q-SISk3YE_Sc!s zA|*>crNzIlukBWOQ?v5Z*Lknp^rD%Qde-+H%xy2S+ebzEoEqVPjvHn-yyz6G~lU|$YL^IFY zo*e#f>CH8vyG-*loIjVge`I{|?dt)5tw({J=c|}bUVnOir_Wx`?68c~kD*8QSgFi5 zJ)Ptg>MAhj=978JRyuI!{>5zf|Lx9>- z-yQ!a7Kw|8eKlCn>33kls_$VEQ%&yHOqv2z4F$V?T3R>MzTV|#{ru058O3(`izc4B zDEIzVMSiT+On2*P(O*l~&otG`e#({c{!@g~#(+yH*{X-SwY|5vrTn*k+Oykb@#BbH z+Uo9IOn;Ogh0k`r|7NAq5 z_dHp2)BmdC9XY|fh||YsDV@F978JRyuBVM)TAK55Ky#> z^Sk||@|TA@%MvbmdCaYR-+iXBH8n68qz3|CJS!_b2UUcWtdgT|lWwyNj z^n9|qThYa<{)UX1@qBD=|N2+Wn_tyy?N`00{0HBPdzZhL|G2s%R55Oah}h+Cy3x0$ p3)q!6>OT~{x-pp@=6Z$$7v(N!El*ai?Fj>kdAj978JRyuBVM)TAK55Ky#> z^Sk||@|TA@%MvbmdCaYR-+iXBH8n68qz3|CJS!_b2UUcWtdgT|lWwyNj z^n9|qThYa<{)UX1@qBD=|N2+Wn_tyy?N`00{0HBPdzZhL|G2s%R55Oah}h+Cy3x0$ p3)q!6>OT~{x-pp@=6Z$$7v(N!El*ai?Fj>kdAj Date: Mon, 4 Nov 2024 14:59:38 +0100 Subject: [PATCH 7/7] preauth raw secp256k1 --- ragger_tests/application_client/curve.py | 13 +++++ .../00000.png | Bin 0 -> 361 bytes .../00001.png | Bin 0 -> 371 bytes .../00000.png | Bin .../00001.png | Bin .../00002.png | Bin .../00003.png | Bin .../00004.png | Bin .../00000.png | Bin .../00001.png | Bin .../00002.png | Bin .../00003.png | Bin .../00004.png | Bin .../00000.png | Bin .../00001.png | Bin .../00002.png | Bin .../00003.png | Bin .../00004.png | Bin .../00000.png | Bin .../00001.png | Bin .../00002.png | Bin .../00003.png | Bin .../00004.png | Bin .../00000.png | Bin .../00001.png | Bin .../00002.png | Bin .../00003.png | Bin .../00004.png | Bin .../00000.png | Bin .../00001.png | Bin .../00002.png | Bin .../00003.png | Bin .../00004.png | Bin .../00000.png | Bin .../00001.png | Bin .../00002.png | Bin .../00003.png | Bin .../00004.png | Bin .../00000.png | Bin .../00001.png | Bin .../00002.png | Bin .../00003.png | Bin .../00004.png | Bin .../00000.png | Bin 0 -> 660 bytes .../00001.png | Bin 0 -> 702 bytes .../00002.png | Bin 0 -> 410 bytes .../00003.png | Bin 0 -> 301 bytes .../00004.png} | Bin .../00000.png | Bin 0 -> 684 bytes .../00001.png | Bin 0 -> 694 bytes .../00002.png | Bin 0 -> 410 bytes .../00003.png | Bin 0 -> 301 bytes .../00004.png} | Bin .../00000.png | Bin 0 -> 635 bytes .../00001.png | Bin 0 -> 657 bytes .../00002.png | Bin 0 -> 410 bytes .../00003.png | Bin 0 -> 301 bytes .../00004.png} | Bin .../00000.png | Bin 0 -> 675 bytes .../00001.png | Bin 0 -> 705 bytes .../00002.png | Bin 0 -> 410 bytes .../00003.png | Bin 0 -> 301 bytes .../00004.png} | Bin .../00000.png | Bin 0 -> 700 bytes .../00001.png | Bin 0 -> 677 bytes .../00002.png | Bin 0 -> 410 bytes .../00003.png | Bin 0 -> 301 bytes .../00004.png} | Bin .../00000.png | Bin 0 -> 677 bytes .../00001.png | Bin 0 -> 675 bytes .../00002.png | Bin 0 -> 410 bytes .../00003.png | Bin 0 -> 301 bytes .../00004.png} | Bin .../00000.png | Bin 0 -> 681 bytes .../00001.png | Bin 0 -> 694 bytes .../00002.png | Bin 0 -> 410 bytes .../00003.png | Bin 0 -> 301 bytes .../00004.png} | Bin .../00000.png | Bin 0 -> 657 bytes .../00001.png | Bin 0 -> 711 bytes .../00002.png | Bin 0 -> 410 bytes .../00003.png | Bin 0 -> 301 bytes .../00004.png} | Bin ragger_tests/test_sign_preauth_raw_ed25519.py | 46 ++++++++++-------- .../test_sign_preauth_raw_secp256k1.py | 12 +++++ ragger_tests/test_sign_tx_secp256k1.py | 15 ++++-- 86 files changed, 60 insertions(+), 26 deletions(-) create mode 100644 ragger_tests/snapshots/nanos/test_sign_preauth_raw_secp256k1_subintent_vector_3.si/00000.png create mode 100644 ragger_tests/snapshots/nanos/test_sign_preauth_raw_secp256k1_subintent_vector_3.si/00001.png rename ragger_tests/snapshots/nanosp/{checked_childless_subintent.si => test_sign_preauth_raw_ed25519_checked_childless_subintent.si}/00000.png (100%) rename ragger_tests/snapshots/nanosp/{checked_childless_subintent.si => test_sign_preauth_raw_ed25519_checked_childless_subintent.si}/00001.png (100%) rename ragger_tests/snapshots/nanosp/{checked_childless_subintent.si => test_sign_preauth_raw_ed25519_checked_childless_subintent.si}/00002.png (100%) rename ragger_tests/snapshots/nanosp/{checked_childless_subintent.si => test_sign_preauth_raw_ed25519_checked_childless_subintent.si}/00003.png (100%) rename ragger_tests/snapshots/nanosp/{checked_childless_subintent.si => test_sign_preauth_raw_ed25519_checked_childless_subintent.si}/00004.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_0.si => test_sign_preauth_raw_ed25519_subintent_vector_0.si}/00000.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_0.si => test_sign_preauth_raw_ed25519_subintent_vector_0.si}/00001.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_0.si => test_sign_preauth_raw_ed25519_subintent_vector_0.si}/00002.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_0.si => test_sign_preauth_raw_ed25519_subintent_vector_0.si}/00003.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_0.si => test_sign_preauth_raw_ed25519_subintent_vector_0.si}/00004.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_1.si => test_sign_preauth_raw_ed25519_subintent_vector_1.si}/00000.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_1.si => test_sign_preauth_raw_ed25519_subintent_vector_1.si}/00001.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_1.si => test_sign_preauth_raw_ed25519_subintent_vector_1.si}/00002.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_1.si => test_sign_preauth_raw_ed25519_subintent_vector_1.si}/00003.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_1.si => test_sign_preauth_raw_ed25519_subintent_vector_1.si}/00004.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_2.si => test_sign_preauth_raw_ed25519_subintent_vector_2.si}/00000.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_2.si => test_sign_preauth_raw_ed25519_subintent_vector_2.si}/00001.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_2.si => test_sign_preauth_raw_ed25519_subintent_vector_2.si}/00002.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_2.si => test_sign_preauth_raw_ed25519_subintent_vector_2.si}/00003.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_2.si => test_sign_preauth_raw_ed25519_subintent_vector_2.si}/00004.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_3.si => test_sign_preauth_raw_ed25519_subintent_vector_3.si}/00000.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_3.si => test_sign_preauth_raw_ed25519_subintent_vector_3.si}/00001.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_3.si => test_sign_preauth_raw_ed25519_subintent_vector_3.si}/00002.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_3.si => test_sign_preauth_raw_ed25519_subintent_vector_3.si}/00003.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_3.si => test_sign_preauth_raw_ed25519_subintent_vector_3.si}/00004.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_4.si => test_sign_preauth_raw_ed25519_subintent_vector_4.si}/00000.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_4.si => test_sign_preauth_raw_ed25519_subintent_vector_4.si}/00001.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_4.si => test_sign_preauth_raw_ed25519_subintent_vector_4.si}/00002.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_4.si => test_sign_preauth_raw_ed25519_subintent_vector_4.si}/00003.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_4.si => test_sign_preauth_raw_ed25519_subintent_vector_4.si}/00004.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_5.si => test_sign_preauth_raw_ed25519_subintent_vector_5.si}/00000.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_5.si => test_sign_preauth_raw_ed25519_subintent_vector_5.si}/00001.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_5.si => test_sign_preauth_raw_ed25519_subintent_vector_5.si}/00002.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_5.si => test_sign_preauth_raw_ed25519_subintent_vector_5.si}/00003.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_5.si => test_sign_preauth_raw_ed25519_subintent_vector_5.si}/00004.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_6.si => test_sign_preauth_raw_ed25519_subintent_vector_6.si}/00000.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_6.si => test_sign_preauth_raw_ed25519_subintent_vector_6.si}/00001.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_6.si => test_sign_preauth_raw_ed25519_subintent_vector_6.si}/00002.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_6.si => test_sign_preauth_raw_ed25519_subintent_vector_6.si}/00003.png (100%) rename ragger_tests/snapshots/nanosp/{subintent_vector_6.si => test_sign_preauth_raw_ed25519_subintent_vector_6.si}/00004.png (100%) create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_checked_childless_subintent.si/00000.png create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_checked_childless_subintent.si/00001.png create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_checked_childless_subintent.si/00002.png create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_checked_childless_subintent.si/00003.png rename ragger_tests/snapshots/nanosp/{checked_childless_subintent.si/00005.png => test_sign_preauth_raw_secp256k1_checked_childless_subintent.si/00004.png} (100%) create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_0.si/00000.png create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_0.si/00001.png create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_0.si/00002.png create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_0.si/00003.png rename ragger_tests/snapshots/nanosp/{subintent_vector_0.si/00005.png => test_sign_preauth_raw_secp256k1_subintent_vector_0.si/00004.png} (100%) create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_1.si/00000.png create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_1.si/00001.png create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_1.si/00002.png create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_1.si/00003.png rename ragger_tests/snapshots/nanosp/{subintent_vector_1.si/00005.png => test_sign_preauth_raw_secp256k1_subintent_vector_1.si/00004.png} (100%) create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_2.si/00000.png create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_2.si/00001.png create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_2.si/00002.png create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_2.si/00003.png rename ragger_tests/snapshots/nanosp/{subintent_vector_2.si/00005.png => test_sign_preauth_raw_secp256k1_subintent_vector_2.si/00004.png} (100%) create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_3.si/00000.png create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_3.si/00001.png create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_3.si/00002.png create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_3.si/00003.png rename ragger_tests/snapshots/nanosp/{subintent_vector_3.si/00005.png => test_sign_preauth_raw_secp256k1_subintent_vector_3.si/00004.png} (100%) create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_4.si/00000.png create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_4.si/00001.png create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_4.si/00002.png create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_4.si/00003.png rename ragger_tests/snapshots/nanosp/{subintent_vector_4.si/00005.png => test_sign_preauth_raw_secp256k1_subintent_vector_4.si/00004.png} (100%) create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_5.si/00000.png create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_5.si/00001.png create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_5.si/00002.png create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_5.si/00003.png rename ragger_tests/snapshots/nanosp/{subintent_vector_5.si/00005.png => test_sign_preauth_raw_secp256k1_subintent_vector_5.si/00004.png} (100%) create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_6.si/00000.png create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_6.si/00001.png create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_6.si/00002.png create mode 100644 ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_6.si/00003.png rename ragger_tests/snapshots/nanosp/{subintent_vector_6.si/00005.png => test_sign_preauth_raw_secp256k1_subintent_vector_6.si/00004.png} (100%) create mode 100644 ragger_tests/test_sign_preauth_raw_secp256k1.py diff --git a/ragger_tests/application_client/curve.py b/ragger_tests/application_client/curve.py index 3a6c82ba..4f3193fe 100644 --- a/ragger_tests/application_client/curve.py +++ b/ragger_tests/application_client/curve.py @@ -6,6 +6,11 @@ class Curve(ABC): + @classmethod + @abstractmethod + def curve_name(cls) -> str: + pass + @classmethod @abstractmethod def unpack_pubkey(cls, response: bytes) -> PK: @@ -47,6 +52,10 @@ def ins_sign_pre_auth_raw(cls) -> InsType: pass class SECP256K1(Curve): + + @classmethod + def curve_name(cls) -> str: + return "secp256k1" @classmethod def ins_sign_rola(cls) -> InsType: @@ -82,6 +91,10 @@ def unpack_pubkey(cls, response: bytes) -> PK: class Curve25519(Curve): + + @classmethod + def curve_name(cls) -> str: + return "ed25519" @classmethod def ins_sign_rola(cls) -> InsType: diff --git a/ragger_tests/snapshots/nanos/test_sign_preauth_raw_secp256k1_subintent_vector_3.si/00000.png b/ragger_tests/snapshots/nanos/test_sign_preauth_raw_secp256k1_subintent_vector_3.si/00000.png new file mode 100644 index 0000000000000000000000000000000000000000..3713cb504db555ea6a2418a798b3d7c338de2f73 GIT binary patch literal 361 zcmV-v0ha!WP)25Ao)~m1=>9LpR05^8uCzE!09={W+kkJ@cNj$=8c+J)(~e5%~KU+os-;>4;0 z7N>DLb_yaht0s%`0F3tpaMCR2CI4gqz)!PP++m5eZKKFlww{9>6?3>n9-$}c*S~`A zE-1f&*<^AC8b>0(`QZx59PP4*?~;#jRiZZjQPidxZzKB3OR*5NtHXx1Z99s$7fu5j zo=a(oy9_PXe1>sdN^`RSw%m+2!?d*QZRjBsc=X*K3YoZDO000000000009=&^3Yh|Q`zMGU00000NkvXX Hu0mjfa)+WB literal 0 HcmV?d00001 diff --git a/ragger_tests/snapshots/nanos/test_sign_preauth_raw_secp256k1_subintent_vector_3.si/00001.png b/ragger_tests/snapshots/nanos/test_sign_preauth_raw_secp256k1_subintent_vector_3.si/00001.png new file mode 100644 index 0000000000000000000000000000000000000000..3a2c9fdd934190242f469865ae8b807fd685bef6 GIT binary patch literal 371 zcmV-(0gV2MP)GnAc{=& zr%{la>Q9C?u!LF264qf$K;C{Av2!iSFe<&=wv5e=Q5mA0bNaQecPk>5^2Aux`EPLt zehyzjiVQST^ue^v(B5*Wgdk2BACMd$*WEYWe%ct{j0=99MMgc|@&DqJ*(AkQ@-F?c(;XroBBGm2-{)82BBG!!B&7fV000000000000000+xbe- z(psZmYpp42r$*NXrB7|ifbw;|6LlS1_tskH^3UaGpX%0Ht(6Dh1^1Tt^zPn$C@ePb zc4r(I!*M;G^2BYCxdS+KKDW`_85i{r@hKIf^1NNMgrAezaGmC%@)a_7fVgVi47p2g zv8|^HUua{vVSp$4BhvvMv&4N}q^oi(#=%Hq<-o1B@eZJf0j@dYSv*1TF?Rsvo|yO! zL!a`#T`=0hv&M7--HnG|rU3u|{5z&=LBk7*CzE!LN-tQyByY6S?4dxXoKMp^@x52~ zHHP!ugNunR+H22jPC z@M`j6O7u5XbJ;!6J0VTa182$1P2T+ zG){M7b`gE@cLmLi-Q#BNfB@QAbaM3#Gq#d^2hi)w{!!2nFod-#p7DxBe=wkp;g|ENK6{FO2|}=MZDI!E@Cp}>}3mx#`|ab0K+_v^$E*Z6!msvpYcR?$_YwXeLZ6cf<0e z(Z$npl9l5q=z!r0me{bo_zF9BfIDichjvO@ zv7>JlJJ5+-cY$x5kDU{MG|S!B#dDQMNnVU{tvuk>4*CFzyTGH#_*;-g@HRd`(mLjz zVPvkYs}^Hkc(gG}s3kspm?ne}^6!|r3v$E4;>DABglT;pa;u|^belIM6dgUgN78M6 zyL+za#xHJ4lm$jcV1Y%U{_XaF?j(t0m!U-;EV(5G%ErPkMqh+BM=@ zJAI!K?UIrXW3&%0n?2X26Ynnka%p9_m6MR8>88(#%Ifm@#|KQ$!IPfj1lAMe=@Uew zUz8I@=0JU8X+~wEt@G@<(?lpSTfEnlZQgcCH4#iQlVap?N2`HN6+K9xx7nxX6!xJ#{2b*asaJ!O}+t<807*qoM6N<$f*k%%-~a#s literal 0 HcmV?d00001 diff --git a/ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_checked_childless_subintent.si/00002.png b/ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_checked_childless_subintent.si/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..8c60fc0d6766e73e9ecd04c2f15255ada8973e12 GIT binary patch literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|>x2ba4!+nDh2VbkSi29*4kN zC;e{xpLE09eny2!Lo2(+rZ?65gr+AkIoUV?HNn7vpkG$|Zf$!WJMZpY`PXbc9-2ob zoK4LP-F~V}Kd3XarceDY|G|@23JsW3HrVU0s9!5xa5eJ!^PdmP6Q-SISk3YE_Sc!s zA|*>crNzIlukBWOQ?v5Z*Lknp^rD%Qde-+H%xy2S+ebzEoEqVPjvHn-yyz6G~lU|$YL^IFY zo*e#f>CH8vyG-*loIjVge`I{|?dt)5tw({J=c|}bUVnOir_Wx`?68c~kD*8QSgFi5 zJ)Ptg>MAhj=978JRyuI!{>5zf|Lx9>- z-yQ!a7Kw|8eKlCn>33kls_$VEQ%&yHOqv2z4F$V?T3R>MzTV|#{ru058O3(`izc4B zDEIzVMSiT+On2*P(O*l~&otG`e#({c{!@g~#(+yH*{X-SwY|5vrTn*k+Oykb@#BbH z+Uo9IOn;Ogh0k`r|7NAq5 z_dHp2)BmdC9XY|fh||YsDV@F>kpzgsq!a)E00000000000002sI3Fom zT5I&Q)|#R=G`lt_y=zM@DBtHZQP;Ki-dY=6{=R&jQ{7stwekRb;NBAN_Tj@pVX@h- zJLABZj_VneC#FSa25{)}bfdX59_kO`T`ET9`E|__{+-mO8#GUq?~s`R;#(V@kh`Rd z9sR2C7i~;84e&&NWIDiOmYCN?S}KoXd>Co0Tv*i(t^pJ=z%^(5EuJ8Fn>B#aA57fC z(7Sx>5RA5P)RjB`HY(%UkNV;AD|G-5B2QS^GSA)V{%<;w*tGGqOSxLw%Q6fIrPQNvD`||# zH9|6U&`*b=AtYc}HMLe7Zrkk71}eFg0ruR3tDU1dVuvy$U_fmP4Ns}qX&$UNfM<}{ zTecgDJFjmO&9)(~=_zf&<{EIM7xc<|1CMQdB{*k5*a381dKB?1g)L27Cc2z*C8CD4 z?SL~2Jx^-$%ApAW0N^(3y`Y^2%P_qBUIzIu5#s5fZMy1eO()U*?OJFGW8QPosx^^9ELOri<#|#^%NhFOJWVUHozO2v z19s}{qPun{InpX&yCd%$UK}5jDQmAow7)4o1MEyRaOt;{ef;5;_091&Ep|L{@3)lq z;#$ujCj+K^8|~EJQL(4!XgwlPCba3~qe{|O2mk;8000000000000000oa7ta5w`S+ S)w?kO0000n1T literal 0 HcmV?d00001 diff --git a/ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_0.si/00001.png b/ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_0.si/00001.png new file mode 100644 index 0000000000000000000000000000000000000000..5cf0c20fa88bd2f23e90c2c10fbabb77cfbd8e0a GIT binary patch literal 694 zcmV;n0!jUeP)vDkiRbHKIvL(DFp-Y0!M3D+rBOy4v5WG z9*hIS9O?-1h0|i?3_$w#X-?x{T$FEywSBX`J(g>p;5w<96Uwgm9aheOVXuid#35wzecwIb3r=2_dn`YP0v~S>=EGlaU)0LZc z@LYk-+x2ba4!+nDh2VbkSi29*4kN zC;e{xpLE09eny2!Lo2(+rZ?65gr+AkIoUV?HNn7vpkG$|Zf$!WJMZpY`PXbc9-2ob zoK4LP-F~V}Kd3XarceDY|G|@23JsW3HrVU0s9!5xa5eJ!^PdmP6Q-SISk3YE_Sc!s zA|*>crNzIlukBWOQ?v5Z*Lknp^rD%Qde-+H%xy2S+ebzEoEqVPjvHn-yyz6G~lU|$YL^IFY zo*e#f>CH8vyG-*loIjVge`I{|?dt)5tw({J=c|}bUVnOir_Wx`?68c~kD*8QSgFi5 zJ)Ptg>MAhj=978JRyuI!{>5zf|Lx9>- z-yQ!a7Kw|8eKlCn>33kls_$VEQ%&yHOqv2z4F$V?T3R>MzTV|#{ru058O3(`izc4B zDEIzVMSiT+On2*P(O*l~&otG`e#({c{!@g~#(+yH*{X-SwY|5vrTn*k+Oykb@#BbH z+Uo9IOn;Ogh0k`r|7NAq5 z_dHp2)BmdC9XY|fh||YsDV@F0)+jEP)jeMWxv<}9L@`w3Uib;80KJrA*lOuDT=Be^KWR3u{)w(y-A!%Yq zXBFMhj=50EyYbN5Ob6kxuY2Yk;J^nE4GO zpX$0@XY_?vo#_m1K8S`R8fBeD>`O-z) zF>NcNM9U=X14j9ik(i%&+3QDHpN!fiU7~Hpt-tm|Qa&L7!M2lP-J6)3&$n#NoYY>H zKV1wq1CY{E@rrvUbggMgq{@9{40ZyblO)?YoJhsJNJsjzGa33NH<1mvoQlw`4@)Gh z0n_=h`wug!|nfl{S`u}i_&O&rw008)B=UkA#c0~?AbhXSA5{{n83O|yGtlN%Q z@Vy=)l1s^Yw3Nw-B#V~(xhd@M&_Cg8@bA0pmFLnA!DC1pGS^^AI7^p(tpd|Cv(ORt z^+c>|XCnTXZ?WX+Dw`vqP0(_4(w$i;^#V~m8*Xn-x3yH6~L$A1JYDFFZg0000000000Z0BQ%N-3rN znR8wud#m(Op>3*d84!QY=R&Px{a#AxhWzjH+9zE~$vMjjcp*niO|99xKoWDa#yjVM zQJmE2#1}4$l`BBfwx?EF4#q|PMon$aT6-Bks)_tgj*9D)r{YgoxdPN)>sly>q?&F0 zs>qHuiknXGh2ybu06f)F*LBid?$4c(si_phcbFqrrGDo=Na$d_YTWn7W5) zQ)S+6GTO#>E7KKJBM)z;0RR9(*n4WjTef??n3j^RD!1XEtBBqAkxQ8N{#x->jCw!Gza;HS7*2Tf$B62aWyZT^o)}xIM%w?zI3ta% zhtc~00PwGQ^e$-RQdA%1aXubb*~NI=@>C?5uG`M_PN(WWJcsWdt{D4NZ0B_bM!90= zUO$olgkx7g(mlM%lt)vo=jdW5tTU?EltZr~ zR7ST(YSOXN4}aEo4S=&5OCACR?k7%;w9UC^DWGzD1&p3p1)Oja6L6D5%fH_Us|9(A rmOA_D3;+NC00000000000E_qnUCIjQ=66FY00000NkvXXu0mjf;ixX3 literal 0 HcmV?d00001 diff --git a/ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_1.si/00002.png b/ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_1.si/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..8c60fc0d6766e73e9ecd04c2f15255ada8973e12 GIT binary patch literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|>x2ba4!+nDh2VbkSi29*4kN zC;e{xpLE09eny2!Lo2(+rZ?65gr+AkIoUV?HNn7vpkG$|Zf$!WJMZpY`PXbc9-2ob zoK4LP-F~V}Kd3XarceDY|G|@23JsW3HrVU0s9!5xa5eJ!^PdmP6Q-SISk3YE_Sc!s zA|*>crNzIlukBWOQ?v5Z*Lknp^rD%Qde-+H%xy2S+ebzEoEqVPjvHn-yyz6G~lU|$YL^IFY zo*e#f>CH8vyG-*loIjVge`I{|?dt)5tw({J=c|}bUVnOir_Wx`?68c~kD*8QSgFi5 zJ)Ptg>MAhj=978JRyuI!{>5zf|Lx9>- z-yQ!a7Kw|8eKlCn>33kls_$VEQ%&yHOqv2z4F$V?T3R>MzTV|#{ru058O3(`izc4B zDEIzVMSiT+On2*P(O*l~&otG`e#({c{!@g~#(+yH*{X-SwY|5vrTn*k+Oykb@#BbH z+Uo9IOn;Ogh0k`r|7NAq5 z_dHp2)BmdC9XY|fh||YsDV@F4Xgz!SY%W^WtbeK?eF zw)UVOfa$cJPI=^Ny%;Cu53^T_NqOBqvP8$p(R7_=SNRn(cYyiUx+m02 z(!_3!D*8o(>0Ad-T#w8Jh?r&W>%&Tww*msA^py=+?ZzA+F&%ug7|#-Mg4g5#NqaE! z3?qB>*v=WT@T@W2M6Gf2VVV#^$iHKy78J=*S|(DlkplQ)jOTTycCXFm8Pqg#cdje$vdDkMa~r5__Eff2ZFdX(qqa@m4>kL1>_p!lp^9KOq@CJgSkax zQ%}$X;zjK1Syw9ASBk)79Vc!*mZZP^Bjp$xIWL0K;h{=(-F}KIF^yR}n1Y9L#Q+Z+ zTL~eAd@Sop_ylCgQCc&z5V#6myra5FHIkg&>eDl+HBz?$qj2L|%cyHKR^P>@ZfDCy zp~j})ghwpf4OF#5E%r^_-nE!YvPw^l^#vGR_uZgm7EiRp&erv0sL?eq(;kdgvRrxs zr0oDVg3Fc#_c32+{C^cwB`IvimEoGj1=xJ!kF@MbgXyz;N-YQwdHe4rts2_Jcio~x za0&k%(M>Q?)##WDdV}a$BENhPLI@#*5JCtcgb+dqA%u`e`2~*Xnw<^sLNEXT002ov JPDHLkV1j^RJ0Ji6 literal 0 HcmV?d00001 diff --git a/ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_2.si/00001.png b/ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_2.si/00001.png new file mode 100644 index 0000000000000000000000000000000000000000..5e0774104e6d8855cfa43331fb154645fdbe1d51 GIT binary patch literal 705 zcmV;y0zUnTP) zOYfcjY^^O(o3cJLO7Gf|1?BhoT&U}@aqqpeDSuzieX4u!tu+~d7jm}LyQ4e1I7DoY z_FxF?M3EE!P3r_hh1xDV6gtH*<#>9jn4h9lmj0C==28RSLtzrSNK} z*HaZiOS6b@BUHVPzc+4GBPgaG0wRyl3Bji@+&D+mow(aXp%@Skqj*A+VyVi~=%T&{ zr@ZY_7?eV?Dwkqv1*J=##xh;OTu*|Fo4o$X0jW9I&GVkHY?K;(7egW6hSv6ov_)!Q zEoz>WN~Da;?dl%aZJB9P9nN|p6)vAn0001ggj_ACP**W}WepWdG;X#G9+KT&vsAyB zY#k;j5b07V(UecBgUDkgU5wFH9v1OEK)PhgsUkd4)THcUq3(vLX zvy@;pCL(sTL{pw0<`U+cI`9CYDXJ?us92k&aO};<@C>ybS;V n00000000000000005W_5PU{9x2ba4!+nDh2VbkSi29*4kN zC;e{xpLE09eny2!Lo2(+rZ?65gr+AkIoUV?HNn7vpkG$|Zf$!WJMZpY`PXbc9-2ob zoK4LP-F~V}Kd3XarceDY|G|@23JsW3HrVU0s9!5xa5eJ!^PdmP6Q-SISk3YE_Sc!s zA|*>crNzIlukBWOQ?v5Z*Lknp^rD%Qde-+H%xy2S+ebzEoEqVPjvHn-yyz6G~lU|$YL^IFY zo*e#f>CH8vyG-*loIjVge`I{|?dt)5tw({J=c|}bUVnOir_Wx`?68c~kD*8QSgFi5 zJ)Ptg>MAhj=978JRyuI!{>5zf|Lx9>- z-yQ!a7Kw|8eKlCn>33kls_$VEQ%&yHOqv2z4F$V?T3R>MzTV|#{ru058O3(`izc4B zDEIzVMSiT+On2*P(O*l~&otG`e#({c{!@g~#(+yH*{X-SwY|5vrTn*k+Oykb@#BbH z+Uo9IOn;Ogh0k`r|7NAq5 z_dHp2)BmdC9XY|fh||YsDV@F_YP)e>rB^LkK?ct@WhUm`}V4?eiSn| zuX1o6B+Xen%J{@(F>?i2WPdKF?O>c7FWk3MCiS*F^2F|wQFF?2)A$``t^oI|sUF%P zxnxUs75kx+=9&|H;&{v)0K_eKT^H}DTuQPr%DJ-PR9kokNZbh?4aTz|li*{{0Lj%c z_YNc9+PZ2o=HgywR6|$c=F2o8gphy7NG`~Ri(nLL54WVEJb10ly+`H&ZPa=sz>nfR z7uL7-?!0n?GDP2OrCB8j#PY}7&n~elS(^(we+*)lkd%?HL}F!4m&VTj zoGH7|BXfliLdc)SMloO@MduvSt^W zG2lR>C+IcQ2EA7~m<+*{NV}0NkAdCm>6l|t6TP4V;C2kZ)q+YlGO+DN#@)Eo77lko zy40I=i5OV{)5WqUs(TannJ&kavg=!SMOz(_FqjN+r=F_>eMLqB>~NBl&bxrq8|#ke zg79QWqfJcfZk;e{+TDmZraE02KJnY?6?)1z9mO`p0;w8gb+dqA%qY@ i2qA9ytz@7FprHlUbrC`cA%qY@2qA&pC@Je_tN^ROg&iN;&{<^lX{6MO-{wDmF_w z7zcqltw$(dxGYw#08QPW;Db-Cw>-X}eCLUU933@cZFIclPZdP+*O zt-FdI=)hbvz!%QP$_YT+GS~HGrOK@Y8)F?S2eR7698hBhxHlQE1qp(;$pJN`W9BEF zCU#(W$n>n6cH`6ELo{t3=+e zK+xr`cL8$?)hosueL@H!AIK+A};Bz`&e0G;CTduP!GiJ?v&?1$157nBv3XkND|z&9pwh6qbDz z(HsJ^fxZ#aKLw^puWyLMrpSQxm^Bj+WYIWDY6kkIMRWx2ba4!+nDh2VbkSi29*4kN zC;e{xpLE09eny2!Lo2(+rZ?65gr+AkIoUV?HNn7vpkG$|Zf$!WJMZpY`PXbc9-2ob zoK4LP-F~V}Kd3XarceDY|G|@23JsW3HrVU0s9!5xa5eJ!^PdmP6Q-SISk3YE_Sc!s zA|*>crNzIlukBWOQ?v5Z*Lknp^rD%Qde-+H%xy2S+ebzEoEqVPjvHn-yyz6G~lU|$YL^IFY zo*e#f>CH8vyG-*loIjVge`I{|?dt)5tw({J=c|}bUVnOir_Wx`?68c~kD*8QSgFi5 zJ)Ptg>MAhj=978JRyuI!{>5zf|Lx9>- z-yQ!a7Kw|8eKlCn>33kls_$VEQ%&yHOqv2z4F$V?T3R>MzTV|#{ru058O3(`izc4B zDEIzVMSiT+On2*P(O*l~&otG`e#({c{!@g~#(+yH*{X-SwY|5vrTn*k+Oykb@#BbH z+Uo9IOn;Ogh0k`r|7NAq5 z_dHp2)BmdC9XY|fh||YsDV@FVK#9i{?38m1I5UVqleqR|3Eg8fn=eh_Xgb+dqA%qY@2qA=!?R+dz z$vMlPDWxT{UDhq5@~K~QKzyFhg<8k@J?C5v`TO#EmmgdzT7sO^M z55|FG4s|N=h0|i?3_#@j6sK`8Zqg6xQ&~;c$8yUPTqiAam2y*jhLtlw?X~I+aY#zF zt*Z)lv}3Lr;0woN2X?iMdw@g@aBDE0B@zU0vj<4Zi>YTA z`4rn$!RS}+b*3^@3J)Kq2_c01JH~TCxKpXu#b62Q;AL3#VMa$ijQY1?U*sHkvBCpM zq{I=FKdpw23022;xLF}CVuAG!-2;Ni?!IHn3jAK3-U?&hHo5OZvhtQxgp(k%N=kP( z@1L@QH4;soh#}S60TI8|`cy>MFvwQ(%cw6BSuUi4>g|#nyBveRwFJ%34Ea)ROd%)| zK^i)jirC8h1Ui#?Lqmfzp!(9HmW*u)anlvPRK$>VjGplpO)$ko2qEM}cP>cXn&MZd zqTnC@2lm5wI!J9exzj$75enu*s;^E~il$j;J?hHXSe1L-Xj+W3<|1ZAO&0$ebp3wY zg=f1NB1WBg6I5^*833Ga>T-n3i<&&K+Ez~^Zp5AMQbw;be4`#-f<^{lVfLnJLKfm{ zW8r*40+l1l*Wo_dm5L~I+b*51^<6+zQ6CWCp?8>;aVaV(J-& z-j&a?!RQP3I>QXL#+wh*0000!j*(iBpF_$dc0jq4IfsVw$44ud_LYY+Q*@mx)buL6 zHDEn7l+3WYbov*L1C};b>|~<*&R*egrf-sWLzT2FKc%k7i}n1(!QR8=cc)mp2zUv{>Rp<}DX z)?*F;0Dw1R_PZc;S7D88<4TrO)ZNcIN=gT$iBQJby8V_XQB%!iWhS2zTS~g}&%6wC z!_SQ;8T?P!>5=(-f)$0>&(NiC$WIoUEVW&lrw9u#!_}Mo49Lv0%*d5LoeAOSv3v-* zMSG004o3h002ov JPDHLkV1k^8J2n6S literal 0 HcmV?d00001 diff --git a/ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_4.si/00002.png b/ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_4.si/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..8c60fc0d6766e73e9ecd04c2f15255ada8973e12 GIT binary patch literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|>x2ba4!+nDh2VbkSi29*4kN zC;e{xpLE09eny2!Lo2(+rZ?65gr+AkIoUV?HNn7vpkG$|Zf$!WJMZpY`PXbc9-2ob zoK4LP-F~V}Kd3XarceDY|G|@23JsW3HrVU0s9!5xa5eJ!^PdmP6Q-SISk3YE_Sc!s zA|*>crNzIlukBWOQ?v5Z*Lknp^rD%Qde-+H%xy2S+ebzEoEqVPjvHn-yyz6G~lU|$YL^IFY zo*e#f>CH8vyG-*loIjVge`I{|?dt)5tw({J=c|}bUVnOir_Wx`?68c~kD*8QSgFi5 zJ)Ptg>MAhj=978JRyuI!{>5zf|Lx9>- z-yQ!a7Kw|8eKlCn>33kls_$VEQ%&yHOqv2z4F$V?T3R>MzTV|#{ru058O3(`izc4B zDEIzVMSiT+On2*P(O*l~&otG`e#({c{!@g~#(+yH*{X-SwY|5vrTn*k+Oykb@#BbH z+Uo9IOn;Ogh0k`r|7NAq5 z_dHp2)BmdC9XY|fh||YsDV@F-U>JvrLSy=YB$CJiRs{;F`gyj1RoOvB<;n_ zJB)m)`*zNVg=Z(z4b&PpU#1Bmg#0@)Ye71F^#qEzW_YPAPMbYF!(cSc$Z+=<%-LnbC4ZZ z44^kMo=R7eYrWdjU`rdw++J8uWU-`}mk(=Iv-aq0up<+e3nxoy#X7RVf)>j|BfV~A zp?o53u&ET0#lo)d&iXwS9?b3?@GMiO zCOEtwFZ?+31`i;F5b_pQf-nv{Ab3VfI>isL5j|284+k>g$VDI+_VbBn9FY>xuotc= z0VP-lmmENeX{wwRVJ;7li*m&UB9+eNVx%tnL@t*fgIR{t?dGGQ*IfJ6KI3ll5)Br-|Jsz3!Ccq3}DbTmju#QzNt{ zY1OvwDt4lS?uHJ&&>kxdfT-17*QFOKw-RiO(pO#()i%ZeN!P(WW4sr{2|gwUNZP=< zXBb&(+bU;_E6*yUI%=6OK1>rr2q7b6;PPT15+0{QXhPahe)l*Kv`R?JfE>T#%0u&j zb!25wt00kUj_Y8hb#rX#p{fRFudA^-qNzucp-xCObw99o2&7X`KG;y{0FJ0tXcH^b zu70s9-cQhv@C2TjOB8J5iWgJt?_R!4A1!O&z2m0ppuj|{b{+E)vkC+ay>)Qd;vJ~_ z{qXs>6-ft_@1SWNrWop={5sxX<)~ZwF9z(k!7MfsJXwd_>=*kD@Ye zS$l6nuFXhMEHHkSi8JH2xw~Om^-VbN&D^jnd>-VRp%gC=8uz`+;dP41Aca|EmN zy-XY6X#-Hi&hbFylPlm#$j8+{A%qY@2qA<{9 literal 0 HcmV?d00001 diff --git a/ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_5.si/00002.png b/ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_5.si/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..8c60fc0d6766e73e9ecd04c2f15255ada8973e12 GIT binary patch literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|>x2ba4!+nDh2VbkSi29*4kN zC;e{xpLE09eny2!Lo2(+rZ?65gr+AkIoUV?HNn7vpkG$|Zf$!WJMZpY`PXbc9-2ob zoK4LP-F~V}Kd3XarceDY|G|@23JsW3HrVU0s9!5xa5eJ!^PdmP6Q-SISk3YE_Sc!s zA|*>crNzIlukBWOQ?v5Z*Lknp^rD%Qde-+H%xy2S+ebzEoEqVPjvHn-yyz6G~lU|$YL^IFY zo*e#f>CH8vyG-*loIjVge`I{|?dt)5tw({J=c|}bUVnOir_Wx`?68c~kD*8QSgFi5 zJ)Ptg>MAhj=978JRyuI!{>5zf|Lx9>- z-yQ!a7Kw|8eKlCn>33kls_$VEQ%&yHOqv2z4F$V?T3R>MzTV|#{ru058O3(`izc4B zDEIzVMSiT+On2*P(O*l~&otG`e#({c{!@g~#(+yH*{X-SwY|5vrTn*k+Oykb@#BbH z+Uo9IOn;Ogh0k`r|7NAq5 z_dHp2)BmdC9XY|fh||YsDV@F>d2!)-=?8N;q$-T@&Glp;w(PDciIp5R0ih$xzr7eXJLI@#*5JCtcgb+dqInGy# zme!j5TWd{GyJUT2RMz^E8_MtVov7=vac`}ukpEwP&Z%y#)mm`^USPD$+TL9KC@?pB zd2k*$=1`APp13SBR{$dW)1Ag(T+~0zS}7*wv3%qS?vo>PO7m3t9WqydIclnh7?Lh_ zbXUQFcFavDcw#&<0}ye`T-U`qDv#o9jB>6#aH<_V10?1I9|hxGB9q|XoB@*FG4mNl z)?!;V8GYeZXOy8{`0&FtA%qYz#yRwyqYP_D3P;B;YFj#f<0c&6woB4*Msqrjqi&bj z=uBO5h{PCcvwlQ;zIzX7BH`nUT|ZG+r!Q)a|^E|olQ_F#XY)L;Z*g-Mh8HFQ8x82nZUwG zryLpN(`G6mgpfDc|1QWpl+2fWj(k}>==NHnj4Y2Iyup$u7~Sv9zp^A!70D(owTdKX z&gFl6cJ2Z4n$z#-?@WXMrh6&3^b~dipt2d5U@EW=j8aKu7PI@U(7oIo#!ZfV6@lF| zb19MJQlu3QVmxgni3B4(U=Mwka*nLI9MN-P-8}bM3Nbmd0?hXT(}%@~%wCDYe1nwb rGa-Z!LI@#*5JCtcgb+dq`7nL~1i|YFn)vWl00000NkvXXu0mjfq~j_X literal 0 HcmV?d00001 diff --git a/ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_6.si/00001.png b/ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_6.si/00001.png new file mode 100644 index 0000000000000000000000000000000000000000..474f2fb06d21dbc8f55fddb9ffcb85be5d5e18d3 GIT binary patch literal 711 zcmV;&0yzDNP)6sfePnDlx10=m; zW(*^r>bk>ZjD?RjW-!zXFAk;&A%y%pl4n8q@e5c2=CNi*it7O*ODSe5+~}o|xB>~$ z^o5xOX;stw2z+FgfW7k~G4EokFlg0d5S@-{f188epnK_=601F`$~=A29m%yeXsXPC zqJ$ssr;eS?)&#YX6<}U?{nWD|)(M(673P+LXZq2LR_$mnRk^dBp&9yB>SHopD{b9l zi9&B3JY~_nxu0&pQHSXXl=s={`Da8dO?!9j{rdMT=-x5NnMP_2OUjc6#d3IWzvwU& z^uvRUTwMKD%OGK>S*X9yc(rtuX&d6EnlieBf!*Nk6G902*PJ>FDo3(h(=*39gDR$< zETJn#Jg1b21J*cv*h?5ey8H1kj}em^r#$pMIe9RT--SZD8ZE_p%f zB0oTYhSGX={gyKQV3Hs>uKC;y^P>NPmaz4><}Q|fl8l{j5El+I;6AQ*{OQTeRKKTW tT1(3iLI@#*5JCtcgb+dqA%u`W!#C2_6C=waU5x+$002ovPDHLkV1g4NOuGO8 literal 0 HcmV?d00001 diff --git a/ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_6.si/00002.png b/ragger_tests/snapshots/nanosp/test_sign_preauth_raw_secp256k1_subintent_vector_6.si/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..8c60fc0d6766e73e9ecd04c2f15255ada8973e12 GIT binary patch literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|>x2ba4!+nDh2VbkSi29*4kN zC;e{xpLE09eny2!Lo2(+rZ?65gr+AkIoUV?HNn7vpkG$|Zf$!WJMZpY`PXbc9-2ob zoK4LP-F~V}Kd3XarceDY|G|@23JsW3HrVU0s9!5xa5eJ!^PdmP6Q-SISk3YE_Sc!s zA|*>crNzIlukBWOQ?v5Z*Lknp^rD%Qde-+H%xy2S+ebzEoEqVPjvHn-yyz6G~lU|$YL^IFY zo*e#f>CH8vyG-*loIjVge`I{|?dt)5tw({J=c|}bUVnOir_Wx`?68c~kD*8QSgFi5 zJ)Ptg>MAhj=978JRyuI!{>5zf|Lx9>- z-yQ!a7Kw|8eKlCn>33kls_$VEQ%&yHOqv2z4F$V?T3R>MzTV|#{ru058O3(`izc4B zDEIzVMSiT+On2*P(O*l~&otG`e#({c{!@g~#(+yH*{X-SwY|5vrTn*k+Oykb@#BbH z+Uo9IOn;Ogh0k`r|7NAq5 z_dHp2)BmdC9XY|fh||YsDV@F