From d476542fd4066a200a807ac8fec5cb8a622ee125 Mon Sep 17 00:00:00 2001 From: vbouzon Date: Thu, 23 May 2024 09:26:42 +0200 Subject: [PATCH 1/6] Update SDK --- .cargo/config.toml | 9 +- .gitattributes | 2 + .../workflows/build_and_functional_tests.yml | 33 + .github/workflows/coding_style_checks.yml | 21 + .github/workflows/guidelines_enforcer.yml | 23 + .github/workflows/misspellings_checks.yml | 29 + .github/workflows/python_tests_checks.yml | 44 + .github/workflows/rust.yml | 54 -- .gitignore | 24 + Cargo.lock | 769 ++++++++++++++++++ Cargo.toml | 36 +- app_stax.json | 18 + build.rs | 3 + crab.gif | Bin 0 -> 861 bytes crab_14x14.gif | Bin 0 -> 854 bytes crab_32x32.gif | Bin 0 -> 385 bytes crab_40x40.gif | Bin 0 -> 479 bytes crab_64x64.gif | Bin 0 -> 721 bytes ledger_app.toml | 9 +- src/c/aes.c | 44 +- src/c/aes.h | 10 +- src/main.rs | 127 +-- 22 files changed, 1102 insertions(+), 153 deletions(-) create mode 100644 .gitattributes create mode 100644 .github/workflows/build_and_functional_tests.yml create mode 100644 .github/workflows/coding_style_checks.yml create mode 100644 .github/workflows/guidelines_enforcer.yml create mode 100644 .github/workflows/misspellings_checks.yml create mode 100644 .github/workflows/python_tests_checks.yml delete mode 100644 .github/workflows/rust.yml create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 app_stax.json create mode 100644 crab.gif create mode 100644 crab_14x14.gif create mode 100644 crab_32x32.gif create mode 100644 crab_40x40.gif create mode 100644 crab_64x64.gif diff --git a/.cargo/config.toml b/.cargo/config.toml index 0afae1e..c233973 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,6 +1,9 @@ -[target.nanos] -runner = "speculos.py -m nanos" +[target.nanosplus] +runner = "speculos -a=1 --model=nanosp" + +[build] +target = "nanosplus" [unstable] build-std = ["core"] -build-std-features = ["compiler-builtins-mem"] \ No newline at end of file +build-std-features = ["compiler-builtins-mem"] diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.github/workflows/build_and_functional_tests.yml b/.github/workflows/build_and_functional_tests.yml new file mode 100644 index 0000000..3f5937d --- /dev/null +++ b/.github/workflows/build_and_functional_tests.yml @@ -0,0 +1,33 @@ +name: Build and run functional tests using ragger through reusable workflow + +# This workflow will build the app and then run functional tests using the Ragger framework upon Speculos emulation. +# It calls a reusable workflow developed by Ledger's internal developer team to build the application and upload the +# resulting binaries. +# It then calls another reusable workflow to run the Ragger tests on the compiled application binary. +# +# While this workflow is optional, having functional testing on your application is mandatory and this workflow and +# tooling environment is meant to be easy to use and adapt after forking your application + +on: + workflow_dispatch: + push: + branches: + - master + - main + - develop + pull_request: + +jobs: + build_application: + name: Build application using the reusable workflow + uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_build.yml@v1 + with: + upload_app_binaries_artifact: "compiled_app_binaries" + builder: ledger-app-builder + + ragger_tests: + name: Run ragger tests using the reusable workflow + needs: build_application + uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_ragger_tests.yml@v1 + with: + download_app_binaries_artifact: "compiled_app_binaries" diff --git a/.github/workflows/coding_style_checks.yml b/.github/workflows/coding_style_checks.yml new file mode 100644 index 0000000..7f91930 --- /dev/null +++ b/.github/workflows/coding_style_checks.yml @@ -0,0 +1,21 @@ +name: Run coding style check + +# This workflow will run linting checks to ensure a level of code quality among all Ledger applications. +# +# The presence of this workflow is mandatory as a minimal level of linting is required. + +on: + workflow_dispatch: + push: + branches: + - master + - main + - develop + pull_request: + +jobs: + check_linting: + name: Check linting using the reusable workflow + uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_lint.yml@v1 + with: + source: './src' diff --git a/.github/workflows/guidelines_enforcer.yml b/.github/workflows/guidelines_enforcer.yml new file mode 100644 index 0000000..fdaf9f2 --- /dev/null +++ b/.github/workflows/guidelines_enforcer.yml @@ -0,0 +1,23 @@ +name: Ensure compliance with Ledger guidelines + +# This workflow is mandatory in all applications +# It calls a reusable workflow guidelines_enforcer developed by Ledger's internal developer team. +# The successful completion of the reusable workflow is a mandatory step for an app to be available on the Ledger +# application store. +# +# More information on the guidelines can be found in the repository: +# LedgerHQ/ledger-app-workflows/ + +on: + workflow_dispatch: + push: + branches: + - master + - main + - develop + pull_request: + +jobs: + guidelines_enforcer: + name: Call Ledger guidelines_enforcer + uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_guidelines_enforcer.yml@v1 diff --git a/.github/workflows/misspellings_checks.yml b/.github/workflows/misspellings_checks.yml new file mode 100644 index 0000000..ec16bed --- /dev/null +++ b/.github/workflows/misspellings_checks.yml @@ -0,0 +1,29 @@ +name: Misspellings checks + +# This workflow performs some misspelling checks on the repository +# It is there to help us maintain a level of quality in our codebase and does not have to be kept on forked +# applications. + +on: + workflow_dispatch: + push: + branches: + - master + - main + - develop + pull_request: + +jobs: + misspell: + name: Check misspellings + runs-on: ubuntu-latest + steps: + - name: Clone + uses: actions/checkout@v3 + + - name: Check misspellings + uses: codespell-project/actions-codespell@v2 + with: + builtin: clear,rare + check_filenames: true + ignore_words_list: crate,Crate diff --git a/.github/workflows/python_tests_checks.yml b/.github/workflows/python_tests_checks.yml new file mode 100644 index 0000000..f035629 --- /dev/null +++ b/.github/workflows/python_tests_checks.yml @@ -0,0 +1,44 @@ +name: Checks on the Python tests + +# This workflow performs some checks on the Python client used by the Boilerplate tests +# It is there to help us maintain a level of quality in our codebase and does not have to be kept on forked +# applications. + +on: + workflow_dispatch: + push: + branches: + - master + - main + - develop + pull_request: + +jobs: + + lint: + name: Boilerplate client linting + runs-on: ubuntu-latest + steps: + - name: Clone + uses: actions/checkout@v3 + - name: Installing PIP dependencies + run: | + pip install pylint + pip install --extra-index-url https://test.pypi.org/simple/ -r tests/requirements.txt + - name: Lint Python code + run: | + pylint --rc tests/setup.cfg tests/application_client/ + + mypy: + name: Type checking + runs-on: ubuntu-latest + steps: + - name: Clone + uses: actions/checkout@v3 + - name: Installing PIP dependencies + run: | + pip install mypy + pip install --extra-index-url https://test.pypi.org/simple/ -r tests/requirements.txt + - name: Mypy type checking + run: | + mypy tests/application_client/ diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml deleted file mode 100644 index b0c66a3..0000000 --- a/.github/workflows/rust.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: Rust - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - workflow_dispatch: - inputs: - name: - description: 'Manually triggered' - -env: - CARGO_TERM_COLOR: always - -jobs: - build_clippy_fmt: - runs-on: ubuntu-latest - steps: - - name: arm-none-eabi-gcc - uses: fiam/arm-none-eabi-gcc@v1.0.3 - with: - release: '9-2019-q4' - - name: Checkout - uses: actions/checkout@v3 - - name: Checkout SDK (targets) - uses: actions/checkout@v3 - with: - repository: 'LedgerHQ/ledger-nanos-sdk' - path: rsdk - - name: Install clang - run: sudo apt-get update && sudo apt install -y clang - - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - override: true - components: rust-src, rustfmt, clippy - - name: Cargo build - uses: actions-rs/cargo@v1 - with: - command: build - args: -Z build-std=core -Z build-std-features=compiler-builtins-mem --target ./rsdk/nanos.json - - run: rustup component add clippy - - name: Cargo clippy - uses: actions-rs/cargo@v1 - with: - command: clippy - args: -Z build-std=core -Z build-std-features=compiler-builtins-mem --target ./rsdk/nanos.json - - run: rustup component add rustfmt - - name: Cargo fmt - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e964dec --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +target +app.json +app_nanos.json +app_nanosplus.json +app_nanox.json + +# Temporary directory with snapshots taken during test runs +tests/snapshots-tmp/ + +# Python +*.pyc[cod] +*.egg +__pycache__/ +*.egg-info/ +.eggs/ +.python-version + +# Related to the Ledger VSCode extension +# Virtual env for sideload (macOS and Windows) +ledger/ +# Build directory +build/ + +.idea/ \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..5343928 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,769 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bindgen" +version = "0.59.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "clap", + "env_logger", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "which", +] + +[[package]] +name = "bindgen" +version = "0.65.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.65", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cc" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-sys" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags 1.3.2", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "const-zero" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3c6565524986fe3225da0beb9b4aa55ebc73cd57ff8cb4ccf016ca4c8d006af" + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "critical-section" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" + +[[package]] +name = "cty" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" + +[[package]] +name = "either" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" + +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "flate2" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "gif" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "spin", + "stable_deref_trait", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "include_gif" +version = "1.1.0" +source = "git+https://github.com/LedgerHQ/ledger-device-rust-sdk.git?branch=feat/stax_nbgl_2#1d261c2f3a434f7b4d698b49498111974709bef7" +dependencies = [ + "flate2", + "gif", + "syn 1.0.109", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "ledger_device_sdk" +version = "1.9.0" +source = "git+https://github.com/LedgerHQ/ledger-device-rust-sdk.git?branch=feat/stax_nbgl_2#1d261c2f3a434f7b4d698b49498111974709bef7" +dependencies = [ + "const-zero", + "include_gif", + "ledger_secure_sdk_sys", + "num-traits", + "numtoa", + "rand_core", + "zeroize", +] + +[[package]] +name = "ledger_secure_sdk_sys" +version = "1.3.0" +source = "git+https://github.com/LedgerHQ/ledger-device-rust-sdk.git?branch=feat/stax_nbgl_2#1d261c2f3a434f7b4d698b49498111974709bef7" +dependencies = [ + "bindgen 0.65.1", + "cc", + "glob", +] + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libloading" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +dependencies = [ + "adler", +] + +[[package]] +name = "nanopass" +version = "1.2.1" +dependencies = [ + "bindgen 0.59.2", + "cc", + "cty", + "heapless", + "include_gif", + "ledger_device_sdk", + "ledger_secure_sdk_sys", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "numtoa" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aa2c4e539b869820a2b82e1aef6ff40aa85e65decdd5185e83fb4b1249cd00f" + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn 2.0.65", +] + +[[package]] +name = "proc-macro2" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-width" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/Cargo.toml b/Cargo.toml index ee23558..4bb6d01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,27 +3,43 @@ name = "nanopass" version = "1.2.1" authors = ["yhql", "Olivier Hériveaux"] edition = "2021" -build = "build.rs" [build-dependencies] cc = "1.0.73" bindgen = "0.59.2" [dependencies] -nanos_sdk = { git = "https://github.com/LedgerHQ/ledger-nanos-sdk.git" } -nanos_ui = { git = "https://github.com/LedgerHQ/ledger-nanos-ui.git" } +ledger_device_sdk = { git = "https://github.com/LedgerHQ/ledger-device-rust-sdk.git", branch = "feat/stax_nbgl_2" } +ledger_secure_sdk_sys = { git = "https://github.com/LedgerHQ/ledger-device-rust-sdk.git", branch = "feat/stax_nbgl_2" } +include_gif = { git = "https://github.com/LedgerHQ/ledger-device-rust-sdk.git", branch = "feat/stax_nbgl_2" } cty = "0.2.0" heapless = { version = "0.7.16", default-features = false } [profile.release] -opt-level = 's' +opt-level = 'z' lto = true -[package.metadata.nanos] -api_level = "1" -name = "NanoPass" +[features] +default = ["pending_review_screen"] +pending_review_screen = [] + +[package.metadata.ledger] curve = ["secp256k1"] flags = "0" -icon = "key_16x16.gif" -icon_small = "key_14x14.gif" -path = [""] +path = ["10016'"] +name = "NanoPass" + +[package.metadata.ledger.nanos] +icon = "crab.gif" + +[package.metadata.ledger.nanox] +icon = "key_14x14.gif" + +[package.metadata.ledger.nanosplus] +icon = "key_14x14.gif" + +[package.metadata.ledger.stax] +icon = "crab_32x32.gif" + +[package.metadata.ledger.flex] +icon = "crab_40x40.gif" diff --git a/app_stax.json b/app_stax.json new file mode 100644 index 0000000..e32aaa8 --- /dev/null +++ b/app_stax.json @@ -0,0 +1,18 @@ +{ + "apiLevel": "15", + "binary": "target/stax/release/app.hex", + "dataSize": 8192, + "derivationPath": { + "curves": [ + "secp256k1" + ], + "paths": [ + "44'/1'" + ] + }, + "flags": "0x200", + "icon": "crab_32x32.gif", + "name": "Rust Boilerplate", + "targetId": "0x33200004", + "version": "1.2.2" +} \ No newline at end of file diff --git a/build.rs b/build.rs index 7e100f6..62a2ce3 100644 --- a/build.rs +++ b/build.rs @@ -19,6 +19,9 @@ use std::path::PathBuf; use std::process::Command; fn main() { + + println!("cargo:rerun-if-changed=src/aes.c"); + let output = Command::new("arm-none-eabi-gcc") .arg("-print-sysroot") .output() diff --git a/crab.gif b/crab.gif new file mode 100644 index 0000000000000000000000000000000000000000..1e53099d4dec0b9dd5d12fc33af05757ac10c8a1 GIT binary patch literal 861 zcmZ?wbhEHb6krfw_|551D!5#Mbl6(3bk&DLOM HVz34PHNhSv literal 0 HcmV?d00001 diff --git a/crab_14x14.gif b/crab_14x14.gif new file mode 100644 index 0000000000000000000000000000000000000000..7de10f3c2534f6bdbe75ee4525d59a4938ac9372 GIT binary patch literal 854 zcmZ?wbhEHby0pvGLJ?E_CX>@2HM@dak04x9i001BWAOHXa{xHZ% ztGzhu&Ab2LH4LGq9KnGeN;wrrAqdA@3x!dDOQFmLK@d1p6c(PLP-svN0RY2hU|cqV z*5TDh5&^J8r*a1ERtmYSmN{d$(G>-B4Gz-nE2LueLT2xq9K(Pd(0sy}W zy?zS^O#qXK2oF(|xM0GkG>!rf)vQ*Uj|q}93IlBm(g?=FnQaD8D%vD60LaM(gyd*2 zKq@!_3PE}S76?F6VAVPt0c0?lHDX6GGbsSp_ f7#PfSieh0UzyJ_6>_V}Jk&Bx+bL!lQAp!t9bl--O literal 0 HcmV?d00001 diff --git a/crab_40x40.gif b/crab_40x40.gif new file mode 100644 index 0000000000000000000000000000000000000000..5f74e55034e01e337dda5edf056e06aa77e64ec5 GIT binary patch literal 479 zcmV<50U-WINk%w1VJHA70OJ7wcXxOH|Nn@Hi0M)j$}3rWj7eoaYzQ5 zQVMzqOCbowBD@2|EC4Vlc7~mD=L3)i2Zb(~6jYJdg5n_a43^fgp-NQ(0D`V&PzZd> zSE^i`M77sor1GlYF9B*6D|CBhdWK6e7;FV&VpegGiD7yL1%UwvXOVtzWRF(>FBM;X zaFmjuegc4kj+|tid2)&q3TUfyV5gLZ24_NUMX0D;3=e2Y0168P2&;cxN0WLe00a+8 z1QG-Ud6dBc3!lEM0SMh;1X10%he!)Cu)@fussw|L(BmNxE?TgIHsH#2K-0@cpx!NfLp&5 zAP{9m%P(9M&xAR1pa-h~1Pt83F~P?Vq>TjbxFgRLj{*qZIH8aP$fX;c+LV&?3q_4{ zBrTMs0JfKtfJ&$e8$}91IB{6eIMlHtSse;GOxztXES>}uC(I&IV+jP`Wfux08{$TR Vfvlu5Vxr8sv**vCLu-ft06TJyyKMjf literal 0 HcmV?d00001 diff --git a/crab_64x64.gif b/crab_64x64.gif new file mode 100644 index 0000000000000000000000000000000000000000..7271f2e3bf9e28be78dd2902d3aa36cdb6a9cf10 GIT binary patch literal 721 zcmV;?0xtbWNk%w1VL$*t0OJ4v|NsA)nVE=)h^nfpcXxLH00320Rc2;p?(XipySv@p z-OS9)5fKq0A|f+0Geks0A^s6Va%Ew3Wn>_CX>@2HM@dak04x9i002M$KmY&){t(DX ztGzhu&Ab0#D2`-lo@lDBZ0o*oEYEap-*}BE6Eg6UT~4zYEF{GN0^l1ADT>4rFsLpR z)qyk%TqJUdssfuWo26kTZAvw}-(?{4g-s}$@O*iJD1KdJdTs#$W`u==e=UK7g=K_~ zhgFA)D1CQmk0tE)@iMI^| zpzHhHyBV;!$_ZGapg@5GodobG*gzn;hY$k@aMB^5!~g^c8g%e5z=4JS1_?kMd6C>T z49XI!+~-jt0fPnhJvgx7fI)f^N1p3=vcdp|5lND>39~@MhX#rs)XDT=EeZ*Cmdx2y z0E40iAijjjplVe$6&3&$+LVBUn>qQX6AmsQJo^-hys%tETaYxMuOl% zUI}QqE3&*uiH8GBfhFOmzYCU0sYl|tP=aq`f>btjxIckP^U-rgkvkGCfq){g_%~k% z3i$V*GkthR*)#3T0fhkmJ!QcH4!&n0Z60cHi-t@%D1a0fB65dZ)? D)WkZY literal 0 HcmV?d00001 diff --git a/ledger_app.toml b/ledger_app.toml index e7a3127..5266444 100644 --- a/ledger_app.toml +++ b/ledger_app.toml @@ -1,2 +1,7 @@ -[rust-app] -manifest-path = "./Cargo.toml" +[app] +build_directory = "./" +sdk = "Rust" +devices = ["nanos", "nanox", "nanos+", "stax", "flex"] + +[tests] +pytest_directory = "./tests/" diff --git a/src/c/aes.c b/src/c/aes.c index eaf2b69..7bca6d2 100644 --- a/src/c/aes.c +++ b/src/c/aes.c @@ -19,10 +19,10 @@ ECB-AES128 2b7e151628aed2a6abf7158809cf4f3c resulting cipher - 3ad77bb40d7a3660a89ecaf32466ef97 - f5d3d58503b9699de785895a96fdbaaf - 43b1cd7f598ece23881b00e3ed030688 - 7b0c785e27e8ad3f8223207104725dd4 + 3ad77bb40d7a3660a89ecaf32466ef97 + f5d3d58503b9699de785895a96fdbaaf + 43b1cd7f598ece23881b00e3ed030688 + 7b0c785e27e8ad3f8223207104725dd4 NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0) @@ -55,7 +55,7 @@ NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0) #define Nr 10 // The number of rounds in AES Cipher. #endif -// jcallan@github points out that declaring Multiply as a function +// jcallan@github points out that declaring Multiply as a function // reduces code size considerably with the Keil ARM compiler. // See this link for more information: https://github.com/kokke/tiny-AES-C/pull/3 #ifndef MULTIPLY_AS_A_FUNCTION @@ -74,7 +74,7 @@ typedef uint8_t state_t[4][4]; // The lookup-tables are marked const so they can be placed in read-only storage instead of RAM -// The numbers below can be computed dynamically trading ROM for RAM - +// The numbers below can be computed dynamically trading ROM for RAM - // This can be useful in (embedded) bootloader applications, where ROM is often limited. static const uint8_t sbox[256] = { //0 1 2 3 4 5 6 7 8 9 A B C D E F @@ -113,7 +113,7 @@ static const uint8_t rsbox[256] = { 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d }; -// The round constant word array, Rcon[i], contains the values given by +// The round constant word array, Rcon[i], contains the values given by // x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8) static const uint8_t Rcon[11] = { 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 }; @@ -123,8 +123,8 @@ static const uint8_t Rcon[11] = { * that you can remove most of the elements in the Rcon array, because they are unused. * * From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon - * - * "Only the first some of these constants are actually used – up to rcon[10] for AES-128 (as 11 round keys are needed), + * + * "Only the first some of these constants are actually used – up to rcon[10] for AES-128 (as 11 round keys are needed), * up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm." */ @@ -147,12 +147,12 @@ static uint8_t getSBoxInvert(uint8_t num) */ #define getSBoxInvert(num) (rsbox[(num)]) -// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states. +// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states. static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key) { unsigned i, j, k; uint8_t tempa[4]; // Used for the column/row operations - + // The first round key is the key itself. for (i = 0; i < Nk; ++i) { @@ -188,7 +188,7 @@ static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key) tempa[3] = u8tmp; } - // SubWord() is a function that takes a four-byte input word and + // SubWord() is a function that takes a four-byte input word and // applies the S-box to each of the four bytes to produce an output word. // Function Subword() @@ -272,14 +272,14 @@ static void ShiftRows(state_t* state) { uint8_t temp; - // Rotate first row 1 columns to left + // Rotate first row 1 columns to left temp = (*state)[0][1]; (*state)[0][1] = (*state)[1][1]; (*state)[1][1] = (*state)[2][1]; (*state)[2][1] = (*state)[3][1]; (*state)[3][1] = temp; - // Rotate second row 2 columns to left + // Rotate second row 2 columns to left temp = (*state)[0][2]; (*state)[0][2] = (*state)[2][2]; (*state)[2][2] = temp; @@ -307,7 +307,7 @@ static void MixColumns(state_t* state) uint8_t i; uint8_t Tmp, Tm, t; for (i = 0; i < 4; ++i) - { + { t = (*state)[i][0]; Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ; Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ; @@ -349,7 +349,7 @@ static void InvMixColumns(state_t* state) int i; uint8_t a, b, c, d; for (i = 0; i < 4; ++i) - { + { a = (*state)[i][0]; b = (*state)[i][1]; c = (*state)[i][2]; @@ -381,14 +381,14 @@ static void InvShiftRows(state_t* state) { uint8_t temp; - // Rotate first row 1 columns to right + // Rotate first row 1 columns to right temp = (*state)[3][1]; (*state)[3][1] = (*state)[2][1]; (*state)[2][1] = (*state)[1][1]; (*state)[1][1] = (*state)[0][1]; (*state)[0][1] = temp; - // Rotate second row 2 columns to right + // Rotate second row 2 columns to right temp = (*state)[0][2]; (*state)[0][2] = (*state)[2][2]; (*state)[2][2] = temp; @@ -535,14 +535,14 @@ void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length) void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length) { uint8_t buffer[AES_BLOCKLEN]; - + unsigned i; int bi; for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi) { if (bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */ { - + memcpy(buffer, ctx->Iv, AES_BLOCKLEN); Cipher((state_t*)buffer,ctx->RoundKey); @@ -554,9 +554,9 @@ void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length) { ctx->Iv[bi] = 0; continue; - } + } ctx->Iv[bi] += 1; - break; + break; } bi = 0; } diff --git a/src/c/aes.h b/src/c/aes.h index 31335ba..a4f99a4 100644 --- a/src/c/aes.h +++ b/src/c/aes.h @@ -55,8 +55,8 @@ void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv); #endif #if defined(ECB) && (ECB == 1) -// buffer size is exactly AES_BLOCKLEN bytes; -// you need only AES_init_ctx as IV is not used in ECB +// buffer size is exactly AES_BLOCKLEN bytes; +// you need only AES_init_ctx as IV is not used in ECB // NB: ECB is considered insecure for most uses void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf); void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf); @@ -68,7 +68,7 @@ void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf); // buffer size MUST be mutile of AES_BLOCKLEN; // Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme // NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv() -// no IV should ever be reused with the same key +// no IV should ever be reused with the same key void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length); void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length); @@ -77,11 +77,11 @@ void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length); #if defined(CTR) && (CTR == 1) -// Same function for encrypting as for decrypting. +// Same function for encrypting as for decrypting. // IV is incremented for every block, and used after encryption as XOR-compliment for output // Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme // NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv() -// no IV should ever be reused with the same key +// no IV should ever be reused with the same key void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length); #endif // #if defined(CTR) && (CTR == 1) diff --git a/src/main.rs b/src/main.rs index 5bc111c..3773f86 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -// Copyright 2020 Ledger SAS +// Copyright 2024 Ledger SAS // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,25 +15,30 @@ #![no_std] #![no_main] -use nanos_sdk::buttons::ButtonEvent; -use nanos_sdk::ecc; -use nanos_sdk::io; -use nanos_sdk::io::ApduHeader; -use nanos_sdk::io::{Reply, StatusWords}; -use nanos_sdk::nvm; -use nanos_sdk::random; -use nanos_sdk::NVMData; -use nanos_ui::bagls; -use nanos_ui::layout::Draw; -use nanos_ui::ui; + +use ledger_secure_sdk_sys::buttons::ButtonEvent; +use ledger_device_sdk::io; +use ledger_device_sdk::io::{ApduHeader, Reply, StatusWords}; +use ledger_device_sdk::{ecc, nvm, NVMData}; +use ledger_device_sdk::io::Event::{Command}; +use ledger_device_sdk::random::{rand_bytes, Random}; +use ledger_device_sdk::ui::{bagls, SCREEN_HEIGHT}; mod password; use heapless::Vec; use password::{ArrayString, PasswordItem}; mod tinyaes; -use core::convert::TryFrom; use core::mem::MaybeUninit; -nanos_sdk::set_panic!(nanos_sdk::exiting_panic); +#[cfg(feature = "pending_review_screen")] +#[cfg(not(any(target_os = "stax", target_os = "flex")))] +use ledger_device_sdk::ui::gadgets::display_pending_review; +#[cfg(not(any(target_os = "stax", target_os = "flex")))] +use ledger_device_sdk::ui::gadgets::{Menu, MessageValidator, popup, SingleMessage}; +#[cfg(not(any(target_os = "stax", target_os = "flex")))] +use ledger_device_sdk::ui::layout::Draw; + +#[cfg(not(any(target_os = "stax", target_os = "flex")))] +ledger_device_sdk::set_panic!(ledger_device_sdk::exiting_panic); /// Stores all passwords in Non-Volatile Memory #[link_section = ".nvm_data"] @@ -50,25 +55,25 @@ static BIP32_PATH: [u32; 2] = ecc::make_bip32_path(b"m/10016'/0"); const NAME: &str = env!("CARGO_PKG_NAME"); const VERSION: &str = env!("CARGO_PKG_VERSION"); -enum Error { - NoConsent, - StorageFull, - EntryNotFound, - DecryptFailed, + +#[repr(u16)] +pub enum Error { + NoConsent = 0x69f0_u16, + StorageFull = 0x9210_u16, + EntryNotFound = 0x6a88_u16, + DecryptFailed = 0x9d60_u16, + InsNotSupported } -impl Into for Error { - fn into(self) -> Reply { - match self { - Error::NoConsent => Reply(0x69f0_u16), - Error::StorageFull => Reply(0x9210_u16), - Error::EntryNotFound => Reply(0x6a88_u16), - Error::DecryptFailed => Reply(0x9d60_u16), - } + +impl From for Reply { + fn from(sw: Error) -> Reply { + Reply(sw as u16) } } -enum Instruction { +/// Possible input commands received through APDUs. +pub enum Instruction { GetVersion, GetSize, Add, @@ -86,9 +91,9 @@ enum Instruction { } impl TryFrom for Instruction { - type Error = (); + type Error = Error; - fn try_from(v: ApduHeader) -> Result { + fn try_from(v: ApduHeader) -> Result { match v.ins { 0x01 => Ok(Self::GetVersion), 0x02 => Ok(Self::GetSize), @@ -104,7 +109,7 @@ impl TryFrom for Instruction { 0x0c => Ok(Self::Quit), 0x0d => Ok(Self::ShowOnScreen), 0x0e => Ok(Self::HasName), - _ => Err(()), + _ => Err(Self::Error::InsNotSupported), } } } @@ -138,7 +143,16 @@ impl Lfsr { #[no_mangle] extern "C" fn sample_main() { - let mut comm = io::Comm::new(); + // Create the communication manager, and configure it to accept only APDU from the 0xe0 class. + // If any APDU with a wrong class value is received, comm will respond automatically with + // BadCla status word. + let mut comm = io::Comm::new().set_expected_cla(0xe0); + + // Developer mode / pending review popup + // must be cleared with user interaction + #[cfg(feature = "pending_review_screen")] + #[cfg(not(any(target_os = "stax", target_os = "flex")))] + display_pending_review(&mut comm); // Don't use PASSWORDS directly in the program. It is static and using // it requires using unsafe everytime. Instead, take a reference here, so @@ -148,7 +162,7 @@ extern "C" fn sample_main() { // Encryption/decryption key for import and export. let mut enc_key = [0u8; 32]; - let _ = ecc::bip32_derive(ecc::CurvesId::Secp256k1, &BIP32_PATH, &mut enc_key); + let _ = ecc::bip32_derive(ecc::CurvesId::Secp256k1, &BIP32_PATH, &mut enc_key, None); // iteration counter let mut c = 0; @@ -156,20 +170,20 @@ extern "C" fn sample_main() { let mut lfsr = Lfsr::new(u8::random() & 0x3f, 0x30); loop { match comm.next_event() { - io::Event::Button(ButtonEvent::BothButtonsRelease) => nanos_sdk::exit_app(0), + io::Event::Button(ButtonEvent::BothButtonsRelease) => ledger_secure_sdk_sys::exit_app(0), io::Event::Button(ButtonEvent::RightButtonRelease) => { display_infos(passwords); c = 0; } io::Event::Ticker => { - let y_offset = ((nanos_ui::SCREEN_HEIGHT as i32) / 2) - 16; + let y_offset = ((SCREEN_HEIGHT as i32) / 2) - 16; if c == 0 { bagls::RectFull::new() .pos(0, y_offset) .width(8) .height(8) .erase(); - ui::SingleMessage::new("NanoPass").show(); + SingleMessage::new("NanoPass").show(); lfsr = Lfsr::new(u8::random() & 0x3f, 0x30); } else if c == 128 { bagls::RectFull::new() @@ -256,7 +270,7 @@ extern "C" fn sample_main() { match passwords.into_iter().find(|&&x| x.name == name) { Some(&p) => { - if ui::MessageValidator::new( + if MessageValidator::new( &[name.as_str()], &[&"Read", &"password"], &[&"Cancel"], @@ -285,23 +299,23 @@ extern "C" fn sample_main() { match passwords.into_iter().find(|&&x| x.name == name) { Some(&p) => { - if ui::MessageValidator::new( + if MessageValidator::new( &[name.as_str()], &[&"Read", &"password"], &[&"Cancel"], ) - .ask() + .ask() { - ui::popup(p.login.as_str()); - ui::popup(p.pass.as_str()); + popup(p.login.as_str()); + popup(p.pass.as_str()); comm.reply_ok(); } else { - ui::popup("Operation cancelled"); + popup("Operation cancelled"); comm.reply(Error::NoConsent); } } None => { - ui::popup("Password not found"); + popup("Password not found"); comm.reply(Error::EntryNotFound); } } @@ -313,7 +327,7 @@ extern "C" fn sample_main() { let name = ArrayString::<32>::from_bytes(comm.get(5, 5 + 32)); match passwords.into_iter().position(|x| x.name == name) { Some(p) => { - if ui::MessageValidator::new( + if MessageValidator::new( &[name.as_str()], &[&"Remove", &"password"], &[&"Cancel"], @@ -358,10 +372,10 @@ extern "C" fn sample_main() { io::Event::Command(Instruction::Clear) => { // Remove all passwords comm.reply::( - if ui::MessageValidator::new(&[], &[&"Remove all", &"passwords"], &[&"Cancel"]) + if MessageValidator::new(&[], &[&"Remove all", &"passwords"], &[&"Cancel"]) .ask() { - if ui::MessageValidator::new(&[], &[&"Are you", &"sure?"], &[&"Cancel"]) + if MessageValidator::new(&[], &[&"Are you", &"sure?"], &[&"Cancel"]) .ask() { passwords.clear(); @@ -378,7 +392,7 @@ extern "C" fn sample_main() { // Exit io::Event::Command(Instruction::Quit) => { comm.reply_ok(); - nanos_sdk::exit_app(0); + ledger_secure_sdk_sys::exit_app(0); } // HasName io::Event::Command(Instruction::HasName) => { @@ -428,7 +442,7 @@ fn display_infos(passwords: &nvm::Collection) { const APP_VERSION_STR: &str = concat!(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); - ui::Menu::new(&[APP_VERSION_STR, stored_str]).show(); + Menu::new(&[APP_VERSION_STR, stored_str]).show(); } /// Generates a random password. @@ -438,7 +452,6 @@ fn display_infos(passwords: &nvm::Collection) { /// * `dest` - An array where the result is stored. Must be at least /// `size` long. No terminal zero is written. /// * `size` - The size of the password to be generated -use random::Random; fn generate_random_password(dest: &mut [u8], size: usize) { for item in dest.iter_mut().take(size) { let rand_index = u32::random_from_range(0..PASS_CHARS.len() as u32); @@ -477,7 +490,7 @@ fn set_password( return match passwords.into_iter().position(|x| x.name == *name) { Some(index) => { // A password with this name already exists. - if !ui::MessageValidator::new(&[name.as_str()], &[&"Update", &"password"], &[&"Cancel"]) + if !MessageValidator::new(&[name.as_str()], &[&"Update", &"password"], &[&"Cancel"]) .ask() { return Err(Error::NoConsent); @@ -491,7 +504,7 @@ fn set_password( } None => { // Ask user confirmation - if !ui::MessageValidator::new(&[name.as_str()], &[&"Create", &"password"], &[&"Cancel"]) + if !MessageValidator::new(&[name.as_str()], &[&"Create", &"password"], &[&"Cancel"]) .ask() { return Err(Error::NoConsent); @@ -515,7 +528,7 @@ fn export( enc_key: Option<&[u8; 32]>, ) { // Ask user confirmation - if !ui::MessageValidator::new(&[], &[&"Export", &"passwords"], &[&"Cancel"]).ask() { + if !MessageValidator::new(&[], &[&"Export", &"passwords"], &[&"Cancel"]).ask() { comm.reply(Error::NoConsent); return; } @@ -523,7 +536,7 @@ fn export( // If export is in plaintext, add a warning let encrypted = enc_key.is_some(); if !encrypted - && !ui::MessageValidator::new(&[&"Export is plaintext!"], &[&"Confirm"], &[&"Cancel"]).ask() + && !MessageValidator::new(&[&"Export is plaintext!"], &[&"Confirm"], &[&"Cancel"]).ask() { comm.reply(Error::NoConsent); return; @@ -536,7 +549,7 @@ fn export( // We are now waiting for N APDUs to retrieve all passwords. // If encryption is enabled, the IV is returned during the first iteration. - ui::SingleMessage::new("Exporting...").show(); + SingleMessage::new("Exporting...").show(); let mut iter = passwords.into_iter(); let mut next_item = iter.next(); @@ -548,7 +561,7 @@ fn export( // If encryption is enabled, encrypt the buffer inplace. if encrypted { let mut nonce = [0u8; 16]; - random::rand_bytes(&mut nonce); + rand_bytes(&mut nonce); comm.append(&nonce); let mut buffer: Vec = Vec::new(); buffer.extend_from_slice(password.name.bytes()).unwrap(); @@ -618,14 +631,14 @@ fn import( count_bytes.copy_from_slice(comm.get(5, 5 + 4)); let mut count = u32::from_be_bytes(count_bytes); // Ask user confirmation - if !ui::MessageValidator::new(&[], &[&"Import", &"passwords"], &[&"Cancel"]).ask() { + if !MessageValidator::new(&[], &[&"Import", &"passwords"], &[&"Cancel"]).ask() { comm.reply(Error::NoConsent); return; } else { comm.reply_ok(); } // Wait for all items - ui::SingleMessage::new("Importing...").show(); + SingleMessage::new("Importing...").show(); while count > 0 { match comm.next_command() { // Fetch next password From 41a1ee7b86c191417d101db0aa21252a24769e25 Mon Sep 17 00:00:00 2001 From: vbouzon Date: Fri, 24 May 2024 09:08:21 +0200 Subject: [PATCH 2/6] Split UI & ADPU interactions --- .cargo/config.toml | 2 +- app_stax.json | 8 +- src/main.rs | 555 ++++++++++++++++++++++++++------------------- 3 files changed, 328 insertions(+), 237 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index c233973..e1bde7d 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -2,7 +2,7 @@ runner = "speculos -a=1 --model=nanosp" [build] -target = "nanosplus" +target = "stax" [unstable] build-std = ["core"] diff --git a/app_stax.json b/app_stax.json index e32aaa8..199d7da 100644 --- a/app_stax.json +++ b/app_stax.json @@ -1,18 +1,18 @@ { "apiLevel": "15", "binary": "target/stax/release/app.hex", - "dataSize": 8192, + "dataSize": 24064, "derivationPath": { "curves": [ "secp256k1" ], "paths": [ - "44'/1'" + "10016'" ] }, "flags": "0x200", "icon": "crab_32x32.gif", - "name": "Rust Boilerplate", + "name": "NanoPass", "targetId": "0x33200004", - "version": "1.2.2" + "version": "1.2.1" } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 3773f86..dd9e71c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,12 +22,18 @@ use ledger_device_sdk::io::{ApduHeader, Reply, StatusWords}; use ledger_device_sdk::{ecc, nvm, NVMData}; use ledger_device_sdk::io::Event::{Command}; use ledger_device_sdk::random::{rand_bytes, Random}; +#[cfg(not(any(target_os = "stax", target_os = "flex")))] use ledger_device_sdk::ui::{bagls, SCREEN_HEIGHT}; mod password; use heapless::Vec; use password::{ArrayString, PasswordItem}; mod tinyaes; use core::mem::MaybeUninit; +use include_gif::include_gif; +#[cfg(any(target_os = "stax", target_os = "flex"))] +use ledger_device_sdk::nbgl::{NbglGlyph, NbglHomeAndSettings}; +#[cfg(not(any(target_os = "stax", target_os = "flex")))] +use ledger_device_sdk::ui::bitmaps::{CERTIFICATE, DASHBOARD_X, Glyph}; #[cfg(feature = "pending_review_screen")] #[cfg(not(any(target_os = "stax", target_os = "flex")))] @@ -35,6 +41,8 @@ use ledger_device_sdk::ui::gadgets::display_pending_review; #[cfg(not(any(target_os = "stax", target_os = "flex")))] use ledger_device_sdk::ui::gadgets::{Menu, MessageValidator, popup, SingleMessage}; #[cfg(not(any(target_os = "stax", target_os = "flex")))] +use ledger_device_sdk::ui::gadgets::{EventOrPageIndex, MultiPageMenu, Page}; +#[cfg(not(any(target_os = "stax", target_os = "flex")))] use ledger_device_sdk::ui::layout::Draw; #[cfg(not(any(target_os = "stax", target_os = "flex")))] @@ -93,7 +101,7 @@ pub enum Instruction { impl TryFrom for Instruction { type Error = Error; - fn try_from(v: ApduHeader) -> Result { + fn try_from(v: ApduHeader) -> Result { match v.ins { 0x01 => Ok(Self::GetVersion), 0x02 => Ok(Self::GetSize), @@ -109,7 +117,7 @@ impl TryFrom for Instruction { 0x0c => Ok(Self::Quit), 0x0d => Ok(Self::ShowOnScreen), 0x0e => Ok(Self::HasName), - _ => Err(Self::Error::InsNotSupported), + _ => Err(Error::InsNotSupported), } } } @@ -146,7 +154,7 @@ extern "C" fn sample_main() { // Create the communication manager, and configure it to accept only APDU from the 0xe0 class. // If any APDU with a wrong class value is received, comm will respond automatically with // BadCla status word. - let mut comm = io::Comm::new().set_expected_cla(0xe0); + let mut comm = io::Comm::new().set_expected_cla(0x80); // Developer mode / pending review popup // must be cleared with user interaction @@ -169,248 +177,277 @@ extern "C" fn sample_main() { // lfsr with period 16*4 - 1 (63), all pixels divided in 8 boxes let mut lfsr = Lfsr::new(u8::random() & 0x3f, 0x30); loop { - match comm.next_event() { - io::Event::Button(ButtonEvent::BothButtonsRelease) => ledger_secure_sdk_sys::exit_app(0), - io::Event::Button(ButtonEvent::RightButtonRelease) => { - display_infos(passwords); - c = 0; - } - io::Event::Ticker => { - let y_offset = ((SCREEN_HEIGHT as i32) / 2) - 16; - if c == 0 { - bagls::RectFull::new() - .pos(0, y_offset) - .width(8) - .height(8) - .erase(); - SingleMessage::new("NanoPass").show(); - lfsr = Lfsr::new(u8::random() & 0x3f, 0x30); - } else if c == 128 { - bagls::RectFull::new() - .pos(1, y_offset + 1) - .width(7) - .height(7) - .display(); - } else if c >= 64 { - let pos = lfsr.next(); - let (x, y) = ((pos & 15) * 8, (pos >> 4) * 8); - bagls::RectFull::new() - .pos(x.into(), (y_offset + y as i32).into()) - .width(8) - .height(8) - .erase(); - let rect = bagls::RectFull::new() - .pos((x + 1).into(), (y_offset + y as i32 + 1).into()) - .width(7) - .height(7); - if c > 128 { - rect.erase(); - } else { - rect.display(); - } + if let io::Event::Command(ins) = display_infos(passwords, &mut comm) { + match ins { + + // Get version string + // Should comply with other apps standard + Instruction::GetVersion => { + comm.append(&[1]); // Format + comm.append(&[NAME.len() as u8]); + comm.append(NAME.as_bytes()); + comm.append(&[VERSION.len() as u8]); + comm.append(VERSION.as_bytes()); + comm.append(&[0]); // No flags + comm.reply_ok(); } - c = (c + 1) % 192; - } - io::Event::Button(_) => {} - // Get version string - // Should comply with other apps standard - io::Event::Command(Instruction::GetVersion) => { - comm.append(&[1]); // Format - comm.append(&[NAME.len() as u8]); - comm.append(NAME.as_bytes()); - comm.append(&[VERSION.len() as u8]); - comm.append(VERSION.as_bytes()); - comm.append(&[0]); // No flags - comm.reply_ok(); - } - // Get number of stored passwords - io::Event::Command(Instruction::GetSize) => { - let len: [u8; 4] = passwords.len().to_be_bytes(); - comm.append(&len); - comm.reply_ok(); - } - // Add a password - // If P1 == 0, password is in the data - // If P1 == 1, password must be generated by the device - io::Event::Command(Instruction::Add) => { - let mut offset = 5; - let name = ArrayString::<32>::from_bytes(comm.get(offset, offset + 32)); - offset += 32; - let login = ArrayString::<32>::from_bytes(comm.get(offset, offset + 32)); - offset += 32; - let pass = match comm.get_apdu_metadata().p1 { - 0 => Some(ArrayString::<32>::from_bytes(comm.get(offset, offset + 32))), - _ => None, - }; - comm.reply::(match set_password(passwords, &name, &login, &pass) { - Ok(()) => StatusWords::Ok.into(), - Err(e) => e.into(), - }); - c = 0; - } - // Get password name - // This is used by the client to list the names of stored password - // Login is not returned. - io::Event::Command(Instruction::GetName) => { - let mut index_bytes = [0; 4]; - index_bytes.copy_from_slice(comm.get(5, 5 + 4)); - let index = u32::from_be_bytes(index_bytes); - match passwords.get(index as usize) { - Some(password) => { - comm.append(password.name.bytes()); - comm.reply_ok() - } - None => comm.reply(Error::EntryNotFound), + // Get number of stored passwords + (Instruction::GetSize) => { + let len: [u8; 4] = passwords.len().to_be_bytes(); + comm.append(&len); + comm.reply_ok(); } - } - // Get password by name - // Returns login and password data. - io::Event::Command(Instruction::GetByName) => { - let name = ArrayString::<32>::from_bytes(comm.get(5, 5 + 32)); - - match passwords.into_iter().find(|&&x| x.name == name) { - Some(&p) => { - if MessageValidator::new( - &[name.as_str()], - &[&"Read", &"password"], - &[&"Cancel"], - ) - .ask() - { - comm.append(p.login.bytes()); - comm.append(p.pass.bytes()); - comm.reply_ok(); - } else { - comm.reply(Error::NoConsent); + // Add a password + // If P1 == 0, password is in the data + // If P1 == 1, password must be generated by the device + (Instruction::Add) => { + let mut offset = 5; + let name = ArrayString::<32>::from_bytes(comm.get(offset, offset + 32)); + offset += 32; + let login = ArrayString::<32>::from_bytes(comm.get(offset, offset + 32)); + offset += 32; + let pass = match comm.get_apdu_metadata().p1 { + 0 => Some(ArrayString::<32>::from_bytes(comm.get(offset, offset + 32))), + _ => None, + }; + comm.reply::(match set_password(passwords, &name, &login, &pass) { + Ok(()) => StatusWords::Ok.into(), + Err(e) => e.into(), + }); + c = 0; + } + // Get password name + // This is used by the client to list the names of stored password + // Login is not returned. + Instruction::GetName => { + let mut index_bytes = [0; 4]; + index_bytes.copy_from_slice(comm.get(5, 5 + 4)); + let index = u32::from_be_bytes(index_bytes); + match passwords.get(index as usize) { + Some(password) => { + comm.append(password.name.bytes()); + comm.reply_ok() } + None => comm.reply(Error::EntryNotFound), } - None => { - // Password not found - comm.reply(Error::EntryNotFound); + } + // Get password by name + // Returns login and password data. + (Instruction::GetByName) => { + let name = ArrayString::<32>::from_bytes(comm.get(5, 5 + 32)); + + match passwords.into_iter().find(|&&x| x.name == name) { + Some(&p) => { + if validate( + &[name.as_str()], + &[&"Read", &"password"], + &[&"Cancel"], + ) + { + comm.append(p.login.bytes()); + comm.append(p.pass.bytes()); + comm.reply_ok(); + } else { + comm.reply(Error::NoConsent); + } + } + None => { + // Password not found + comm.reply(Error::EntryNotFound); + } } + c = 0; } - c = 0; - } - // Display a password on the screen only, without communicating it - // to the host. - io::Event::Command(Instruction::ShowOnScreen) => { - let name = ArrayString::<32>::from_bytes(comm.get(5, 5 + 32)); - - match passwords.into_iter().find(|&&x| x.name == name) { - Some(&p) => { - if MessageValidator::new( - &[name.as_str()], - &[&"Read", &"password"], - &[&"Cancel"], - ) - .ask() - { - popup(p.login.as_str()); - popup(p.pass.as_str()); - comm.reply_ok(); - } else { - popup("Operation cancelled"); - comm.reply(Error::NoConsent); + // Display a password on the screen only, without communicating it + // to the host. + (Instruction::ShowOnScreen) => { + let name = ArrayString::<32>::from_bytes(comm.get(5, 5 + 32)); + + match passwords.into_iter().find(|&&x| x.name == name) { + Some(&p) => { + if validate( + &[name.as_str()], + &[&"Read", &"password"], + &[&"Cancel"], + ) + { + popup(p.login.as_str()); + popup(p.pass.as_str()); + comm.reply_ok(); + } else { + popup("Operation cancelled"); + comm.reply(Error::NoConsent); + } + } + None => { + popup("Password not found"); + comm.reply(Error::EntryNotFound); } } - None => { - popup("Password not found"); - comm.reply(Error::EntryNotFound); - } + c = 0; } - c = 0; - } - // Delete password by name - io::Event::Command(Instruction::DeleteByName) => { - let name = ArrayString::<32>::from_bytes(comm.get(5, 5 + 32)); - match passwords.into_iter().position(|x| x.name == name) { - Some(p) => { - if MessageValidator::new( - &[name.as_str()], - &[&"Remove", &"password"], - &[&"Cancel"], - ) - .ask() - { - passwords.remove(p); - comm.reply_ok(); - } else { - comm.reply(Error::NoConsent); + // Delete password by name + (Instruction::DeleteByName) => { + let name = ArrayString::<32>::from_bytes(comm.get(5, 5 + 32)); + match passwords.into_iter().position(|x| x.name == name) { + Some(p) => { + if + validate( + &[name.as_str()], + &[&"Remove", &"password"], + &[&"Cancel"], + ) + { + passwords.remove(p); + comm.reply_ok(); + } else { + comm.reply(Error::NoConsent); + } + } + None => { + // Password not found + comm.reply(Error::EntryNotFound); } } - None => { - // Password not found - comm.reply(Error::EntryNotFound); - } + c = 0; } - c = 0; - } - // Export - // P1 can be 0 for plaintext, 1 for encrypted export. - io::Event::Command(Instruction::Export) => match comm.get_apdu_metadata().p1 { - 0 => export(&mut comm, &passwords, None), - 1 => export(&mut comm, &passwords, Some(&enc_key)), - _ => comm.reply(StatusWords::Unknown), - }, - // Reserved for export - io::Event::Command(Instruction::ExportNext) => { - comm.reply(StatusWords::Unknown); - } - // Import - // P1 can be 0 for plaintext, 1 for encrypted import. - io::Event::Command(Instruction::Import) => match comm.get_apdu_metadata().p1 { - 0 => import(&mut comm, &mut passwords, None), - 1 => import(&mut comm, &mut passwords, Some(&enc_key)), - _ => comm.reply(StatusWords::Unknown), - }, - // Reserved for import - io::Event::Command(Instruction::ImportNext) => { - comm.reply(StatusWords::Unknown); - } - io::Event::Command(Instruction::Clear) => { - // Remove all passwords - comm.reply::( - if MessageValidator::new(&[], &[&"Remove all", &"passwords"], &[&"Cancel"]) - .ask() - { - if MessageValidator::new(&[], &[&"Are you", &"sure?"], &[&"Cancel"]) - .ask() + // Export + // P1 can be 0 for plaintext, 1 for encrypted export. + (Instruction::Export) => match comm.get_apdu_metadata().p1 { + 0 => export(&mut comm, &passwords, None), + 1 => export(&mut comm, &passwords, Some(&enc_key)), + _ => comm.reply(StatusWords::Unknown), + }, + // Reserved for export + (Instruction::ExportNext) => { + comm.reply(StatusWords::Unknown); + } + // Import + // P1 can be 0 for plaintext, 1 for encrypted import. + (Instruction::Import) => match comm.get_apdu_metadata().p1 { + 0 => import(&mut comm, &mut passwords, None), + 1 => import(&mut comm, &mut passwords, Some(&enc_key)), + _ => comm.reply(StatusWords::Unknown), + }, + // Reserved for import + (Instruction::ImportNext) => { + comm.reply(StatusWords::Unknown); + } + (Instruction::Clear) => { + // Remove all passwords + comm.reply::( + if validate(&[], &[&"Remove all", &"passwords"], &[&"Cancel"]) { - passwords.clear(); - StatusWords::Ok.into() + if validate(&[], &[&"Are you", &"sure?"], &[&"Cancel"]) + { + passwords.clear(); + StatusWords::Ok.into() + } else { + Error::NoConsent.into() + } } else { Error::NoConsent.into() + }, + ); + c = 0; + } + // Exit + (Instruction::Quit) => { + comm.reply_ok(); + ledger_secure_sdk_sys::exit_app(0); + } + // HasName + (Instruction::HasName) => { + let name = ArrayString::<32>::from_bytes(comm.get(5, 5 + 32)); + match passwords.into_iter().find(|&&x| x.name == name) { + Some(_) => { + comm.append(&[1]); + } + None => { + comm.append(&[0]); } - } else { - Error::NoConsent.into() - }, - ); - c = 0; - } - // Exit - io::Event::Command(Instruction::Quit) => { - comm.reply_ok(); - ledger_secure_sdk_sys::exit_app(0); - } - // HasName - io::Event::Command(Instruction::HasName) => { - let name = ArrayString::<32>::from_bytes(comm.get(5, 5 + 32)); - match passwords.into_iter().find(|&&x| x.name == name) { - Some(_) => { - comm.append(&[1]); - } - None => { - comm.append(&[0]); } + comm.reply_ok(); } - comm.reply_ok(); } } } } + + +#[cfg(not(any(target_os = "stax", target_os = "flex")))] +fn validate( message:&[&str], + + confirm: &[&str], + + cancel: &[&str]) -> bool +{ + return MessageValidator::new( + message, + confirm, + cancel, + ) + .ask() +} + + +#[cfg(any(target_os = "stax", target_os = "flex"))] +fn validate( message:&[&str], + + confirm: &[&str], + + cancel: &[&str]) -> bool +{ + true +} + + +#[cfg(not(any(target_os = "stax", target_os = "flex")))] +fn display_screensaver(c: i32, lfsr: &mut Lfsr) { + let y_offset = ((SCREEN_HEIGHT as i32) / 2) - 16; + if c == 0 { + bagls::RectFull::new() + .pos(0, y_offset) + .width(8) + .height(8) + .erase(); + SingleMessage::new("NanoPass").show(); + *lfsr = Lfsr::new(u8::random() & 0x3f, 0x30); + } else if c == 128 { + bagls::RectFull::new() + .pos(1, y_offset + 1) + .width(7) + .height(7) + .display(); + } else if c >= 64 { + let pos = lfsr.next(); + let (x, y) = ((pos & 15) * 8, (pos >> 4) * 8); + bagls::RectFull::new() + .pos(x.into(), (y_offset + y as i32).into()) + .width(8) + .height(8) + .height(8) + .erase(); + let rect = bagls::RectFull::new() + .pos((x + 1).into(), (y_offset + y as i32 + 1).into()) + .width(7) + .height(7); + if c > 128 { + rect.erase(); + } else { + rect.display(); + } + } + +} + +#[cfg(any(target_os = "stax", target_os = "flex"))] +fn display_screensaver(c: i32, lfsr: &mut Lfsr) { +} + /// Conversion to a two-digit number fn int2dec(x: usize) -> [u8; 2] { let mut t = (x % 100) as u16; @@ -429,7 +466,9 @@ fn int2dec(x: usize) -> [u8; 2] { /// Display global information about the app: /// - Current number of passwords stored /// - App Version -fn display_infos(passwords: &nvm::Collection) { +/// +#[cfg(not(any(target_os = "stax", target_os = "flex")))] +fn display_infos(passwords: &nvm::Collection, comm: &mut io::Comm) -> io::Event { let mut stored_n = *b" passwords"; let pwlen_bytes = int2dec(passwords.len()); @@ -440,9 +479,48 @@ fn display_infos(passwords: &nvm::Collection) { // '0' and '9', thus is valid utf8 let stored_str = unsafe { core::str::from_utf8_unchecked(&stored_n) }; - const APP_VERSION_STR: &str = concat!(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); + const APP_ICON: Glyph = Glyph::from_include(include_gif!("crab.gif")); + let pages = [ + // The from trait allows to create different styles of pages + // without having to use the new() function. + &Page::from((["NanoPass", "is ready"], &APP_ICON)), + &Page::from((["Passwords", stored_str])), + &Page::from((["Version", env!("CARGO_PKG_VERSION")], true)), + &Page::from(("Quit", &DASHBOARD_X)), + ]; + loop { + match MultiPageMenu::new(comm, &pages).show() { + EventOrPageIndex::Event(e) => return e, + EventOrPageIndex::Index(3) => ledger_device_sdk::exit_app(0), + EventOrPageIndex::Index(_) => (), + } + } +} +#[cfg(any(target_os = "stax", target_os = "flex"))] +fn display_infos(passwords: &nvm::Collection, comm: &mut io::Comm) -> io::Event { + let mut stored_n = *b" passwords"; + let pwlen_bytes = int2dec(passwords.len()); + + stored_n[0] = pwlen_bytes[0]; + stored_n[1] = pwlen_bytes[1]; + + // safety: int2dec returns a [u8; 2] consisting of values between + // '0' and '9', thus is valid utf8 + let stored_str = unsafe { core::str::from_utf8_unchecked(&stored_n) }; - Menu::new(&[APP_VERSION_STR, stored_str]).show(); + const FERRIS: NbglGlyph = NbglGlyph::from_include(include_gif!("key_16x16.gif", NBGL)); + + + // Display the home screen. + NbglHomeAndSettings::new(comm) + .glyph(&FERRIS) + + .infos( + "NanoPass", + env!("CARGO_PKG_VERSION"), + env!("CARGO_PKG_AUTHORS"), + ) + .show() } /// Generates a random password. @@ -490,8 +568,7 @@ fn set_password( return match passwords.into_iter().position(|x| x.name == *name) { Some(index) => { // A password with this name already exists. - if !MessageValidator::new(&[name.as_str()], &[&"Update", &"password"], &[&"Cancel"]) - .ask() + if !validate(&[name.as_str()], &[&"Update", &"password"], &[&"Cancel"]) { return Err(Error::NoConsent); } @@ -504,8 +581,7 @@ fn set_password( } None => { // Ask user confirmation - if !MessageValidator::new(&[name.as_str()], &[&"Create", &"password"], &[&"Cancel"]) - .ask() + if !validate(&[name.as_str()], &[&"Create", &"password"], &[&"Cancel"]) { return Err(Error::NoConsent); } @@ -528,7 +604,7 @@ fn export( enc_key: Option<&[u8; 32]>, ) { // Ask user confirmation - if !MessageValidator::new(&[], &[&"Export", &"passwords"], &[&"Cancel"]).ask() { + if !validate(&[], &[&"Export", &"passwords"], &[&"Cancel"]) { comm.reply(Error::NoConsent); return; } @@ -536,7 +612,7 @@ fn export( // If export is in plaintext, add a warning let encrypted = enc_key.is_some(); if !encrypted - && !MessageValidator::new(&[&"Export is plaintext!"], &[&"Confirm"], &[&"Cancel"]).ask() + && !validate(&[&"Export is plaintext!"], &[&"Confirm"], &[&"Cancel"]) { comm.reply(Error::NoConsent); return; @@ -549,7 +625,7 @@ fn export( // We are now waiting for N APDUs to retrieve all passwords. // If encryption is enabled, the IV is returned during the first iteration. - SingleMessage::new("Exporting...").show(); + show_message("Exporting..."); let mut iter = passwords.into_iter(); let mut next_item = iter.next(); @@ -631,14 +707,14 @@ fn import( count_bytes.copy_from_slice(comm.get(5, 5 + 4)); let mut count = u32::from_be_bytes(count_bytes); // Ask user confirmation - if !MessageValidator::new(&[], &[&"Import", &"passwords"], &[&"Cancel"]).ask() { + if !validate(&[], &[&"Import", &"passwords"], &[&"Cancel"]) { comm.reply(Error::NoConsent); return; } else { comm.reply_ok(); } // Wait for all items - SingleMessage::new("Importing...").show(); + show_message("Importing..."); while count > 0 { match comm.next_command() { // Fetch next password @@ -717,4 +793,19 @@ fn import( } } } + } + +#[cfg(not(any(target_os = "stax", target_os = "flex")))] +fn show_message(msg: &str) { + SingleMessage::new(&msg).show() +} + + +#[cfg(any(target_os = "stax", target_os = "flex"))] +fn show_message(msg: &str) { +} + +#[cfg(any(target_os = "stax", target_os = "flex"))] +fn popup(msg: &str) { +} \ No newline at end of file From c578550d11b622aa9aede5bb27a0d5913705252f Mon Sep 17 00:00:00 2001 From: vbouzon Date: Fri, 24 May 2024 10:41:50 +0200 Subject: [PATCH 3/6] With total hackhish review --- app_stax.json | 2 +- src/main.rs | 22 ++++++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/app_stax.json b/app_stax.json index 199d7da..2d762a0 100644 --- a/app_stax.json +++ b/app_stax.json @@ -1,7 +1,7 @@ { "apiLevel": "15", "binary": "target/stax/release/app.hex", - "dataSize": 24064, + "dataSize": 24576, "derivationPath": { "curves": [ "secp256k1" diff --git a/src/main.rs b/src/main.rs index dd9e71c..340584d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,7 +17,7 @@ use ledger_secure_sdk_sys::buttons::ButtonEvent; -use ledger_device_sdk::io; +use ledger_device_sdk::{io, nbgl}; use ledger_device_sdk::io::{ApduHeader, Reply, StatusWords}; use ledger_device_sdk::{ecc, nvm, NVMData}; use ledger_device_sdk::io::Event::{Command}; @@ -32,6 +32,7 @@ use core::mem::MaybeUninit; use include_gif::include_gif; #[cfg(any(target_os = "stax", target_os = "flex"))] use ledger_device_sdk::nbgl::{NbglGlyph, NbglHomeAndSettings}; +use ledger_device_sdk::nbgl::{Field, NbglReview}; #[cfg(not(any(target_os = "stax", target_os = "flex")))] use ledger_device_sdk::ui::bitmaps::{CERTIFICATE, DASHBOARD_X, Glyph}; @@ -401,7 +402,24 @@ fn validate( message:&[&str], cancel: &[&str]) -> bool { - true + let my_fields = [ + Field { + name: "Amount", + value: "111 CRAB", + }, + Field { + name: "Destination", + value: "0x1234567890ABCDEF1234567890ABCDEF12345678", + }, + Field { + name: "Memo", + value: "This is a test transaction.", + }, + ]; + + NbglReview::<32,1024>::new() + .titles(confirm.first().unwrap_or(&""), "subtitle", message.first().unwrap_or(&"") ) + .show(&[]) } From c16ed90054a8fb9922d08acdc98c475d90490986 Mon Sep 17 00:00:00 2001 From: Marion LAFON Date: Fri, 30 Aug 2024 11:19:08 +0200 Subject: [PATCH 4/6] Compilation works bit useless app on stax --- .cargo/config.toml | 2 +- Cargo.lock | 234 +++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 6 +- src/main.rs | 39 ++++---- 4 files changed, 246 insertions(+), 35 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index e1bde7d..2a2b09e 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -5,5 +5,5 @@ runner = "speculos -a=1 --model=nanosp" target = "stax" [unstable] -build-std = ["core"] +build-std = ["core", "alloc"] build-std-features = ["compiler-builtins-mem"] diff --git a/Cargo.lock b/Cargo.lock index 5343928..caf9d06 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -98,6 +98,12 @@ dependencies = [ "which", ] +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + [[package]] name = "bitflags" version = "1.3.2" @@ -110,6 +116,12 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +[[package]] +name = "bytemuck" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773d90827bc3feecfb67fab12e24de0749aad83c74b9504ecde46237b5cd24e2" + [[package]] name = "byteorder" version = "1.5.0" @@ -190,6 +202,37 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "cty" version = "0.2.2" @@ -202,6 +245,16 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +[[package]] +name = "embedded-alloc" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddae17915accbac2cfbc64ea0ae6e3b330e6ea124ba108dada63646fd3c6f815" +dependencies = [ + "critical-section", + "linked_list_allocator", +] + [[package]] name = "env_logger" version = "0.9.3" @@ -225,6 +278,31 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "exr" +version = "1.72.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fdeflate" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +dependencies = [ + "simd-adler32", +] + [[package]] name = "flate2" version = "1.0.30" @@ -235,11 +313,20 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "spin", +] + [[package]] name = "gif" -version = "0.11.4" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" dependencies = [ "color_quant", "weezl", @@ -251,6 +338,16 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + [[package]] name = "hash32" version = "0.2.1" @@ -297,16 +394,44 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "image" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "jpeg-decoder", + "num-traits", + "png", + "qoi", + "tiff", +] + [[package]] name = "include_gif" -version = "1.1.0" -source = "git+https://github.com/LedgerHQ/ledger-device-rust-sdk.git?branch=feat/stax_nbgl_2#1d261c2f3a434f7b4d698b49498111974709bef7" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8511434ad4b61bf0be7c61707d0172b6ad091519da93bf95d66555f3f0d994ac" dependencies = [ "flate2", - "gif", + "image", "syn 1.0.109", ] +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" +dependencies = [ + "rayon", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -319,10 +444,17 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + [[package]] name = "ledger_device_sdk" -version = "1.9.0" -source = "git+https://github.com/LedgerHQ/ledger-device-rust-sdk.git?branch=feat/stax_nbgl_2#1d261c2f3a434f7b4d698b49498111974709bef7" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff9990e68e73870cac570e9c45f1de618419d5b4c2b3923b51d23428cd7b9c8" dependencies = [ "const-zero", "include_gif", @@ -335,11 +467,14 @@ dependencies = [ [[package]] name = "ledger_secure_sdk_sys" -version = "1.3.0" -source = "git+https://github.com/LedgerHQ/ledger-device-rust-sdk.git?branch=feat/stax_nbgl_2#1d261c2f3a434f7b4d698b49498111974709bef7" +version = "1.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bc18740897f4e3b28025e97c31210dd36a69fe1114d66fe9372938d6f3673b6" dependencies = [ "bindgen 0.65.1", "cc", + "critical-section", + "embedded-alloc", "glob", ] @@ -359,6 +494,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "linked_list_allocator" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286" + [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -400,6 +541,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" dependencies = [ "adler", + "simd-adler32", ] [[package]] @@ -452,6 +594,19 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +[[package]] +name = "png" +version = "0.17.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "prettyplease" version = "0.2.20" @@ -471,6 +626,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + [[package]] name = "quote" version = "1.0.36" @@ -486,6 +650,26 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "regex" version = "1.10.4" @@ -561,6 +745,18 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + [[package]] name = "spin" version = "0.9.8" @@ -622,6 +818,17 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + [[package]] name = "unicode-ident" version = "1.0.12" @@ -767,3 +974,12 @@ name = "zeroize" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] diff --git a/Cargo.toml b/Cargo.toml index 4bb6d01..a10382e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,9 +9,9 @@ cc = "1.0.73" bindgen = "0.59.2" [dependencies] -ledger_device_sdk = { git = "https://github.com/LedgerHQ/ledger-device-rust-sdk.git", branch = "feat/stax_nbgl_2" } -ledger_secure_sdk_sys = { git = "https://github.com/LedgerHQ/ledger-device-rust-sdk.git", branch = "feat/stax_nbgl_2" } -include_gif = { git = "https://github.com/LedgerHQ/ledger-device-rust-sdk.git", branch = "feat/stax_nbgl_2" } +ledger_device_sdk = { version="1.14.0" } +ledger_secure_sdk_sys = { version = "1.4.4" } +include_gif = "1.2.0" cty = "0.2.0" heapless = { version = "0.7.16", default-features = false } diff --git a/src/main.rs b/src/main.rs index 340584d..e6a9aa8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,12 +15,10 @@ #![no_std] #![no_main] - -use ledger_secure_sdk_sys::buttons::ButtonEvent; -use ledger_device_sdk::{io, nbgl}; +use ledger_secure_sdk_sys; +use ledger_device_sdk::{io}; use ledger_device_sdk::io::{ApduHeader, Reply, StatusWords}; use ledger_device_sdk::{ecc, nvm, NVMData}; -use ledger_device_sdk::io::Event::{Command}; use ledger_device_sdk::random::{rand_bytes, Random}; #[cfg(not(any(target_os = "stax", target_os = "flex")))] use ledger_device_sdk::ui::{bagls, SCREEN_HEIGHT}; @@ -46,7 +44,6 @@ use ledger_device_sdk::ui::gadgets::{EventOrPageIndex, MultiPageMenu, Page}; #[cfg(not(any(target_os = "stax", target_os = "flex")))] use ledger_device_sdk::ui::layout::Draw; -#[cfg(not(any(target_os = "stax", target_os = "flex")))] ledger_device_sdk::set_panic!(ledger_device_sdk::exiting_panic); /// Stores all passwords in Non-Volatile Memory @@ -155,7 +152,7 @@ extern "C" fn sample_main() { // Create the communication manager, and configure it to accept only APDU from the 0xe0 class. // If any APDU with a wrong class value is received, comm will respond automatically with // BadCla status word. - let mut comm = io::Comm::new().set_expected_cla(0x80); + let mut comm = io::Comm::new().set_expected_cla(0x0); // Developer mode / pending review popup // must be cleared with user interaction @@ -193,7 +190,7 @@ extern "C" fn sample_main() { comm.reply_ok(); } // Get number of stored passwords - (Instruction::GetSize) => { + Instruction::GetSize => { let len: [u8; 4] = passwords.len().to_be_bytes(); comm.append(&len); comm.reply_ok(); @@ -201,7 +198,7 @@ extern "C" fn sample_main() { // Add a password // If P1 == 0, password is in the data // If P1 == 1, password must be generated by the device - (Instruction::Add) => { + Instruction::Add => { let mut offset = 5; let name = ArrayString::<32>::from_bytes(comm.get(offset, offset + 32)); offset += 32; @@ -234,7 +231,7 @@ extern "C" fn sample_main() { } // Get password by name // Returns login and password data. - (Instruction::GetByName) => { + Instruction::GetByName => { let name = ArrayString::<32>::from_bytes(comm.get(5, 5 + 32)); match passwords.into_iter().find(|&&x| x.name == name) { @@ -262,7 +259,7 @@ extern "C" fn sample_main() { // Display a password on the screen only, without communicating it // to the host. - (Instruction::ShowOnScreen) => { + Instruction::ShowOnScreen => { let name = ArrayString::<32>::from_bytes(comm.get(5, 5 + 32)); match passwords.into_iter().find(|&&x| x.name == name) { @@ -290,7 +287,7 @@ extern "C" fn sample_main() { } // Delete password by name - (Instruction::DeleteByName) => { + Instruction::DeleteByName => { let name = ArrayString::<32>::from_bytes(comm.get(5, 5 + 32)); match passwords.into_iter().position(|x| x.name == name) { Some(p) => { @@ -316,27 +313,27 @@ extern "C" fn sample_main() { } // Export // P1 can be 0 for plaintext, 1 for encrypted export. - (Instruction::Export) => match comm.get_apdu_metadata().p1 { + Instruction::Export => match comm.get_apdu_metadata().p1 { 0 => export(&mut comm, &passwords, None), 1 => export(&mut comm, &passwords, Some(&enc_key)), _ => comm.reply(StatusWords::Unknown), }, // Reserved for export - (Instruction::ExportNext) => { + Instruction::ExportNext => { comm.reply(StatusWords::Unknown); } // Import // P1 can be 0 for plaintext, 1 for encrypted import. - (Instruction::Import) => match comm.get_apdu_metadata().p1 { + Instruction::Import => match comm.get_apdu_metadata().p1 { 0 => import(&mut comm, &mut passwords, None), 1 => import(&mut comm, &mut passwords, Some(&enc_key)), _ => comm.reply(StatusWords::Unknown), }, // Reserved for import - (Instruction::ImportNext) => { + Instruction::ImportNext => { comm.reply(StatusWords::Unknown); } - (Instruction::Clear) => { + Instruction::Clear => { // Remove all passwords comm.reply::( if validate(&[], &[&"Remove all", &"passwords"], &[&"Cancel"]) @@ -355,12 +352,12 @@ extern "C" fn sample_main() { c = 0; } // Exit - (Instruction::Quit) => { + Instruction::Quit => { comm.reply_ok(); ledger_secure_sdk_sys::exit_app(0); } // HasName - (Instruction::HasName) => { + Instruction::HasName => { let name = ArrayString::<32>::from_bytes(comm.get(5, 5 + 32)); match passwords.into_iter().find(|&&x| x.name == name) { Some(_) => { @@ -417,7 +414,7 @@ fn validate( message:&[&str], }, ]; - NbglReview::<32,1024>::new() + NbglReview::<>::new() .titles(confirm.first().unwrap_or(&""), "subtitle", message.first().unwrap_or(&"") ) .show(&[]) } @@ -528,11 +525,9 @@ fn display_infos(passwords: &nvm::Collection, comm: &mut io:: const FERRIS: NbglGlyph = NbglGlyph::from_include(include_gif!("key_16x16.gif", NBGL)); - // Display the home screen. - NbglHomeAndSettings::new(comm) + NbglHomeAndSettings::new() .glyph(&FERRIS) - .infos( "NanoPass", env!("CARGO_PKG_VERSION"), From 778283e661e728b91317a7ef081365e86b9c3a0e Mon Sep 17 00:00:00 2001 From: Marion LAFON Date: Fri, 30 Aug 2024 12:07:34 +0200 Subject: [PATCH 5/6] compilation work for nanosplus --- src/main.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index e6a9aa8..709311f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,6 +30,7 @@ use core::mem::MaybeUninit; use include_gif::include_gif; #[cfg(any(target_os = "stax", target_os = "flex"))] use ledger_device_sdk::nbgl::{NbglGlyph, NbglHomeAndSettings}; +#[cfg(any(target_os = "stax", target_os = "flex"))] use ledger_device_sdk::nbgl::{Field, NbglReview}; #[cfg(not(any(target_os = "stax", target_os = "flex")))] use ledger_device_sdk::ui::bitmaps::{CERTIFICATE, DASHBOARD_X, Glyph}; @@ -499,7 +500,7 @@ fn display_infos(passwords: &nvm::Collection, comm: &mut io:: // The from trait allows to create different styles of pages // without having to use the new() function. &Page::from((["NanoPass", "is ready"], &APP_ICON)), - &Page::from((["Passwords", stored_str])), + &Page::from((["Passwords", stored_str], true)), &Page::from((["Version", env!("CARGO_PKG_VERSION")], true)), &Page::from(("Quit", &DASHBOARD_X)), ]; From 355dc21ce05bf42726103494caca14903e03b875 Mon Sep 17 00:00:00 2001 From: Marion LAFON Date: Fri, 30 Aug 2024 17:26:10 +0200 Subject: [PATCH 6/6] Change NbglReview by NbglChoice --- src/main.rs | 165 +++++++++++++++++++++++++++++----------------------- 1 file changed, 91 insertions(+), 74 deletions(-) diff --git a/src/main.rs b/src/main.rs index 709311f..a6ea0b2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,7 +17,7 @@ use ledger_secure_sdk_sys; use ledger_device_sdk::{io}; -use ledger_device_sdk::io::{ApduHeader, Reply, StatusWords}; +use ledger_device_sdk::io::{ApduHeader, Reply, StatusWords, Event, Comm}; use ledger_device_sdk::{ecc, nvm, NVMData}; use ledger_device_sdk::random::{rand_bytes, Random}; #[cfg(not(any(target_os = "stax", target_os = "flex")))] @@ -31,7 +31,7 @@ use include_gif::include_gif; #[cfg(any(target_os = "stax", target_os = "flex"))] use ledger_device_sdk::nbgl::{NbglGlyph, NbglHomeAndSettings}; #[cfg(any(target_os = "stax", target_os = "flex"))] -use ledger_device_sdk::nbgl::{Field, NbglReview}; +use ledger_device_sdk::nbgl::{init_comm, NbglStatus, NbglChoice, NbglSpinner}; #[cfg(not(any(target_os = "stax", target_os = "flex")))] use ledger_device_sdk::ui::bitmaps::{CERTIFICATE, DASHBOARD_X, Glyph}; @@ -72,6 +72,10 @@ pub enum Error { InsNotSupported } +pub enum AppSW { + Deny = 0x6985, + Ok = 0x9000, +} impl From for Reply { fn from(sw: Error) -> Reply { @@ -153,7 +157,12 @@ extern "C" fn sample_main() { // Create the communication manager, and configure it to accept only APDU from the 0xe0 class. // If any APDU with a wrong class value is received, comm will respond automatically with // BadCla status word. - let mut comm = io::Comm::new().set_expected_cla(0x0); + let mut comm = Comm::new().set_expected_cla(0x80); + + // Initialize reference to Comm instance for NBGL + // API calls. + #[cfg(any(target_os = "stax", target_os = "flex"))] + init_comm(&mut comm); // Developer mode / pending review popup // must be cleared with user interaction @@ -172,13 +181,14 @@ extern "C" fn sample_main() { let _ = ecc::bip32_derive(ecc::CurvesId::Secp256k1, &BIP32_PATH, &mut enc_key, None); // iteration counter - let mut c = 0; // lfsr with period 16*4 - 1 (63), all pixels divided in 8 boxes let mut lfsr = Lfsr::new(u8::random() & 0x3f, 0x30); + let mut c: i32 = 0; loop { - if let io::Event::Command(ins) = display_infos(passwords, &mut comm) { + // Wait for either a specific button push to exit the app + // or an APDU command + if let Event::Command(ins) = display_infos(&mut comm) { match ins { - // Get version string // Should comply with other apps standard Instruction::GetVersion => { @@ -189,13 +199,13 @@ extern "C" fn sample_main() { comm.append(VERSION.as_bytes()); comm.append(&[0]); // No flags comm.reply_ok(); - } + }, // Get number of stored passwords Instruction::GetSize => { let len: [u8; 4] = passwords.len().to_be_bytes(); comm.append(&len); comm.reply_ok(); - } + }, // Add a password // If P1 == 0, password is in the data // If P1 == 1, password must be generated by the device @@ -214,11 +224,11 @@ extern "C" fn sample_main() { Err(e) => e.into(), }); c = 0; - } + }, // Get password name // This is used by the client to list the names of stored password // Login is not returned. - Instruction::GetName => { + Instruction::GetName => { let mut index_bytes = [0; 4]; index_bytes.copy_from_slice(comm.get(5, 5 + 4)); let index = u32::from_be_bytes(index_bytes); @@ -229,7 +239,7 @@ extern "C" fn sample_main() { } None => comm.reply(Error::EntryNotFound), } - } + }, // Get password by name // Returns login and password data. Instruction::GetByName => { @@ -238,16 +248,19 @@ extern "C" fn sample_main() { match passwords.into_iter().find(|&&x| x.name == name) { Some(&p) => { if validate( + &[&"Get password"], &[name.as_str()], - &[&"Read", &"password"], + &[&"Read password"], &[&"Cancel"], ) { comm.append(p.login.bytes()); comm.append(p.pass.bytes()); comm.reply_ok(); + NbglStatus::new().text("").show(true); } else { comm.reply(Error::NoConsent); + NbglStatus::new().text("").show(false); } } None => { @@ -256,7 +269,7 @@ extern "C" fn sample_main() { } } c = 0; - } + }, // Display a password on the screen only, without communicating it // to the host. @@ -266,8 +279,9 @@ extern "C" fn sample_main() { match passwords.into_iter().find(|&&x| x.name == name) { Some(&p) => { if validate( + &[&"Show password on the device"] , &[name.as_str()], - &[&"Read", &"password"], + &[&"Read password"], &[&"Cancel"], ) { @@ -285,7 +299,7 @@ extern "C" fn sample_main() { } } c = 0; - } + }, // Delete password by name Instruction::DeleteByName => { @@ -294,15 +308,18 @@ extern "C" fn sample_main() { Some(p) => { if validate( + &[&"Delete password"], &[name.as_str()], - &[&"Remove", &"password"], + &[&"Remove password"], &[&"Cancel"], ) { passwords.remove(p); comm.reply_ok(); + NbglStatus::new().text("Password deleted").show(true); } else { comm.reply(Error::NoConsent); + NbglStatus::new().text("Operation rejected").show(false); } } None => { @@ -311,7 +328,7 @@ extern "C" fn sample_main() { } } c = 0; - } + }, // Export // P1 can be 0 for plaintext, 1 for encrypted export. Instruction::Export => match comm.get_apdu_metadata().p1 { @@ -322,7 +339,7 @@ extern "C" fn sample_main() { // Reserved for export Instruction::ExportNext => { comm.reply(StatusWords::Unknown); - } + }, // Import // P1 can be 0 for plaintext, 1 for encrypted import. Instruction::Import => match comm.get_apdu_metadata().p1 { @@ -333,30 +350,33 @@ extern "C" fn sample_main() { // Reserved for import Instruction::ImportNext => { comm.reply(StatusWords::Unknown); - } + }, Instruction::Clear => { // Remove all passwords comm.reply::( - if validate(&[], &[&"Remove all", &"passwords"], &[&"Cancel"]) + if validate(&[], &[&"Remove all passwords"], &[&"Confirm"], &[&"Cancel"]) { - if validate(&[], &[&"Are you", &"sure?"], &[&"Cancel"]) + if validate(&[], &[&"Are you sure?"], &[&"Confirm"], &[&"Cancel"]) { passwords.clear(); + NbglStatus::new().text("All password are removed").show(true); StatusWords::Ok.into() } else { + NbglStatus::new().text("Operation rejected").show(false); Error::NoConsent.into() } } else { + NbglStatus::new().text("Operation rejected").show(false); Error::NoConsent.into() }, ); c = 0; - } + }, // Exit Instruction::Quit => { comm.reply_ok(); ledger_secure_sdk_sys::exit_app(0); - } + }, // HasName Instruction::HasName => { let name = ArrayString::<32>::from_bytes(comm.get(5, 5 + 32)); @@ -369,17 +389,17 @@ extern "C" fn sample_main() { } } comm.reply_ok(); - } + }, } - } + }; } } - - #[cfg(not(any(target_os = "stax", target_os = "flex")))] fn validate( message:&[&str], + sub_message: &[&str], + confirm: &[&str], cancel: &[&str]) -> bool @@ -394,30 +414,34 @@ fn validate( message:&[&str], #[cfg(any(target_os = "stax", target_os = "flex"))] -fn validate( message:&[&str], +fn validate( message:&[&str], + + sub_message: &[&str], confirm: &[&str], cancel: &[&str]) -> bool { - let my_fields = [ - Field { - name: "Amount", - value: "111 CRAB", - }, - Field { - name: "Destination", - value: "0x1234567890ABCDEF1234567890ABCDEF12345678", - }, - Field { - name: "Memo", - value: "This is a test transaction.", - }, - ]; - NbglReview::<>::new() - .titles(confirm.first().unwrap_or(&""), "subtitle", message.first().unwrap_or(&"") ) - .show(&[]) + + let success = NbglChoice::new().show( + message.first().unwrap_or(&""), + sub_message.first().unwrap_or(&""), + confirm.first().unwrap_or(&""), + cancel.first().unwrap_or(&""), + ); + + if success { + return true; + } else { + return false; + } + + + +// NbglReview::<>::new() +// .titles(confirm.first().unwrap_or(&""), "", message.first().unwrap_or(&"") ) +// .show(&[my_fields[0]]) } @@ -482,25 +506,14 @@ fn int2dec(x: usize) -> [u8; 2] { /// Display global information about the app: /// - Current number of passwords stored /// - App Version -/// +/// #[cfg(not(any(target_os = "stax", target_os = "flex")))] -fn display_infos(passwords: &nvm::Collection, comm: &mut io::Comm) -> io::Event { - let mut stored_n = *b" passwords"; - let pwlen_bytes = int2dec(passwords.len()); - - stored_n[0] = pwlen_bytes[0]; - stored_n[1] = pwlen_bytes[1]; - - // safety: int2dec returns a [u8; 2] consisting of values between - // '0' and '9', thus is valid utf8 - let stored_str = unsafe { core::str::from_utf8_unchecked(&stored_n) }; - +fn display_infos(comm: &mut io::Comm) -> io::Event { const APP_ICON: Glyph = Glyph::from_include(include_gif!("crab.gif")); let pages = [ // The from trait allows to create different styles of pages // without having to use the new() function. &Page::from((["NanoPass", "is ready"], &APP_ICON)), - &Page::from((["Passwords", stored_str], true)), &Page::from((["Version", env!("CARGO_PKG_VERSION")], true)), &Page::from(("Quit", &DASHBOARD_X)), ]; @@ -512,21 +525,14 @@ fn display_infos(passwords: &nvm::Collection, comm: &mut io:: } } } -#[cfg(any(target_os = "stax", target_os = "flex"))] -fn display_infos(passwords: &nvm::Collection, comm: &mut io::Comm) -> io::Event { - let mut stored_n = *b" passwords"; - let pwlen_bytes = int2dec(passwords.len()); - - stored_n[0] = pwlen_bytes[0]; - stored_n[1] = pwlen_bytes[1]; - - // safety: int2dec returns a [u8; 2] consisting of values between - // '0' and '9', thus is valid utf8 - let stored_str = unsafe { core::str::from_utf8_unchecked(&stored_n) }; +#[cfg(any(target_os = "stax", target_os = "flex"))] +pub fn display_infos(_: &mut Comm) -> Event { + // Load glyph from 64x64 4bpp gif file with include_gif macro. Creates an NBGL compatible glyph. const FERRIS: NbglGlyph = NbglGlyph::from_include(include_gif!("key_16x16.gif", NBGL)); // Display the home screen. + NbglHomeAndSettings::new() .glyph(&FERRIS) .infos( @@ -582,11 +588,13 @@ fn set_password( return match passwords.into_iter().position(|x| x.name == *name) { Some(index) => { // A password with this name already exists. - if !validate(&[name.as_str()], &[&"Update", &"password"], &[&"Cancel"]) + if !validate(&[name.as_str()], &[&"Update password"], &[&"Confirm"], &[&"Cancel"]) { + NbglStatus::new().text("Operation rejected").show(false); return Err(Error::NoConsent); } passwords.remove(index); + NbglStatus::new().text("").show(true); match passwords.add(&new_item) { Ok(()) => Ok(()), // We just removed a password, this should not happen @@ -595,10 +603,12 @@ fn set_password( } None => { // Ask user confirmation - if !validate(&[name.as_str()], &[&"Create", &"password"], &[&"Cancel"]) + if !validate(&[name.as_str()], &[&"Create password"], &[&"Confirm"], &[&"Cancel"]) { + NbglStatus::new().text("Operation rejected").show(false); return Err(Error::NoConsent); } + NbglStatus::new().text("").show(true); match passwords.add(&new_item) { Ok(()) => Ok(()), Err(nvm::StorageFullError) => Err(Error::StorageFull), @@ -618,7 +628,7 @@ fn export( enc_key: Option<&[u8; 32]>, ) { // Ask user confirmation - if !validate(&[], &[&"Export", &"passwords"], &[&"Cancel"]) { + if !validate(&[], &[&"Export passwords"], &[&"Confirm"], &[&"Cancel"]) { comm.reply(Error::NoConsent); return; } @@ -626,7 +636,7 @@ fn export( // If export is in plaintext, add a warning let encrypted = enc_key.is_some(); if !encrypted - && !validate(&[&"Export is plaintext!"], &[&"Confirm"], &[&"Cancel"]) + && !validate(&[], &[&"Export is plaintext!"], &[&"Confirm"], &[&"Cancel"]) { comm.reply(Error::NoConsent); return; @@ -721,7 +731,7 @@ fn import( count_bytes.copy_from_slice(comm.get(5, 5 + 4)); let mut count = u32::from_be_bytes(count_bytes); // Ask user confirmation - if !validate(&[], &[&"Import", &"passwords"], &[&"Cancel"]) { + if !validate(&[], &[&"Import passwords"], &[&"Confirm"], &[&"Cancel"]) { comm.reply(Error::NoConsent); return; } else { @@ -818,8 +828,15 @@ fn show_message(msg: &str) { #[cfg(any(target_os = "stax", target_os = "flex"))] fn show_message(msg: &str) { + NbglSpinner::new().text(msg).show(); } #[cfg(any(target_os = "stax", target_os = "flex"))] fn popup(msg: &str) { + let _info = NbglChoice::new().show( + msg, + "", + "Ok", + "" + ); } \ No newline at end of file