From 8c327ca53102898d343023b087c8d8031251b640 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Tue, 6 Feb 2024 14:25:27 +0100 Subject: [PATCH 001/126] SFT-3222: Rename GitHub Action. * .github/workflows/validate_and_build.yaml: Rename file to ... * .github/workflows/build.yaml: ... this one and remove lint job. Signed-off-by: Jean-Pierre De Jesus DIAZ --- .../{validate_and_build.yaml => build.yaml} | 52 ++++--------------- 1 file changed, 10 insertions(+), 42 deletions(-) rename .github/workflows/{validate_and_build.yaml => build.yaml} (84%) diff --git a/.github/workflows/validate_and_build.yaml b/.github/workflows/build.yaml similarity index 84% rename from .github/workflows/validate_and_build.yaml rename to .github/workflows/build.yaml index 796f8b94e..370459b0f 100644 --- a/.github/workflows/validate_and_build.yaml +++ b/.github/workflows/build.yaml @@ -3,44 +3,13 @@ # # validate_and_build.yaml - GitHub actions for Passport -name: Validate and Build +name: Build on: [push] jobs: - lint: - name: Lint + firmware: + name: Firmware runs-on: ubuntu-20.04 - services: - registry: - image: registry:2 - ports: - - 5000:5000 - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - uses: docker/setup-buildx-action@v3 - with: - driver-opts: network=host - - uses: docker/build-push-action@v5 - with: - push: true - context: . - cache-from: type=gha - cache-to: type=gha - tags: localhost:5000/foundation-devices/passport2:latest - - uses: extractions/setup-just@69d82fb0233557aec017ef13706851d0694e0f1d - - run: echo "DOCKER_IMAGE=localhost:5000/foundation-devices/passport2:latest" >> $GITHUB_ENV - - - name: Lint the codebase - run: just lint - - build-firmware: - name: Build Firmware - runs-on: ubuntu-20.04 - needs: [lint] - strategy: matrix: build: @@ -120,10 +89,10 @@ jobs: name: v${{env.version}}${{ matrix.build.hash_suffix }}-hashes.md path: ports/stm32/build-Passport/v${{env.version}}-beta${{ matrix.build.hash_suffix }}-hashes.md - build-bootloader: - name: Build Bootloader + bootloader: + name: Bootloader runs-on: ubuntu-20.04 - needs: [lint, build-firmware] + needs: [firmware] # TODO: PASS1-665. strategy: @@ -164,10 +133,10 @@ jobs: name: bootloader-${{ env.SCREEN_MODE }}.bin path: ports/stm32/boards/Passport/bootloader/arm/release/bootloader-${{ env.SCREEN_MODE }}.bin - build-simulator: - name: Build Simulator + simulator: + name: Simulator runs-on: ubuntu-20.04 - needs: [lint, build-firmware] + needs: [firmware] strategy: matrix: @@ -200,9 +169,8 @@ jobs: run: just build-simulator ${{ matrix.screen }} build-tools: - name: Build Tools + name: Tools runs-on: ubuntu-20.04 - needs: [lint] services: registry: From 0b6d88e62cc3de74e1035150dc2d45941736ca54 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Tue, 6 Feb 2024 14:26:39 +0100 Subject: [PATCH 002/126] SFT-3222: Add rust-toolchain action. * .github/actions/rust-toolchain/action.yml: Add vendored dtolnay/rust-toolchain action. * .reuse/dep5: Add dtolnay to dep5 as the copyright holder of rust-toolchain action file. Signed-off-by: Jean-Pierre De Jesus DIAZ --- .github/actions/rust-toolchain/action.yml | 123 ++++++++++++++++++++++ .reuse/dep5 | 4 + 2 files changed, 127 insertions(+) create mode 100644 .github/actions/rust-toolchain/action.yml diff --git a/.github/actions/rust-toolchain/action.yml b/.github/actions/rust-toolchain/action.yml new file mode 100644 index 000000000..03aef0df7 --- /dev/null +++ b/.github/actions/rust-toolchain/action.yml @@ -0,0 +1,123 @@ +name: rustup toolchain install +author: David Tolnay +description: Install the Rust toolchain +branding: + icon: activity + color: purple + +inputs: + toolchain: + description: Rust toolchain specification -- see https://rust-lang.github.io/rustup/concepts/toolchains.html#toolchain-specification + required: true + targets: + description: Comma-separated list of target triples to install for this toolchain + required: false + target: + description: Alias for `targets` + required: false + components: + description: Comma-separated list of components to be additionally installed + required: false + +outputs: + cachekey: + description: A short hash of the rustc version, appropriate for use as a cache key. "20220627a831" + value: ${{steps.rustc-version.outputs.cachekey}} + name: + description: Rustup's name for the selected version of the toolchain. "1.62.0" # suitable for use with `cargo +${{steps.toolchain.outputs.name}}` + value: ${{steps.parse.outputs.toolchain}} + +runs: + using: composite + steps: + - id: parse + run: | + : parse toolchain version + if [[ $toolchain =~ ^stable' '[0-9]+' '(year|month|week|day)s?' 'ago$ ]]; then + if [[ ${{runner.os}} == macOS ]]; then + echo "toolchain=1.$((($(date -v-$(sed 's/stable \([0-9]*\) \(.\).*/\1\2/' <<< $toolchain) +%s)/60/60/24-16569)/7/6))" >> $GITHUB_OUTPUT + else + echo "toolchain=1.$((($(date --date "${toolchain#stable }" +%s)/60/60/24-16569)/7/6))" >> $GITHUB_OUTPUT + fi + elif [[ $toolchain =~ ^stable' 'minus' '[0-9]+' 'releases?$ ]]; then + echo "toolchain=1.$((($(date +%s)/60/60/24-16569)/7/6-${toolchain//[^0-9]/}))" >> $GITHUB_OUTPUT + else + echo "toolchain=$toolchain" >> $GITHUB_OUTPUT + fi + env: + toolchain: ${{inputs.toolchain}} + shell: bash + + - id: flags + run: | + : construct rustup command line + echo "targets=$(for t in ${targets//,/ }; do echo -n ' --target' $t; done)" >> $GITHUB_OUTPUT + echo "components=$(for c in ${components//,/ }; do echo -n ' --component' $c; done)" >> $GITHUB_OUTPUT + echo "downgrade=${{inputs.toolchain == 'nightly' && inputs.components && ' --allow-downgrade' || ''}}" >> $GITHUB_OUTPUT + env: + targets: ${{inputs.targets || inputs.target || ''}} + components: ${{inputs.components}} + shell: bash + + - run: | + : install rustup if needed + if ! command -v rustup &>/dev/null; then + curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused --location --silent --show-error --fail "https://sh.rustup.rs" | sh -s -- --default-toolchain none -y + echo "${CARGO_HOME:-$HOME/.cargo}/bin" >> $GITHUB_PATH + fi + if: runner.os != 'Windows' + shell: bash + + - name: rustup toolchain install ${{steps.parse.outputs.toolchain}} + run: rustup toolchain install ${{steps.parse.outputs.toolchain}}${{steps.flags.outputs.targets}}${{steps.flags.outputs.components}} --profile minimal${{steps.flags.outputs.downgrade}} --no-self-update + shell: bash + + - run: rustup default ${{steps.parse.outputs.toolchain}} + shell: bash + + - id: rustc-version + run: | + : create cachekey + DATE=$(rustc +${{steps.parse.outputs.toolchain}} --version --verbose | sed -ne 's/^commit-date: \(20[0-9][0-9]\)-\([01][0-9]\)-\([0-3][0-9]\)$/\1\2\3/p') + HASH=$(rustc +${{steps.parse.outputs.toolchain}} --version --verbose | sed -ne 's/^commit-hash: //p') + echo "cachekey=$(echo $DATE$HASH | head -c12)" >> $GITHUB_OUTPUT + shell: bash + + - run: | + : disable incremental compilation + if [ -z "${CARGO_INCREMENTAL+set}" ]; then + echo CARGO_INCREMENTAL=0 >> $GITHUB_ENV + fi + shell: bash + + - run: | + : enable colors in Cargo output + if [ -z "${CARGO_TERM_COLOR+set}" ]; then + echo CARGO_TERM_COLOR=always >> $GITHUB_ENV + fi + shell: bash + + - run: | + : enable Cargo sparse registry + # implemented in 1.66, stabilized in 1.68, made default in 1.70 + if [ -z "${CARGO_REGISTRIES_CRATES_IO_PROTOCOL+set}" -o -f "${{runner.temp}}"/.implicit_cargo_registries_crates_io_protocol ]; then + if rustc +${{steps.parse.outputs.toolchain}} --version --verbose | grep -q '^release: 1\.6[89]\.'; then + touch "${{runner.temp}}"/.implicit_cargo_registries_crates_io_protocol || true + echo CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse >> $GITHUB_ENV + elif rustc +${{steps.parse.outputs.toolchain}} --version --verbose | grep -q '^release: 1\.6[67]\.'; then + touch "${{runner.temp}}"/.implicit_cargo_registries_crates_io_protocol || true + echo CARGO_REGISTRIES_CRATES_IO_PROTOCOL=git >> $GITHUB_ENV + fi + fi + shell: bash + + - run: | + : work around spurious network errors in curl 8.0 + # https://rust-lang.zulipchat.com/#narrow/stream/246057-t-cargo/topic/timeout.20investigation + if rustc +${{steps.parse.outputs.toolchain}} --version --verbose | grep -q '^release: 1\.7[01]\.'; then + echo CARGO_HTTP_MULTIPLEXING=false >> $GITHUB_ENV + fi + shell: bash + + - run: rustc +${{steps.parse.outputs.toolchain}} --version --verbose + shell: bash diff --git a/.reuse/dep5 b/.reuse/dep5 index baa1d61ef..38fbe44ff 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -104,3 +104,7 @@ Files: version.txt Copyright: © 2021 Foundation Devices, Inc. License: GPL-3.0-or-later + +Files: .github/actions/rust-toolchain/action.yml +Copyright: 2021 David Tolnay +License: MIT From 2e7a2e500e9b6768bd6b9d3aab2bcb16718e6e1e Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Tue, 6 Feb 2024 14:27:57 +0100 Subject: [PATCH 003/126] SFT-3222: Add lint GitHub Action. * .github/workflows/lint.yaml: New action. Signed-off-by: Jean-Pierre De Jesus DIAZ --- .github/workflows/lint.yaml | 81 +++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 .github/workflows/lint.yaml diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml new file mode 100644 index 000000000..6e2cfef1b --- /dev/null +++ b/.github/workflows/lint.yaml @@ -0,0 +1,81 @@ +# SPDX-FileCopyrightText: © 2024 Foundation Devices, Inc. +# SPDX-License-Identifier: GPL-3.0-or-later + +name: Lint +on: [push, pull_request] +jobs: + is-reuse-compliant: + name: Is REUSE compliant? + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: fsfe/reuse-action@v2 + + rust-code-compiles: + name: Rust code compiles? + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/rust-toolchain + with: + toolchain: 1.67.1 + targets: thumbv7em-none-eabihf + - run: | + cargo check --manifest-path extmod/foundation-rust/Cargo.toml + # Required by secp256k1-sys. + - run: sudo apt-get install -y gcc-arm-none-eabi + - run: | + cargo check --manifest-path extmod/foundation-rust/Cargo.toml \ + --target thumbv7em-none-eabihf + + is-the-rust-code-formatted: + name: Is the Rust code formatted? + needs: [rust-code-compiles] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/rust-toolchain + with: + toolchain: 1.67.1 + components: rustfmt + - run: | + cargo fmt --manifest-path extmod/foundation-rust/Cargo.toml \ + --all -- --check + + is-the-python-code-formatted: + name: Is the Python code formatted? + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: sudo apt-get install -y pycodestyle + - run: pycodestyle --statistics --exclude translations ports/stm32/boards/Passport + + is-foundation-header-up-to-date: + name: Is foundation.h header file up to date? + needs: [rust-code-compiles] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/rust-toolchain + with: + toolchain: 1.67.1 + - run: cargo install cbindgen@^0.24 --locked + - run: | + cbindgen --config extmod/foundation-rust/cbindgen.toml \ + --output extmod/foundation-rust/include/foundation.h \ + --verify \ + extmod/foundation-rust/ + + rust-tests-pass: + name: Rust tests pass? + needs: [rust-code-compiles] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/rust-toolchain + with: + toolchain: 1.67.1 + targets: + - run: | + cargo test --manifest-path extmod/foundation-rust/Cargo.toml \ + --features std From 4088455aeab93f5e10bde4e71b61639a44a6ffc6 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Tue, 6 Feb 2024 14:35:49 +0100 Subject: [PATCH 004/126] SFT-3222: Update issue number in file. Signed-off-by: Jean-Pierre De Jesus DIAZ --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 370459b0f..dbdc01e62 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -94,7 +94,7 @@ jobs: runs-on: ubuntu-20.04 needs: [firmware] - # TODO: PASS1-665. + # TODO: SFT-1077. strategy: matrix: screen: ['color'] From c088992e4758cac58af671ae0825b3c3df315ddf Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Tue, 6 Feb 2024 14:42:22 +0100 Subject: [PATCH 005/126] SFT-3222: Update MSRV to 1.70.0. Signed-off-by: Jean-Pierre De Jesus DIAZ --- .github/workflows/lint.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 6e2cfef1b..f3360a55d 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -18,7 +18,7 @@ jobs: - uses: actions/checkout@v4 - uses: ./.github/actions/rust-toolchain with: - toolchain: 1.67.1 + toolchain: 1.70.0 targets: thumbv7em-none-eabihf - run: | cargo check --manifest-path extmod/foundation-rust/Cargo.toml @@ -36,7 +36,7 @@ jobs: - uses: actions/checkout@v4 - uses: ./.github/actions/rust-toolchain with: - toolchain: 1.67.1 + toolchain: 1.70.0 components: rustfmt - run: | cargo fmt --manifest-path extmod/foundation-rust/Cargo.toml \ @@ -58,7 +58,7 @@ jobs: - uses: actions/checkout@v4 - uses: ./.github/actions/rust-toolchain with: - toolchain: 1.67.1 + toolchain: 1.70.0 - run: cargo install cbindgen@^0.24 --locked - run: | cbindgen --config extmod/foundation-rust/cbindgen.toml \ @@ -74,7 +74,7 @@ jobs: - uses: actions/checkout@v4 - uses: ./.github/actions/rust-toolchain with: - toolchain: 1.67.1 + toolchain: 1.70.0 targets: - run: | cargo test --manifest-path extmod/foundation-rust/Cargo.toml \ From 8ab68862aa4da154e98f9ce2c8c28e3d8d4e7563 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Tue, 6 Feb 2024 15:41:09 +0100 Subject: [PATCH 006/126] SFT-3222: Remove setup-just action. * .github/workflows/build.yaml: Remove setup-just action. Signed-off-by: Jean-Pierre De Jesus DIAZ --- .github/workflows/build.yaml | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index dbdc01e62..810803a4f 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -38,7 +38,11 @@ jobs: cache-from: type=gha cache-to: type=gha tags: localhost:5000/foundation-devices/passport2:latest - - uses: extractions/setup-just@69d82fb0233557aec017ef13706851d0694e0f1d + - uses: ./.github/actions/rust-toolchain + with: + toolchain: 1.70.0 + targets: thumbv7em-none-eabihf + - run: cargo install just@1.23.0 --locked - run: | echo "DOCKER_IMAGE=localhost:5000/foundation-devices/passport2:latest" >> $GITHUB_ENV echo "SCREEN_MODE=$(echo "${{ matrix.build.screen }}" | tr a-z A-Z)" >> $GITHUB_ENV @@ -119,7 +123,11 @@ jobs: cache-from: type=gha cache-to: type=gha tags: localhost:5000/foundation-devices/passport2:latest - - uses: extractions/setup-just@69d82fb0233557aec017ef13706851d0694e0f1d + - uses: ./.github/actions/rust-toolchain + with: + toolchain: 1.70.0 + targets: thumbv7em-none-eabihf + - run: cargo install just@1.23.0 --locked - run: | echo "DOCKER_IMAGE=localhost:5000/foundation-devices/passport2:latest" >> $GITHUB_ENV echo "SCREEN_MODE=$(echo ${{ matrix.screen }} | tr a-z A-Z)" >> $GITHUB_ENV @@ -162,7 +170,11 @@ jobs: cache-from: type=gha cache-to: type=gha tags: localhost:5000/foundation-devices/passport2:latest - - uses: extractions/setup-just@69d82fb0233557aec017ef13706851d0694e0f1d + - uses: ./.github/actions/rust-toolchain + with: + toolchain: 1.70.0 + targets: thumbv7em-none-eabihf + - run: cargo install just@1.23.0 --locked - run: echo "DOCKER_IMAGE=localhost:5000/foundation-devices/passport2:latest" >> $GITHUB_ENV - name: Build @@ -192,7 +204,11 @@ jobs: cache-from: type=gha cache-to: type=gha tags: localhost:5000/foundation-devices/passport2:latest - - uses: extractions/setup-just@69d82fb0233557aec017ef13706851d0694e0f1d + - uses: ./.github/actions/rust-toolchain + with: + toolchain: 1.70.0 + targets: thumbv7em-none-eabihf + - run: cargo install just@1.23.0 --locked - run: echo "DOCKER_IMAGE=localhost:5000/foundation-devices/passport2:latest" >> $GITHUB_ENV - name: Build From 72ddaa668d06f155f567d04da1c1ede006238022 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Mon, 13 May 2024 14:07:59 +0200 Subject: [PATCH 007/126] SFT-3616: Fix Rust warnings. The Passport firmware code runs on a "single thread" so the warnings don't apply here. * extmod/foundation-rust/src/secp256k1.rs: Replace `&mut ...' with `&mut *addr_of_mut!(...)'. * extmod/foundation-rust/src/ur/decoder.rs: Ditto. * extmod/foundation-rust/src/ur/encoder.rs: Ditto. * extmod/foundation-rust/src/ur/mod.rs: Ditto. --- extmod/foundation-rust/src/secp256k1.rs | 9 ++++++++- extmod/foundation-rust/src/ur/decoder.rs | 5 +++-- extmod/foundation-rust/src/ur/encoder.rs | 6 +++--- extmod/foundation-rust/src/ur/mod.rs | 4 ++-- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/extmod/foundation-rust/src/secp256k1.rs b/extmod/foundation-rust/src/secp256k1.rs index 22e8c4e88..5dba6de13 100644 --- a/extmod/foundation-rust/src/secp256k1.rs +++ b/extmod/foundation-rust/src/secp256k1.rs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: © 2023 Foundation Devices, Inc. // SPDX-License-Identifier: GPL-3.0-or-later +use core::ptr; use once_cell::sync::Lazy; use secp256k1::{ ffi::types::AlignedType, AllPreallocated, Keypair, Message, Secp256k1, @@ -12,7 +13,13 @@ static mut PRE_ALLOCATED_CTX_BUF: [AlignedType; 20] = [AlignedType::ZERO; 20]; /// cbindgen:ignore static PRE_ALLOCATED_CTX: Lazy>> = Lazy::new(|| { - let buf = unsafe { &mut PRE_ALLOCATED_CTX_BUF }; + // SAFETY: + // + // This pre-allocated buffer safety depends on trusting libsecp256k1 + // that it writes the context buffer only once for initialization and + // then only performs reads to it. + let buf = unsafe { &mut *ptr::addr_of_mut!(PRE_ALLOCATED_CTX_BUF) }; + Secp256k1::preallocated_new(buf) .expect("the pre-allocated context buf should have enough space") }); diff --git a/extmod/foundation-rust/src/ur/decoder.rs b/extmod/foundation-rust/src/ur/decoder.rs index 8b81dc608..17e22fb99 100644 --- a/extmod/foundation-rust/src/ur/decoder.rs +++ b/extmod/foundation-rust/src/ur/decoder.rs @@ -3,7 +3,7 @@ //! Decoder. -use core::{fmt, slice, str}; +use core::{fmt, ptr, slice, str}; use foundation_ur::{ bytewords, bytewords::Style, decoder::Error, max_fragment_len, @@ -246,7 +246,8 @@ pub unsafe extern "C" fn ur_decode_single_part( } }; - let message = unsafe { &mut UR_DECODER_SINGLE_PART_MESSAGE }; + let message = + unsafe { &mut *ptr::addr_of_mut!(UR_DECODER_SINGLE_PART_MESSAGE) }; message.clear(); message .resize(UR_DECODER_MAX_SINGLE_PART_MESSAGE_LEN, 0) diff --git a/extmod/foundation-rust/src/ur/encoder.rs b/extmod/foundation-rust/src/ur/encoder.rs index 881e42421..e1974627f 100644 --- a/extmod/foundation-rust/src/ur/encoder.rs +++ b/extmod/foundation-rust/src/ur/encoder.rs @@ -3,7 +3,7 @@ //! Encoder. -use core::{ffi::c_char, fmt::Write}; +use core::{ffi::c_char, fmt::Write, ptr}; use foundation_ur::{max_fragment_len, HeaplessEncoder}; use minicbor::{Encode, Encoder}; @@ -106,7 +106,7 @@ pub unsafe extern "C" fn ur_encoder_start( let value = unsafe { value.to_value() }; // SAFETY: This code assumes that runs on a single thread. - let message = unsafe { &mut UR_ENCODER_MESSAGE }; + let message = unsafe { &mut *ptr::addr_of_mut!(UR_ENCODER_MESSAGE) }; message.clear(); let mut e = Encoder::new(Writer(message)); @@ -142,7 +142,7 @@ pub unsafe extern "C" fn ur_encoder_next_part( ) { let part = encoder.inner.next_part(); - let buf = unsafe { &mut UR_ENCODER_STRING }; + let buf = unsafe { &mut *ptr::addr_of_mut!(UR_ENCODER_STRING) }; buf.clear(); write!(buf, "{part}").unwrap(); buf.push(b'\0').unwrap(); diff --git a/extmod/foundation-rust/src/ur/mod.rs b/extmod/foundation-rust/src/ur/mod.rs index d65ffdeb5..90566d344 100644 --- a/extmod/foundation-rust/src/ur/mod.rs +++ b/extmod/foundation-rust/src/ur/mod.rs @@ -3,7 +3,7 @@ //! Uniform Resources. -use core::{ffi::c_char, fmt, fmt::Write}; +use core::{ffi::c_char, fmt, fmt::Write, ptr}; /// cbindgen:ignore #[used] @@ -43,7 +43,7 @@ impl UR_Error { /// an invalid message. So the data pointed by `message` should be copied /// and `UR_Error` must be dropped. pub unsafe fn new(message: &dyn fmt::Display, kind: UR_ErrorKind) -> Self { - let error = &mut UR_ERROR; + let error = &mut *ptr::addr_of_mut!(UR_ERROR); error.clear(); if write!(error, "{}", message).is_err() { From 448062e6e733183cc4ad9721d4a4d16fadb28dcd Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Mon, 13 May 2024 15:45:25 +0200 Subject: [PATCH 008/126] SFT-3617: Move dependabot.yml. * .github/workflows/dependabot.yaml: Move from here... * .github/dependabot.yaml: ... to here. --- .github/{workflows => }/dependabot.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/{workflows => }/dependabot.yaml (100%) diff --git a/.github/workflows/dependabot.yaml b/.github/dependabot.yaml similarity index 100% rename from .github/workflows/dependabot.yaml rename to .github/dependabot.yaml From 8780a92f06c9470d9fc02347734bae1aed43ac65 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Fri, 10 May 2024 20:16:23 +0200 Subject: [PATCH 009/126] SFT-3618: Rename Schnorr sign function. For consistency. * extmod/foundation-rust/include/foundation.h: Re-generate file. * extmod/foundation-rust/src/secp256k1.rs (foundation_secp256k1_schnorr_sign): Rename this ... (foundation_secp256k1_sign_schnorr): ... to this. * extmod/foundation/modfoundation-secp56k1.h (mod_foundation_secp256k1_sign_schnorr): Update. --- extmod/foundation-rust/include/foundation.h | 2 +- extmod/foundation-rust/src/secp256k1.rs | 2 +- extmod/foundation/modfoundation-secp56k1.h | 2 +- ports/stm32/boards/Passport/modules/taproot.py | 2 +- ports/stm32/boards/Passport/modules/utils.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extmod/foundation-rust/include/foundation.h b/extmod/foundation-rust/include/foundation.h index cbb790588..810cc1b9f 100644 --- a/extmod/foundation-rust/include/foundation.h +++ b/extmod/foundation-rust/include/foundation.h @@ -367,7 +367,7 @@ extern UR_Encoder UR_ENCODER; * - `secret_key` is the secret key used to sign the message. * - `signature` is the output of the resulting signature. */ -void foundation_secp256k1_schnorr_sign(const uint8_t (*data)[32], +void foundation_secp256k1_sign_schnorr(const uint8_t (*data)[32], const uint8_t (*secret_key)[32], uint8_t (*signature)[64]); diff --git a/extmod/foundation-rust/src/secp256k1.rs b/extmod/foundation-rust/src/secp256k1.rs index 5dba6de13..bebf92cdc 100644 --- a/extmod/foundation-rust/src/secp256k1.rs +++ b/extmod/foundation-rust/src/secp256k1.rs @@ -29,7 +29,7 @@ static PRE_ALLOCATED_CTX: Lazy>> = /// - `data` is the message hash. /// - `secret_key` is the secret key used to sign the message. /// - `signature` is the output of the resulting signature. -#[export_name = "foundation_secp256k1_schnorr_sign"] +#[export_name = "foundation_secp256k1_sign_schnorr"] pub extern "C" fn secp256k1_sign_schnorr( data: &[u8; 32], secret_key: &[u8; 32], diff --git a/extmod/foundation/modfoundation-secp56k1.h b/extmod/foundation/modfoundation-secp56k1.h index 2ca47cf97..788f18fa5 100644 --- a/extmod/foundation/modfoundation-secp56k1.h +++ b/extmod/foundation/modfoundation-secp56k1.h @@ -24,7 +24,7 @@ STATIC mp_obj_t mod_foundation_secp256k1_sign_schnorr(mp_obj_t data_obj, mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("secret key should be 32 bytes")); } - foundation_secp256k1_schnorr_sign(data.buf, + foundation_secp256k1_sign_schnorr(data.buf, secret_key.buf, &signature); diff --git a/ports/stm32/boards/Passport/modules/taproot.py b/ports/stm32/boards/Passport/modules/taproot.py index 62820edc9..74a47cfc9 100644 --- a/ports/stm32/boards/Passport/modules/taproot.py +++ b/ports/stm32/boards/Passport/modules/taproot.py @@ -170,7 +170,7 @@ def taproot_sign_key(script_tree, internal_seckey, hash_type, sighash): else: _, h = taproot_tree_helper(script_tree) output_seckey = taproot_tweak_seckey(internal_seckey, h) - sig = secp256k1.schnorr_sign(sighash, output_seckey) + sig = secp256k1.sign_schnorr(sighash, output_seckey) if hash_type != 0: sig += bytes([hash_type]) return sig diff --git a/ports/stm32/boards/Passport/modules/utils.py b/ports/stm32/boards/Passport/modules/utils.py index f9bd608aa..3ef4370c1 100644 --- a/ports/stm32/boards/Passport/modules/utils.py +++ b/ports/stm32/boards/Passport/modules/utils.py @@ -1373,7 +1373,7 @@ def nostr_nip19_from_key(key, key_type): # generate nsec/npub def nostr_sign(key, message): from foundation import secp256k1 - return secp256k1.schnorr_sign(message, key) + return secp256k1.sign_schnorr(message, key) months = { From 2334cf8394f518a15b0c25261c295edb0163a448 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Fri, 10 May 2024 20:36:00 +0200 Subject: [PATCH 010/126] SFT-3618: Add foundation.secp256k1.sign_ecdsa. * extmod/foundation-rust/include/foundation.h: Re-generate file. * extmod/foundation-rust/src/secp256k1.rs (foundation_secp256k1_sign_ecdsa): New function. * extmod/foundation/modfoundation-secp56k1.h (mod_foundation_secp256k1_sign_ecdsa): New function. (mod_foundation_secp256k1_sign_ecdsa_obj): New variable. (mod_foundation_secp256k1_globals_table): Add MP_QSTR_sign_ecdsa. --- extmod/foundation-rust/include/foundation.h | 11 ++++++++ extmod/foundation-rust/src/secp256k1.rs | 22 ++++++++++++++- extmod/foundation/modfoundation-secp56k1.h | 31 ++++++++++++++++++++- 3 files changed, 62 insertions(+), 2 deletions(-) diff --git a/extmod/foundation-rust/include/foundation.h b/extmod/foundation-rust/include/foundation.h index 810cc1b9f..e29180646 100644 --- a/extmod/foundation-rust/include/foundation.h +++ b/extmod/foundation-rust/include/foundation.h @@ -360,6 +360,17 @@ extern UR_Decoder UR_DECODER; extern UR_Encoder UR_ENCODER; +/** + * Computes a ECDSA signature over the message `data`. + * + * - `data` is the message hash. + * - `secret_key` is the secret key used to sign the message. + * - `signature` is the output of the resulting signature. + */ +void foundation_secp256k1_sign_ecdsa(const uint8_t (*data)[32], + const uint8_t (*secret_key)[32], + uint8_t (*signature)[64]); + /** * Computes a Schnorr signature over the message `data`. * diff --git a/extmod/foundation-rust/src/secp256k1.rs b/extmod/foundation-rust/src/secp256k1.rs index bebf92cdc..5423679ab 100644 --- a/extmod/foundation-rust/src/secp256k1.rs +++ b/extmod/foundation-rust/src/secp256k1.rs @@ -4,7 +4,8 @@ use core::ptr; use once_cell::sync::Lazy; use secp256k1::{ - ffi::types::AlignedType, AllPreallocated, Keypair, Message, Secp256k1, + ffi::types::AlignedType, AllPreallocated, KeyPair, Message, Secp256k1, + SecretKey, }; /// cbindgen:ignore @@ -24,6 +25,25 @@ static PRE_ALLOCATED_CTX: Lazy>> = .expect("the pre-allocated context buf should have enough space") }); +/// Computes a ECDSA signature over the message `data`. +/// +/// - `data` is the message hash. +/// - `secret_key` is the secret key used to sign the message. +/// - `signature` is the output of the resulting signature. +#[export_name = "foundation_secp256k1_sign_ecdsa"] +pub extern "C" fn secp256k1_sign_ecdsa( + data: &[u8; 32], + secret_key: &[u8; 32], + signature: &mut [u8; 64], +) { + let secret_key = + SecretKey::from_slice(secret_key).expect("invalid secret key"); + + let msg = Message::from_slice(data).unwrap(); + let sig = PRE_ALLOCATED_CTX.sign_ecdsa(&msg, &secret_key); + signature.copy_from_slice(&sig.serialize_compact()); +} + /// Computes a Schnorr signature over the message `data`. /// /// - `data` is the message hash. diff --git a/extmod/foundation/modfoundation-secp56k1.h b/extmod/foundation/modfoundation-secp56k1.h index 788f18fa5..b2b7d8398 100644 --- a/extmod/foundation/modfoundation-secp56k1.h +++ b/extmod/foundation/modfoundation-secp56k1.h @@ -3,6 +3,34 @@ #include "foundation.h" +/// def sign_ecdsa(data, secret_key) -> bytes: +STATIC mp_obj_t mod_foundation_secp256k1_sign_ecdsa(mp_obj_t data_obj, + mp_obj_t secret_key_obj) +{ + mp_buffer_info_t data; + mp_buffer_info_t secret_key; + uint8_t signature[64]; + + mp_get_buffer_raise(data_obj, &data, MP_BUFFER_READ); + mp_get_buffer_raise(secret_key_obj, &secret_key, MP_BUFFER_READ); + + if (data.len != 32) { + mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("data should be 32 bytes")); + } + + if (secret_key.len != 32) { + mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("secret key should be 32 bytes")); + } + + foundation_secp256k1_sign_ecdsa(data.buf, + secret_key.buf, + &signature); + + return mp_obj_new_bytes(signature, sizeof(signature)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_foundation_secp256k1_sign_ecdsa_obj, + mod_foundation_secp256k1_sign_ecdsa); + /// def sign_schnorr(data, secret_key) -> bytes: /// """ /// """ @@ -34,7 +62,8 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_foundation_secp256k1_sign_schnorr_obj, mod_foundation_secp256k1_sign_schnorr); STATIC const mp_rom_map_elem_t mod_foundation_secp256k1_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR_schnorr_sign), MP_ROM_PTR(&mod_foundation_secp256k1_sign_schnorr_obj) }, + { MP_ROM_QSTR(MP_QSTR_sign_ecdsa), MP_ROM_PTR(&mod_foundation_secp256k1_sign_ecdsa_obj) }, + { MP_ROM_QSTR(MP_QSTR_sign_schnorr), MP_ROM_PTR(&mod_foundation_secp256k1_sign_schnorr_obj) }, }; STATIC MP_DEFINE_CONST_DICT(mod_foundation_secp256k1_globals, mod_foundation_secp256k1_globals_table); From 50d91e01b31ac5544f75d769c431062d1be7b8d9 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Mon, 13 May 2024 17:00:41 +0200 Subject: [PATCH 011/126] SFT-3618: Add foundation.secp256k1.public_key_schnorr. * extmod/foundation/modfoundation-secp56k1.h (mod_foundation_secp256k1_public_key_schnorr): New function. (mod_foundation_secp256k1_public_key_schnorr_obj): New variable. (mod_foundation_secp256k1_globals_table): Add MP_QSTR_public_key_schnorr. * extmod/foundation-rust/include/foundation.h: Re-generate file. * extmod/foundation-rust/src/secp256k1.rs (secp256k1_public_key_schnorr): New function. --- extmod/foundation-rust/include/foundation.h | 10 ++++++++++ extmod/foundation-rust/src/secp256k1.rs | 16 ++++++++++++++++ extmod/foundation/modfoundation-secp56k1.h | 18 ++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/extmod/foundation-rust/include/foundation.h b/extmod/foundation-rust/include/foundation.h index e29180646..7696c3dbc 100644 --- a/extmod/foundation-rust/include/foundation.h +++ b/extmod/foundation-rust/include/foundation.h @@ -360,6 +360,16 @@ extern UR_Decoder UR_DECODER; extern UR_Encoder UR_ENCODER; +/** + * Calculate a "Schnorr" public key from the secret key. + * + * See also: + * + * - https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki#user-content-Public_Key_Conversion + */ +void foundation_secp256k1_public_key_schnorr(const uint8_t (*secret_key)[32], + uint8_t (*public_key)[32]); + /** * Computes a ECDSA signature over the message `data`. * diff --git a/extmod/foundation-rust/src/secp256k1.rs b/extmod/foundation-rust/src/secp256k1.rs index 5423679ab..ef6a929b6 100644 --- a/extmod/foundation-rust/src/secp256k1.rs +++ b/extmod/foundation-rust/src/secp256k1.rs @@ -25,6 +25,22 @@ static PRE_ALLOCATED_CTX: Lazy>> = .expect("the pre-allocated context buf should have enough space") }); +/// Calculate a "Schnorr" public key from the secret key. +/// +/// See also: +/// +/// - https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki#user-content-Public_Key_Conversion +#[export_name = "foundation_secp256k1_public_key_schnorr"] +pub extern "C" fn secp256k1_public_key_schnorr( + secret_key: &[u8; 32], + public_key: &mut [u8; 32], +) { + let keypair = KeyPair::from_seckey_slice(&PRE_ALLOCATED_CTX, secret_key) + .expect("invalid secret key"); + let compressed_key = keypair.public_key().serialize(); + public_key.copy_from_slice(&compressed_key[1..]); +} + /// Computes a ECDSA signature over the message `data`. /// /// - `data` is the message hash. diff --git a/extmod/foundation/modfoundation-secp56k1.h b/extmod/foundation/modfoundation-secp56k1.h index b2b7d8398..0f22a1506 100644 --- a/extmod/foundation/modfoundation-secp56k1.h +++ b/extmod/foundation/modfoundation-secp56k1.h @@ -3,6 +3,23 @@ #include "foundation.h" +/// def public_key(secret_key) -> bytes: +STATIC mp_obj_t mod_foundation_secp256k1_public_key_schnorr(mp_obj_t secret_key_obj) +{ + uint8_t public_key[32]; + mp_buffer_info_t secret_key; + + mp_get_buffer_raise(secret_key_obj, &secret_key, MP_BUFFER_READ); + if (secret_key.len != 32) { + mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("secret key should be 32 bytes")); + } + + foundation_secp256k1_public_key_schnorr(secret_key.buf, &public_key); + return mp_obj_new_bytes(public_key, 32); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_foundation_secp256k1_public_key_schnorr_obj, + mod_foundation_secp256k1_public_key_schnorr); + /// def sign_ecdsa(data, secret_key) -> bytes: STATIC mp_obj_t mod_foundation_secp256k1_sign_ecdsa(mp_obj_t data_obj, mp_obj_t secret_key_obj) @@ -62,6 +79,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_foundation_secp256k1_sign_schnorr_obj, mod_foundation_secp256k1_sign_schnorr); STATIC const mp_rom_map_elem_t mod_foundation_secp256k1_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR_public_key_schnorr), MP_ROM_PTR(&mod_foundation_secp256k1_public_key_schnorr_obj) }, { MP_ROM_QSTR(MP_QSTR_sign_ecdsa), MP_ROM_PTR(&mod_foundation_secp256k1_sign_ecdsa_obj) }, { MP_ROM_QSTR(MP_QSTR_sign_schnorr), MP_ROM_PTR(&mod_foundation_secp256k1_sign_schnorr_obj) }, }; From 15d8fae2f17f5daa0f7e70fdd0bf0468fd4e7ff7 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Mon, 13 May 2024 17:30:40 +0200 Subject: [PATCH 012/126] SFT-3618: Use foundation.secp256k1.public_key_schnorr. * ports/stm32/boards/Passport/modules/tasks/nostr_key_task.py (nostr_key_task): Use secp256k1.public_key_schnorr instead of nostr_pubkey_from_pk. * ports/stm32/boards/Passport/modules/utils.py (nostr_pubkey_from_pk): Remove function. --- ports/stm32/boards/Passport/modules/tasks/nostr_key_task.py | 5 +++-- ports/stm32/boards/Passport/modules/utils.py | 5 ----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/tasks/nostr_key_task.py b/ports/stm32/boards/Passport/modules/tasks/nostr_key_task.py index 971fa4c96..b3a10ceac 100644 --- a/ports/stm32/boards/Passport/modules/tasks/nostr_key_task.py +++ b/ports/stm32/boards/Passport/modules/tasks/nostr_key_task.py @@ -6,13 +6,14 @@ async def nostr_key_task(on_done, index): import stash - from utils import nostr_pubkey_from_pk, nostr_nip19_from_key + from foundation import secp256k1 + from utils import nostr_nip19_from_key path = "m/44'/1237'/{}'/0/0".format(index) with stash.SensitiveValues() as sv: node = sv.derive_path(path) key = node.private_key() - pub = nostr_pubkey_from_pk(key) + pub = secp256k1.public_key_schnorr(key) nsec = nostr_nip19_from_key(key, "nsec") npub = nostr_nip19_from_key(pub, "npub") await on_done({'priv': nsec, 'npub': npub, 'pk': key, 'pub': pub}, None) diff --git a/ports/stm32/boards/Passport/modules/utils.py b/ports/stm32/boards/Passport/modules/utils.py index 3ef4370c1..c4f4097e7 100644 --- a/ports/stm32/boards/Passport/modules/utils.py +++ b/ports/stm32/boards/Passport/modules/utils.py @@ -1361,11 +1361,6 @@ def get_seed_from_words(words): return entropy -def nostr_pubkey_from_pk(pk): - from trezorcrypto import secp256k1 - return secp256k1.publickey(pk, True)[1:] - - def nostr_nip19_from_key(key, key_type): # generate nsec/npub import tcc return tcc.codecs.bech32_plain_encode(key_type, key) From ebf73b874a03bcb6496f5b396a0ebaf184a950e7 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Mon, 13 May 2024 18:11:33 +0200 Subject: [PATCH 013/126] SFT-3618: Use foundation.secp256k1.sign_ecdsa. * ports/stm32/boards/Passport/modules/tasks/sign_psbt_task.py (sign_psbt_task): Use foundation.secp256k1.sign_ecdsa. * ports/stm32/boards/Passport/modules/utils.py (sign_message_digest): Ditto. --- .../boards/Passport/modules/tasks/sign_psbt_task.py | 9 ++++----- ports/stm32/boards/Passport/modules/utils.py | 3 ++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/tasks/sign_psbt_task.py b/ports/stm32/boards/Passport/modules/tasks/sign_psbt_task.py index c73e9eb03..f777fb61f 100644 --- a/ports/stm32/boards/Passport/modules/tasks/sign_psbt_task.py +++ b/ports/stm32/boards/Passport/modules/tasks/sign_psbt_task.py @@ -15,7 +15,6 @@ async def sign_psbt_task(on_done, psbt): from errors import Error from utils import keypath_to_str, swab32 from serializations import ser_sig_der - import trezorcrypto import stash import gc from foundation import secp256k1 @@ -122,14 +121,14 @@ async def sign_psbt_task(on_done, psbt): # TODO: handle taproot scripts inp.tap_key_sig = taproot_sign_key(None, pk, inp.sighash, digest) else: - result = trezorcrypto.secp256k1.sign(pk, digest) + result = secp256k1.sign_ecdsa(digest, pk) # convert signature to DER format - if len(result) != 65: + if len(result) != 64: raise AssertionError('Incorrect signature length.') - r = result[1:33] - s = result[33:65] + r = result[0:32] + s = result[32:64] inp.added_sig = (which_key, ser_sig_der(r, s, inp.sighash)) diff --git a/ports/stm32/boards/Passport/modules/utils.py b/ports/stm32/boards/Passport/modules/utils.py index c4f4097e7..b19effaf1 100644 --- a/ports/stm32/boards/Passport/modules/utils.py +++ b/ports/stm32/boards/Passport/modules/utils.py @@ -1039,13 +1039,14 @@ def split_to_lines(s, width): def sign_message_digest(digest, subpath): + from foundation import secp256k1 # do the signature itself! with stash.SensitiveValues() as sv: node = sv.derive_path(subpath) pk = node.private_key() sv.register(pk) - rv = trezorcrypto.secp256k1.sign(pk, digest) + rv = secp256k1.sign_ecdsa(digest, pk) return rv From 0a2c05605ad4943dbb25ea483f1e81437f0abcd7 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Tue, 14 May 2024 16:41:49 +0200 Subject: [PATCH 014/126] SFT-3618: Disable trezorcrypto.secp256k1. * extmod/trezor-firmware/core/embed/extmod/modtrezorcrypto/modtrezorcrypto.c: (modtrezorcrypto-secp256k1.h): Remove inclusion. (mp_module_trezorcrypto_globals_table): Remove MP_QSTR_secp256k1. --- .../core/embed/extmod/modtrezorcrypto/modtrezorcrypto.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/extmod/trezor-firmware/core/embed/extmod/modtrezorcrypto/modtrezorcrypto.c b/extmod/trezor-firmware/core/embed/extmod/modtrezorcrypto/modtrezorcrypto.c index 47f07762b..8acd1d28e 100644 --- a/extmod/trezor-firmware/core/embed/extmod/modtrezorcrypto/modtrezorcrypto.c +++ b/extmod/trezor-firmware/core/embed/extmod/modtrezorcrypto/modtrezorcrypto.c @@ -56,7 +56,9 @@ static void wrapped_ui_wait_callback(uint32_t current, uint32_t total) { #include "modtrezorcrypto-pbkdf2.h" #include "modtrezorcrypto-random.h" #include "modtrezorcrypto-ripemd160.h" +#ifndef FOUNDATION_ADDITIONS #include "modtrezorcrypto-secp256k1.h" +#endif #include "modtrezorcrypto-sha1.h" #include "modtrezorcrypto-sha256.h" #if USE_KECCAK @@ -116,9 +118,9 @@ STATIC const mp_rom_map_elem_t mp_module_trezorcrypto_globals_table[] = { {MP_ROM_QSTR(MP_QSTR_random), MP_ROM_PTR(&mod_trezorcrypto_random_module)}, {MP_ROM_QSTR(MP_QSTR_ripemd160), MP_ROM_PTR(&mod_trezorcrypto_Ripemd160_type)}, +#ifndef FOUNDATION_ADDITIONS {MP_ROM_QSTR(MP_QSTR_secp256k1), MP_ROM_PTR(&mod_trezorcrypto_secp256k1_module)}, -#ifndef FOUNDATION_ADDITIONS {MP_ROM_QSTR(MP_QSTR_bip340), MP_ROM_PTR(&mod_trezorcrypto_bip340_module)}, {MP_ROM_QSTR(MP_QSTR_sha1), MP_ROM_PTR(&mod_trezorcrypto_Sha1_type)}, #endif // FOUNDATION_ADDITIONS From a16a870ad90273e5f4b41b76ab1438959dd94d9a Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Tue, 14 May 2024 16:52:16 +0200 Subject: [PATCH 015/126] SFT-3618: Don't compile unused files. * py/py.mk (PY_EXTMOD_O_BASENAME): Remove extmod/trezor-firmware/crypto/shamir.o and extmod/trezor-firmware/crypto/schnorr.o. --- py/py.mk | 2 -- 1 file changed, 2 deletions(-) diff --git a/py/py.mk b/py/py.mk index a1f3c7ad2..1d39e5437 100644 --- a/py/py.mk +++ b/py/py.mk @@ -349,10 +349,8 @@ PY_EXTMOD_O_BASENAME = \ extmod/trezor-firmware/crypto/aes/aeskey.o \ extmod/trezor-firmware/crypto/aes/aestab.o \ extmod/trezor-firmware/crypto/aes/aes_modes.o \ - extmod/trezor-firmware/crypto/shamir.o \ extmod/trezor-firmware/crypto/groestl.o \ extmod/trezor-firmware/crypto/slip39.o \ - extmod/trezor-firmware/crypto/schnorr.o \ extmod/trezor-firmware/crypto/rand.o \ extmod/trezor-firmware/crypto/rfc6979.o \ extmod/trezor-firmware/crypto/hmac_drbg.o \ From c6c66ef0e3a56a7c19ac35f773ee0278b9b876fc Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Tue, 14 May 2024 17:13:27 +0200 Subject: [PATCH 016/126] SFT-3618: Move FOUNDATION_ADDITIONS definition. * extmod/trezor-firmware/core/embed/extmod/modtrezorcrypto/modtrezorcrypto.c (FOUNDATION_ADDITIONS): Remove definition. * py/py.mk (CFLAGS_MOD): Add -DFOUNDATION_ADDITIONS=1. --- .../core/embed/extmod/modtrezorcrypto/modtrezorcrypto.c | 2 -- py/py.mk | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/extmod/trezor-firmware/core/embed/extmod/modtrezorcrypto/modtrezorcrypto.c b/extmod/trezor-firmware/core/embed/extmod/modtrezorcrypto/modtrezorcrypto.c index 8acd1d28e..f6ce01d25 100644 --- a/extmod/trezor-firmware/core/embed/extmod/modtrezorcrypto/modtrezorcrypto.c +++ b/extmod/trezor-firmware/core/embed/extmod/modtrezorcrypto/modtrezorcrypto.c @@ -34,8 +34,6 @@ static void wrapped_ui_wait_callback(uint32_t current, uint32_t total) { } } -#define FOUNDATION_ADDITIONS - #include "modtrezorcrypto-aes.h" #include "modtrezorcrypto-bip32.h" #include "modtrezorcrypto-bip340.h" diff --git a/py/py.mk b/py/py.mk index 1d39e5437..2e3a7492a 100644 --- a/py/py.mk +++ b/py/py.mk @@ -87,7 +87,8 @@ CFLAGS_MOD += -DUSE_BIP32_25519_CURVES=0 \ -DUSE_BIP39_GENERATE=0 \ -DUSE_BIP32_CACHE=0 \ -DBIP32_CACHE_SIZE=0 \ - -DBIP32_CACHE_MAXDEPTH=0 + -DBIP32_CACHE_MAXDEPTH=0 \ + -DFOUNDATION_ADDITIONS=1 INC += -I$(TOP)/extmod/trezor-firmware/crypto \ -I$(TOP)/extmod/trezor-firmware/crypto/aes \ From 5cb50e8cff04243df306b665b982b88547dfa15b Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Tue, 14 May 2024 17:16:09 +0200 Subject: [PATCH 017/126] SFT-3618: Disable nist256p1 curve. * extmod/trezor-firmware/crypto/bip32.c (get_curve_by_name): Disable getting nist256p1 information to avoid linking. * py/py.mk (PY_EXTMOD_O_BASENAME): Remove extmod/trezor-firmware/crypto/nist256p1.o. --- extmod/trezor-firmware/crypto/bip32.c | 2 ++ py/py.mk | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/extmod/trezor-firmware/crypto/bip32.c b/extmod/trezor-firmware/crypto/bip32.c index d6d99eeb0..0eb04849b 100644 --- a/extmod/trezor-firmware/crypto/bip32.c +++ b/extmod/trezor-firmware/crypto/bip32.c @@ -796,9 +796,11 @@ const curve_info *get_curve_by_name(const char *curve_name) { return &secp256k1_smart_info; } #endif +#ifndef FOUNDATION_ADDITIONS if (strcmp(curve_name, NIST256P1_NAME) == 0) { return &nist256p1_info; } +#endif if (strcmp(curve_name, ED25519_NAME) == 0) { return &ed25519_info; } diff --git a/py/py.mk b/py/py.mk index 2e3a7492a..f84d1b303 100644 --- a/py/py.mk +++ b/py/py.mk @@ -328,7 +328,6 @@ PY_EXTMOD_O_BASENAME = \ extmod/trezor-firmware/crypto/ecdsa.o \ extmod/trezor-firmware/crypto/curves.o \ extmod/trezor-firmware/crypto/secp256k1.o \ - extmod/trezor-firmware/crypto/nist256p1.o \ extmod/trezor-firmware/crypto/memzero.o \ extmod/trezor-firmware/crypto/hmac.o \ extmod/trezor-firmware/crypto/pbkdf2.o \ From c8531e8419cc860ab195a32638dbd91b32e4c398 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Tue, 14 May 2024 17:27:58 +0200 Subject: [PATCH 018/126] SFT-3618: Disable various hash algorithms. * extmod/trezor-firmware/crypto/bip32.c: Avoid using unused curves. * extmod/trezor-firmware/crypto/hasher.c: Disable various hashes. * extmod/trezor-firmware/crypto/hasher.h: Ditto. * extmod/trezor-firmware/crypto/secp256k1.c: Remove unused curves. * py/py.mk (PY_EXTMOD_O_BASENAME): Remove extmod/trezor-firmware/crypto/groestl.o, extmod/trezor-firmware/crypto/blake256.o, extmod/trezor-firmware/crypto/blake2b.o and extmod/trezor-firmware/crypto/blake2s.o. --- extmod/trezor-firmware/crypto/bip32.c | 2 ++ extmod/trezor-firmware/crypto/hasher.c | 6 ++++++ extmod/trezor-firmware/crypto/hasher.h | 2 ++ extmod/trezor-firmware/crypto/secp256k1.c | 2 ++ py/py.mk | 4 ---- 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/extmod/trezor-firmware/crypto/bip32.c b/extmod/trezor-firmware/crypto/bip32.c index 0eb04849b..62656013b 100644 --- a/extmod/trezor-firmware/crypto/bip32.c +++ b/extmod/trezor-firmware/crypto/bip32.c @@ -785,12 +785,14 @@ const curve_info *get_curve_by_name(const char *curve_name) { if (strcmp(curve_name, SECP256K1_NAME) == 0) { return &secp256k1_info; } +#ifndef FOUNDATION_ADDITIONS if (strcmp(curve_name, SECP256K1_DECRED_NAME) == 0) { return &secp256k1_decred_info; } if (strcmp(curve_name, SECP256K1_GROESTL_NAME) == 0) { return &secp256k1_groestl_info; } +#endif #if USE_KECCAK if (strcmp(curve_name, SECP256K1_SMART_NAME) == 0) { return &secp256k1_smart_info; diff --git a/extmod/trezor-firmware/crypto/hasher.c b/extmod/trezor-firmware/crypto/hasher.c index d82a6acc2..166229f50 100644 --- a/extmod/trezor-firmware/crypto/hasher.c +++ b/extmod/trezor-firmware/crypto/hasher.c @@ -49,6 +49,7 @@ void hasher_InitParam(Hasher *hasher, HasherType type, const void *param, #endif sha3_256_Init(&hasher->ctx.sha3); break; +#ifndef FOUNDATION_ADDITIONS case HASHER_BLAKE: case HASHER_BLAKED: case HASHER_BLAKE_RIPEMD: @@ -64,6 +65,7 @@ void hasher_InitParam(Hasher *hasher, HasherType type, const void *param, blake2b_InitPersonal(&hasher->ctx.blake2b, 32, hasher->param, hasher->param_size); break; +#endif } } @@ -89,6 +91,7 @@ void hasher_Update(Hasher *hasher, const uint8_t *data, size_t length) { #endif sha3_Update(&hasher->ctx.sha3, data, length); break; +#ifndef FOUNDATION_ADDITIONS case HASHER_BLAKE: case HASHER_BLAKED: case HASHER_BLAKE_RIPEMD: @@ -101,6 +104,7 @@ void hasher_Update(Hasher *hasher, const uint8_t *data, size_t length) { case HASHER_BLAKE2B_PERSONAL: blake2b_Update(&hasher->ctx.blake2b, data, length); break; +#endif } } @@ -126,6 +130,7 @@ void hasher_Final(Hasher *hasher, uint8_t hash[HASHER_DIGEST_LENGTH]) { keccak_Final(&hasher->ctx.sha3, hash); break; #endif +#ifndef FOUNDATION_ADDITIONS case HASHER_BLAKE: blake256_Final(&hasher->ctx.blake, hash); break; @@ -144,6 +149,7 @@ void hasher_Final(Hasher *hasher, uint8_t hash[HASHER_DIGEST_LENGTH]) { case HASHER_BLAKE2B_PERSONAL: blake2b_Final(&hasher->ctx.blake2b, hash, 32); break; +#endif } } diff --git a/extmod/trezor-firmware/crypto/hasher.h b/extmod/trezor-firmware/crypto/hasher.h index e58e74bae..feb42e8ed 100644 --- a/extmod/trezor-firmware/crypto/hasher.h +++ b/extmod/trezor-firmware/crypto/hasher.h @@ -45,6 +45,7 @@ typedef enum { HASHER_SHA3K, #endif +#ifndef FOUNDATION_ADDITIONS HASHER_BLAKE, HASHER_BLAKED, HASHER_BLAKE_RIPEMD, @@ -53,6 +54,7 @@ typedef enum { HASHER_BLAKE2B, HASHER_BLAKE2B_PERSONAL, +#endif } HasherType; typedef struct { diff --git a/extmod/trezor-firmware/crypto/secp256k1.c b/extmod/trezor-firmware/crypto/secp256k1.c index 38c3fc0e2..5eece44bc 100644 --- a/extmod/trezor-firmware/crypto/secp256k1.c +++ b/extmod/trezor-firmware/crypto/secp256k1.c @@ -66,6 +66,7 @@ const curve_info secp256k1_info = { .hasher_script = HASHER_SHA2, }; +#ifndef FOUNDATION_ADDITIONS const curve_info secp256k1_decred_info = { .bip32_name = "Bitcoin seed", .params = &secp256k1, @@ -83,6 +84,7 @@ const curve_info secp256k1_groestl_info = { .hasher_pubkey = HASHER_SHA2_RIPEMD, .hasher_script = HASHER_SHA2, }; +#endif #if USE_KECCAK const curve_info secp256k1_smart_info = { diff --git a/py/py.mk b/py/py.mk index f84d1b303..02560a21c 100644 --- a/py/py.mk +++ b/py/py.mk @@ -342,14 +342,10 @@ PY_EXTMOD_O_BASENAME = \ extmod/trezor-firmware/crypto/sha2.o \ extmod/trezor-firmware/crypto/sha3.o \ extmod/trezor-firmware/crypto/hasher.o \ - extmod/trezor-firmware/crypto/blake256.o \ - extmod/trezor-firmware/crypto/blake2b.o \ - extmod/trezor-firmware/crypto/blake2s.o \ extmod/trezor-firmware/crypto/aes/aescrypt.o \ extmod/trezor-firmware/crypto/aes/aeskey.o \ extmod/trezor-firmware/crypto/aes/aestab.o \ extmod/trezor-firmware/crypto/aes/aes_modes.o \ - extmod/trezor-firmware/crypto/groestl.o \ extmod/trezor-firmware/crypto/slip39.o \ extmod/trezor-firmware/crypto/rand.o \ extmod/trezor-firmware/crypto/rfc6979.o \ From d8e22953c6896be2a7cebdce7c6ef99e09880da7 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Tue, 14 May 2024 17:36:51 +0200 Subject: [PATCH 019/126] SFT-3618: Disable secp256k1 precomputed table. * py/py.mk (CFLAGS_MOD): Add -DUSE_PRECOMPUTED_CP=0. --- py/py.mk | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/py/py.mk b/py/py.mk index 02560a21c..25502aade 100644 --- a/py/py.mk +++ b/py/py.mk @@ -88,7 +88,8 @@ CFLAGS_MOD += -DUSE_BIP32_25519_CURVES=0 \ -DUSE_BIP32_CACHE=0 \ -DBIP32_CACHE_SIZE=0 \ -DBIP32_CACHE_MAXDEPTH=0 \ - -DFOUNDATION_ADDITIONS=1 + -DFOUNDATION_ADDITIONS=1 \ + -DUSE_PRECOMPUTED_CP=0 INC += -I$(TOP)/extmod/trezor-firmware/crypto \ -I$(TOP)/extmod/trezor-firmware/crypto/aes \ From f02b843decd793087d1fe4321ccb46a353448104 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Tue, 14 May 2024 17:45:34 +0200 Subject: [PATCH 020/126] SFT-3618: Use lowmemory for secp256k1. * extmod/foundation-rust/Cargo.toml (dependencies) : Enable lowmemory feature. --- extmod/foundation-rust/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extmod/foundation-rust/Cargo.toml b/extmod/foundation-rust/Cargo.toml index 6117bd93d..d9ed8178d 100644 --- a/extmod/foundation-rust/Cargo.toml +++ b/extmod/foundation-rust/Cargo.toml @@ -46,7 +46,7 @@ features = ["critical-section"] [dependencies.secp256k1] version = "0.29" default-features = false -features = ["rand"] +features = ["lowmemory", "rand"] [dependencies.rand] version = "0.8.5" From f11cacb5f4661b87617e4e03edf5fdb14e06f2bd Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Tue, 14 May 2024 17:51:33 +0200 Subject: [PATCH 021/126] SFT-3618: Enable small-hash in bitcoin_hashes. * extmod/foundation-rust/Cargo.lock: Update lockfile. * extmod/foundation-rust/Cargo.toml (dependencies) : New dependency, enable small-hash. --- extmod/foundation-rust/Cargo.lock | 1 + extmod/foundation-rust/Cargo.toml | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/extmod/foundation-rust/Cargo.lock b/extmod/foundation-rust/Cargo.lock index d6084cc39..9251698a6 100644 --- a/extmod/foundation-rust/Cargo.lock +++ b/extmod/foundation-rust/Cargo.lock @@ -105,6 +105,7 @@ dependencies = [ name = "foundation" version = "0.1.0" dependencies = [ + "bitcoin_hashes", "cortex-m", "critical-section", "foundation-ur", diff --git a/extmod/foundation-rust/Cargo.toml b/extmod/foundation-rust/Cargo.toml index d9ed8178d..f9d474ac7 100644 --- a/extmod/foundation-rust/Cargo.toml +++ b/extmod/foundation-rust/Cargo.toml @@ -11,6 +11,11 @@ license = "GPL-3.0-or-later" name = "sizes" required-features = ["std"] +[dependencies.bitcoin_hashes] +version = "0.13" +features = ["small-hash"] +default-features = false + [dependencies.minicbor] version = "0.20" default-features = false From ee1578d77a9c0a1155e1b7c001708ba61ac3a312 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Tue, 21 May 2024 16:15:52 +0200 Subject: [PATCH 022/126] SFT-3656: Fix Rust compilation issues. * src/secp256k1.rs (secp256k1_public_key_schnorr): Use Keypair instead of KeyPair. (secp256k1_sign_ecdsa): Use Message::from_digest_slice. --- extmod/foundation-rust/src/secp256k1.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extmod/foundation-rust/src/secp256k1.rs b/extmod/foundation-rust/src/secp256k1.rs index ef6a929b6..10982c081 100644 --- a/extmod/foundation-rust/src/secp256k1.rs +++ b/extmod/foundation-rust/src/secp256k1.rs @@ -4,7 +4,7 @@ use core::ptr; use once_cell::sync::Lazy; use secp256k1::{ - ffi::types::AlignedType, AllPreallocated, KeyPair, Message, Secp256k1, + ffi::types::AlignedType, AllPreallocated, Keypair, Message, Secp256k1, SecretKey, }; @@ -35,7 +35,7 @@ pub extern "C" fn secp256k1_public_key_schnorr( secret_key: &[u8; 32], public_key: &mut [u8; 32], ) { - let keypair = KeyPair::from_seckey_slice(&PRE_ALLOCATED_CTX, secret_key) + let keypair = Keypair::from_seckey_slice(&PRE_ALLOCATED_CTX, secret_key) .expect("invalid secret key"); let compressed_key = keypair.public_key().serialize(); public_key.copy_from_slice(&compressed_key[1..]); @@ -55,7 +55,7 @@ pub extern "C" fn secp256k1_sign_ecdsa( let secret_key = SecretKey::from_slice(secret_key).expect("invalid secret key"); - let msg = Message::from_slice(data).unwrap(); + let msg = Message::from_digest_slice(data).unwrap(); let sig = PRE_ALLOCATED_CTX.sign_ecdsa(&msg, &secret_key); signature.copy_from_slice(&sig.serialize_compact()); } From 8bb2889be48160f0734c49233ba52fda3954b72e Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Thu, 8 Feb 2024 13:31:20 +0100 Subject: [PATCH 023/126] SFT-3648: Remove unused targets on simulator. * simulator/Makefile (CC_UNIX_TOP): Remove variable. (up): Remove target. (dfu): Ditto. (tags): Ditto. (tools): Ditto. (setup): Ditto. Signed-off-by: Jean-Pierre De Jesus DIAZ --- simulator/Makefile | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/simulator/Makefile b/simulator/Makefile index d530d7862..6eb7b6f44 100644 --- a/simulator/Makefile +++ b/simulator/Makefile @@ -12,7 +12,6 @@ # MPY_TOP = ../ PORT_TOP = $(MPY_TOP)/ports/unix -CC_UNIX_TOP = $(PWD) V=0 LV_CFLAGS_COLOR="-DLV_COLOR_DEPTH=16 -DHAS_FUEL_GAUGE -DSCREEN_MODE_COLOR" LV_CFLAGS_MONO="-DLV_COLOR_DEPTH=16 -DSCREEN_MODE_MONO" @@ -25,7 +24,7 @@ ifeq ($(UNAME_S),Darwin) export PKG_CONFIG_PATH=/usr/local/opt/libffi/lib/pkgconfig endif -MAKE_ARGS = -j 4 VARIANT=$(VARIANT) VARIANT_DIR=$(VARIANT_DIR) CC_UNIX_TOP=$(CC_UNIX_TOP) PROG=passport-mpy V=$(V) DEBUG=1 +MAKE_ARGS = -j 4 VARIANT=$(VARIANT) VARIANT_DIR=$(VARIANT_DIR) PROG=passport-mpy V=$(V) DEBUG=1 all: @echo "Run 'make mono' or 'make color'" @@ -38,26 +37,3 @@ color: clean: cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS) clean - -up: - cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS) deploy-stlink - -dfu: - cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS) deploy - -tags: - cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS) tags - -# Make a vanilla copy of micropython (unix) for use with upip and comparsions purposes -tools: - cd $(PORT_TOP) && $(MAKE) clean deplibs all - cp $(PORT_TOP)/micropython ./micropython - cd $(PORT_TOP) && $(MAKE) clean - -setup: - -ln -s $(PORT_TOP) l-port - -ln -s $(MPY_TOP) l-mpy - -ln -s $(PORT_TOP)/passport-mpy passport-mpy - # cd $(PORT_TOP) ; git submodule update --init - $(MAKE) tools - @echo Setup DONE From da95d35095eaf6aa5b16958d34d4198031c928f4 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Mon, 20 May 2024 15:01:05 +0200 Subject: [PATCH 024/126] SFT-3648: Fix unix port warning on newer compiler. * ports/unix/main.c (mp_import_stat): Fix function signature. --- ports/unix/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/unix/main.c b/ports/unix/main.c index 8db496ca0..ef9b4b116 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -726,7 +726,7 @@ MP_NOINLINE int main_(int argc, char **argv) { } #if !MICROPY_VFS -uint mp_import_stat(const char *path) { +mp_import_stat_t mp_import_stat(const char *path) { struct stat st; if (stat(path, &st) == 0) { if (S_ISDIR(st.st_mode)) { From bee8b541d19c65dbc6f596500bb2675eec923e18 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Wed, 15 May 2024 18:34:33 +0200 Subject: [PATCH 025/126] SFT-3632: Update Cargo dependencies. * extmod/foundation-rust/Cargo.lock: Regenerate file. * extmod/foundation-rust/Cargo.toml (dependencies) : Update to 0.2. : Update to 0.3. : Update to 0.24. --- extmod/foundation-rust/Cargo.lock | 85 ++++++++++++++++++------------- extmod/foundation-rust/Cargo.toml | 6 +-- 2 files changed, 52 insertions(+), 39 deletions(-) diff --git a/extmod/foundation-rust/Cargo.lock b/extmod/foundation-rust/Cargo.lock index 9251698a6..0d322ba08 100644 --- a/extmod/foundation-rust/Cargo.lock +++ b/extmod/foundation-rust/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "bare-metal" version = "0.2.5" @@ -24,7 +30,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" dependencies = [ "bitcoin-internals", - "hex-conservative", + "hex-conservative 0.1.2", +] + +[[package]] +name = "bitcoin_hashes" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" +dependencies = [ + "hex-conservative 0.2.0", ] [[package]] @@ -41,9 +56,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" +checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" [[package]] name = "cfg-if" @@ -87,9 +102,9 @@ checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" [[package]] name = "either" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "embedded-hal" @@ -105,7 +120,7 @@ dependencies = [ name = "foundation" version = "0.1.0" dependencies = [ - "bitcoin_hashes", + "bitcoin_hashes 0.13.0", "cortex-m", "critical-section", "foundation-ur", @@ -127,11 +142,11 @@ checksum = "7e6bdea1eeaa5edb5355234d02f81c6dbdd4bc5146887990dd29a213029bbeae" [[package]] name = "foundation-ur" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17beecc12e50f5a410dfa764b937bc043b4b3f4ec6fe861746a4f8db56638b5" +checksum = "a803ede27fdd585c226d4d70d3da984f5ed01efeafeed1a1eea8bc62e8c2e622" dependencies = [ - "bitcoin_hashes", + "bitcoin_hashes 0.14.0", "crc", "heapless", "itertools", @@ -142,9 +157,9 @@ dependencies = [ [[package]] name = "foundation-urtypes" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f381636222dfe7a832f140a4ca1945edac51754b65896e172dc6fe2e3bd9732" +checksum = "22a59d9988ac2826c9c49d02364b907ee6dfc2385ddf0dcbc8c76e43532fcf87" dependencies = [ "foundation-arena", "heapless", @@ -191,9 +206,18 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hex-conservative" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ed443af458ccb6d81c1e7e661545f94d3176752fb1df2f543b902a1e0f51e2" +checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20" + +[[package]] +name = "hex-conservative" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1aa273bf451e37ed35ced41c71a5e2a4e29064afb104158f2514bcd71c2c986" +dependencies = [ + "arrayvec", +] [[package]] name = "itertools" @@ -206,28 +230,28 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.154" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "minicbor" -version = "0.20.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d15f4203d71fdf90903c2696e55426ac97a363c67b218488a73b534ce7aca10" +checksum = "7a1359d4b3ec786cdc1e44382c8c0e10965d95a173059590f62314c73be72969" dependencies = [ "minicbor-derive", ] [[package]] name = "minicbor-derive" -version = "0.13.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1154809406efdb7982841adb6311b3d095b46f78342dd646736122fe6b19e267" +checksum = "a6bdc119b1a405df86a8cde673295114179dbd0ebe18877c26ba89fb080365c2" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] @@ -285,7 +309,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.61", + "syn", ] [[package]] @@ -311,9 +335,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" dependencies = [ "unicode-ident", ] @@ -423,20 +447,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[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.61" +version = "2.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" +checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" dependencies = [ "proc-macro2", "quote", diff --git a/extmod/foundation-rust/Cargo.toml b/extmod/foundation-rust/Cargo.toml index f9d474ac7..1d82e95bc 100644 --- a/extmod/foundation-rust/Cargo.toml +++ b/extmod/foundation-rust/Cargo.toml @@ -17,7 +17,7 @@ features = ["small-hash"] default-features = false [dependencies.minicbor] -version = "0.20" +version = "0.24" default-features = false [dependencies.heapless] @@ -29,11 +29,11 @@ version = "1" default-features = false [dependencies.foundation-ur] -version = "0.1" +version = "0.2" default-features = false [dependencies.foundation-urtypes] -version = "0.2" +version = "0.3" default-features = false [target.'cfg(target_arch = "arm")'.dependencies.cortex-m] From 5e7f3ef07961df9d926da7a976e4488040161d3c Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Thu, 20 Jun 2024 22:09:42 -0400 Subject: [PATCH 026/126] SFT-2998: fix for firmware updating failures --- extmod/vfs_blockdev.c | 9 +++++++-- lib/oofatfs/ff.c | 2 +- .../STM32H7xx_HAL_Driver/Src/stm32h7xx_ll_sdmmc.c | 7 +++---- ports/stm32/boards/Passport/mpconfigboard.h | 12 +++++++++++- ports/stm32/sdcard.c | 13 +++++++------ 5 files changed, 29 insertions(+), 14 deletions(-) diff --git a/extmod/vfs_blockdev.c b/extmod/vfs_blockdev.c index 57c83b428..0bd098ec6 100644 --- a/extmod/vfs_blockdev.c +++ b/extmod/vfs_blockdev.c @@ -54,8 +54,13 @@ int mp_vfs_blockdev_read(mp_vfs_blockdev_t *self, size_t block_num, size_t num_b mp_obj_array_t ar = {{&mp_type_bytearray}, BYTEARRAY_TYPECODE, 0, num_blocks *self->block_size, buf}; self->readblocks[2] = MP_OBJ_NEW_SMALL_INT(block_num); self->readblocks[3] = MP_OBJ_FROM_PTR(&ar); - mp_call_method_n_kw(2, 0, self->readblocks); - // TODO handle error return + mp_obj_t res = mp_call_method_n_kw(2, 0, self->readblocks); + + // readblocks returns true for success + bool err = !mp_obj_is_true(res); + if (err) { + return 1; + } return 0; } } diff --git a/lib/oofatfs/ff.c b/lib/oofatfs/ff.c index fad1f8f21..4c8a529ad 100644 --- a/lib/oofatfs/ff.c +++ b/lib/oofatfs/ff.c @@ -3617,7 +3617,7 @@ FRESULT f_read ( FATFS *fs; DWORD clst, sect; FSIZE_t remain; - UINT rcnt, cc, csect; + UINT rcnt = 0, cc = 0, csect = 0; BYTE *rbuff = (BYTE*)buff; diff --git a/lib/stm32lib/STM32H7xx_HAL_Driver/Src/stm32h7xx_ll_sdmmc.c b/lib/stm32lib/STM32H7xx_HAL_Driver/Src/stm32h7xx_ll_sdmmc.c index 5d39b9f28..cd27a84a5 100644 --- a/lib/stm32lib/STM32H7xx_HAL_Driver/Src/stm32h7xx_ll_sdmmc.c +++ b/lib/stm32lib/STM32H7xx_HAL_Driver/Src/stm32h7xx_ll_sdmmc.c @@ -1243,16 +1243,15 @@ static uint32_t SDMMC_GetCmdResp1(SDMMC_TypeDef *SDMMCx, uint8_t SD_CMD, uint32_ return SDMMC_ERROR_TIMEOUT; } sta_reg = SDMMCx->STA; - }while(((sta_reg & (SDMMC_FLAG_CCRCFAIL | SDMMC_FLAG_CMDREND | SDMMC_FLAG_CTIMEOUT | SDMMC_FLAG_BUSYD0END)) == 0U) || + }while(((sta_reg & (SDMMC_FLAG_CCRCFAIL | SDMMC_FLAG_CMDREND /*| SDMMC_FLAG_CTIMEOUT*/ | SDMMC_FLAG_BUSYD0END)) == 0U) || ((sta_reg & SDMMC_FLAG_CMDACT) != 0U )); if(__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CTIMEOUT)) { __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CTIMEOUT); - - return SDMMC_ERROR_CMD_RSP_TIMEOUT; } - else if(__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CCRCFAIL)) + + if(__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CCRCFAIL)) { __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CCRCFAIL); diff --git a/ports/stm32/boards/Passport/mpconfigboard.h b/ports/stm32/boards/Passport/mpconfigboard.h index 6045ea126..77556431f 100644 --- a/ports/stm32/boards/Passport/mpconfigboard.h +++ b/ports/stm32/boards/Passport/mpconfigboard.h @@ -108,7 +108,7 @@ void Passport_board_init(void); // SD card detect switch #define MICROPY_HW_SDCARD_DETECT_PIN (pin_E3) -#define MICROPY_HW_SDCARD_DETECT_PULL (GPIO_PULLUP) +#define MICROPY_HW_SDCARD_DETECT_PULL (GPIO_NOPULL) #define MICROPY_HW_SDCARD_DETECT_PRESENT (GPIO_PIN_RESET) // BQ27520 fuel gauge @@ -118,3 +118,13 @@ void Passport_board_init(void); // LCD Teariing Effect output line #define MICROPY_HW_LCD_TE_PIN (pin_B5) #define MICROPY_HW_LCD_TE_PULL (GPIO_NOPULL) + +// SD/MMC card driver interface bus width (defaults to 4 bits) +#ifndef MICROPY_HW_SDCARD_BUS_WIDTH +#define MICROPY_HW_SDCARD_BUS_WIDTH (1) +#endif + +// Whether to automatically mount (and boot from) the SD card if it's present +#ifndef MICROPY_HW_SDCARD_MOUNT_AT_BOOT +#define MICROPY_HW_SDCARD_MOUNT_AT_BOOT (0) +#endif diff --git a/ports/stm32/sdcard.c b/ports/stm32/sdcard.c index ea69e7285..79f9713bd 100644 --- a/ports/stm32/sdcard.c +++ b/ports/stm32/sdcard.c @@ -159,13 +159,14 @@ void sdcard_init(void) { // Note: the mp_hal_pin_config function will configure the GPIO in // fast mode which can do up to 50MHz. This should be plenty for SDIO // which clocks up to 25MHz maximum. - mp_hal_pin_config_alt_static(MICROPY_HW_SDCARD_CK, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDCARD_CK); - mp_hal_pin_config_alt_static(MICROPY_HW_SDCARD_CMD, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDCARD_CMD); - mp_hal_pin_config_alt_static(MICROPY_HW_SDCARD_D0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDCARD_D0); + + mp_hal_pin_config_alt_static_speed(MICROPY_HW_SDCARD_CK, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_SDCARD_CK); + mp_hal_pin_config_alt_static_speed(MICROPY_HW_SDCARD_CMD, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_SDCARD_CMD); + mp_hal_pin_config_alt_static_speed(MICROPY_HW_SDCARD_D0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_SDCARD_D0); #if MICROPY_HW_SDCARD_BUS_WIDTH == 4 - mp_hal_pin_config_alt_static(MICROPY_HW_SDCARD_D1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDCARD_D1); - mp_hal_pin_config_alt_static(MICROPY_HW_SDCARD_D2, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDCARD_D2); - mp_hal_pin_config_alt_static(MICROPY_HW_SDCARD_D3, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDCARD_D3); + mp_hal_pin_config_alt_static_speed(MICROPY_HW_SDCARD_D1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_SDCARD_D1); + mp_hal_pin_config_alt_static_speed(MICROPY_HW_SDCARD_D2, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_SDCARD_D2); + mp_hal_pin_config_alt_static_speed(MICROPY_HW_SDCARD_D3, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_SDCARD_D3); #endif // configure the SD card detect pin From 71cad9c046059df91b6fb49ad56fa60c642493e7 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Thu, 20 Jun 2024 22:18:12 -0400 Subject: [PATCH 027/126] SFT-3235: enabled taproot for btcpay --- ports/stm32/boards/Passport/modules/wallets/btcpay.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/wallets/btcpay.py b/ports/stm32/boards/Passport/modules/wallets/btcpay.py index c67f78856..bacde003b 100644 --- a/ports/stm32/boards/Passport/modules/wallets/btcpay.py +++ b/ports/stm32/boards/Passport/modules/wallets/btcpay.py @@ -19,6 +19,6 @@ {'id': 'microsd', 'label': 'microSD', 'filename_pattern': '{xfp}-btcpay.json', 'filename_pattern_multisig': '{xfp}-btcpay-multisig.json'} ], - # 'select_addr_type': True, - # 'addr_options': [AF_P2WPKH, AF_P2TR], + 'select_addr_type': True, + 'addr_options': [AF_P2WPKH, AF_P2TR], } From 551b915cc30216e36f00c15d852d4b8b8b0adb1a Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Wed, 27 Mar 2024 09:48:08 -0400 Subject: [PATCH 028/126] SFT-3499: moved all calculation inside try block for sha256 --- .../Passport/modules/tasks/calculate_file_sha256_task.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/tasks/calculate_file_sha256_task.py b/ports/stm32/boards/Passport/modules/tasks/calculate_file_sha256_task.py index 5f7d840c9..9fd40c775 100644 --- a/ports/stm32/boards/Passport/modules/tasks/calculate_file_sha256_task.py +++ b/ports/stm32/boards/Passport/modules/tasks/calculate_file_sha256_task.py @@ -52,6 +52,9 @@ async def calculate_file_sha256_task(file_path, on_progress, on_done): on_progress(percent) await sleep_ms(1) + # Calculate the digests and convert it to a string + digest = B2A(chk.digest()) + except CardMissingError: await on_done(None, Error.MICROSD_CARD_MISSING) return @@ -59,7 +62,4 @@ async def calculate_file_sha256_task(file_path, on_progress, on_done): await on_done(None, Error.FILE_READ_ERROR) return - # Calculate the digests and convert it to a string - digest = B2A(chk.digest()) - await on_done(digest, None) From 589e89123a22ebb54c7fdf23ec53dc52a77b7888 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Thu, 30 May 2024 18:20:37 -0400 Subject: [PATCH 029/126] SFT-3670: added theya single-sig with firmware version info --- .../boards/Passport/modules/wallets/generic_json_wallet.py | 6 +++++- ports/stm32/boards/Passport/modules/wallets/theya.py | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ports/stm32/boards/Passport/modules/wallets/generic_json_wallet.py b/ports/stm32/boards/Passport/modules/wallets/generic_json_wallet.py index 1e91c6ce8..e9a01c711 100644 --- a/ports/stm32/boards/Passport/modules/wallets/generic_json_wallet.py +++ b/ports/stm32/boards/Passport/modules/wallets/generic_json_wallet.py @@ -14,7 +14,7 @@ import ujson import stash from utils import xfp2str, to_str -from common import settings +from common import settings, system from public_constants import AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH, AF_P2WSH_P2SH, AF_P2WSH, AF_P2TR from data_codecs.qr_type import QRType from foundation import ur @@ -68,6 +68,10 @@ def create_generic_json_wallet(sw_wallet=None, if zp: rv[name]['_pub'] = zp + if sw_wallet.get('show_fw_version', False): + version = system.get_software_info()[0] + rv['fw_version'] = version + msg = ujson.dumps(rv) if export_mode == 'qr' and qr_type == QRType.UR2: diff --git a/ports/stm32/boards/Passport/modules/wallets/theya.py b/ports/stm32/boards/Passport/modules/wallets/theya.py index 5c5c381e2..397e04dd6 100644 --- a/ports/stm32/boards/Passport/modules/wallets/theya.py +++ b/ports/stm32/boards/Passport/modules/wallets/theya.py @@ -6,11 +6,13 @@ from .multisig_import import read_multisig_config_from_qr, read_multisig_config_from_microsd from .multisig_json import create_multisig_json_wallet +from .generic_json_wallet import create_generic_json_wallet from data_codecs.qr_type import QRType TheyaWallet = { 'label': 'Theya', 'sig_types': [ + {'id': 'single-sig', 'label': 'Single-sig', 'addr_type': None, 'create_wallet': create_generic_json_wallet}, {'id': 'multisig', 'label': 'Multisig', 'addr_type': None, 'create_wallet': create_multisig_json_wallet, 'import_qr': read_multisig_config_from_qr, 'import_microsd': read_multisig_config_from_microsd} ], From 47e7d9f1794967236869f8abf2f0461b51416b64 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Fri, 21 Jun 2024 13:03:39 -0400 Subject: [PATCH 030/126] SFT-3670: changed "show_fw_version" to "export_fw_version" --- .../boards/Passport/modules/wallets/generic_json_wallet.py | 2 +- ports/stm32/boards/Passport/modules/wallets/multisig_json.py | 2 +- ports/stm32/boards/Passport/modules/wallets/theya.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/wallets/generic_json_wallet.py b/ports/stm32/boards/Passport/modules/wallets/generic_json_wallet.py index e9a01c711..2dbb279e7 100644 --- a/ports/stm32/boards/Passport/modules/wallets/generic_json_wallet.py +++ b/ports/stm32/boards/Passport/modules/wallets/generic_json_wallet.py @@ -68,7 +68,7 @@ def create_generic_json_wallet(sw_wallet=None, if zp: rv[name]['_pub'] = zp - if sw_wallet.get('show_fw_version', False): + if sw_wallet.get('export_fw_version', False): version = system.get_software_info()[0] rv['fw_version'] = version diff --git a/ports/stm32/boards/Passport/modules/wallets/multisig_json.py b/ports/stm32/boards/Passport/modules/wallets/multisig_json.py index 17b4e09f6..127efdabf 100644 --- a/ports/stm32/boards/Passport/modules/wallets/multisig_json.py +++ b/ports/stm32/boards/Passport/modules/wallets/multisig_json.py @@ -51,7 +51,7 @@ def create_multisig_json_wallet(sw_wallet=None, # e.g., AF_P2WSH_P2SH: {'deriv':m/48'/0'/4'/1', 'acct': 4} accts.append({'fmt': fmt, 'deriv': dd, 'acct': acct_num}) - if sw_wallet.get('show_fw_version', False): + if sw_wallet.get('export_fw_version', False): version = system.get_software_info()[0] fp.write(' "fw_version": "%s",\n' % version) diff --git a/ports/stm32/boards/Passport/modules/wallets/theya.py b/ports/stm32/boards/Passport/modules/wallets/theya.py index 397e04dd6..343a9af2c 100644 --- a/ports/stm32/boards/Passport/modules/wallets/theya.py +++ b/ports/stm32/boards/Passport/modules/wallets/theya.py @@ -21,7 +21,7 @@ {'id': 'microsd', 'label': 'microSD', 'filename_pattern': '{xfp}-theya.json', 'filename_pattern_multisig': '{xfp}-theya-multisig.json'} ], - 'show_fw_version': True, + 'export_fw_version': True, # 'skip_address_validation': True, # 'skip_multisig_import': True, # 'force_multisig_policy': True From 6c113fd8550730fada47ff1fc04e533734ef455c Mon Sep 17 00:00:00 2001 From: mjg-foundation <114431859+mjg-foundation@users.noreply.github.com> Date: Fri, 21 Jun 2024 13:49:38 -0400 Subject: [PATCH 031/126] Update version.txt to 2.3.2b1 --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index 2bf1c1ccf..86806dec3 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -2.3.1 +2.3.2b1 From 5f08c83ceca743b34569dcf7cab0710cd03cf2c2 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Fri, 21 Jun 2024 16:58:34 -0400 Subject: [PATCH 032/126] SFT-3442: erased device name on reset, needs testing --- ports/stm32/boards/Passport/modules/tasks/erase_passport_task.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/stm32/boards/Passport/modules/tasks/erase_passport_task.py b/ports/stm32/boards/Passport/modules/tasks/erase_passport_task.py index 14fa80f67..5cc5ad222 100644 --- a/ports/stm32/boards/Passport/modules/tasks/erase_passport_task.py +++ b/ports/stm32/boards/Passport/modules/tasks/erase_passport_task.py @@ -32,6 +32,7 @@ async def erase_passport_task(on_done, full_reset): settings.remove_regex("^ext\\.*") settings.remove('next_addrs') settings.remove('derived_keys') + settings.remove('device_name') for key in saved_flow_keys: settings.remove(key) From 7c291dd2fc8764ff9fe9aa9c3838ffb1fe85f4f8 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Wed, 3 Jul 2024 18:45:12 -0400 Subject: [PATCH 033/126] SFT-3694: doubled prediction list --- extmod/foundation/modfoundation-bip39.h | 4 ++-- .../Passport/modules/pages/predictive_text_input_page.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/extmod/foundation/modfoundation-bip39.h b/extmod/foundation/modfoundation-bip39.h index 3e69ead99..feb823990 100644 --- a/extmod/foundation/modfoundation-bip39.h +++ b/extmod/foundation/modfoundation-bip39.h @@ -11,7 +11,7 @@ #include "bip39.h" #include "bip39_utils.h" -#define MATCHES_LEN 80 +#define MATCHES_LEN 160 extern word_info_t bip39_word_info[]; extern word_info_t bytewords_word_info[]; // TODO: Restructure this so bip39 and bytewords are separate @@ -95,4 +95,4 @@ STATIC MP_DEFINE_CONST_DICT(mod_foundation_bip39_globals, mod_foundation_bip39_g STATIC const mp_obj_module_t mod_foundation_bip39_module = { .base = {&mp_type_module}, .globals = (mp_obj_dict_t *)&mod_foundation_bip39_globals, -}; \ No newline at end of file +}; diff --git a/ports/stm32/boards/Passport/modules/pages/predictive_text_input_page.py b/ports/stm32/boards/Passport/modules/pages/predictive_text_input_page.py index 91ccbbe5f..84c1c4e67 100644 --- a/ports/stm32/boards/Passport/modules/pages/predictive_text_input_page.py +++ b/ports/stm32/boards/Passport/modules/pages/predictive_text_input_page.py @@ -96,7 +96,8 @@ def update_predictions(self): if len(prefix) > 0: # print('Lookup words for {}'.format(prefix)) set_list(self.prefixes, self.word_idx, prefix) - self.predictions = get_words_matching_prefix(prefix, max=5, word_list=self.word_list) + self.predictions = get_words_matching_prefix(prefix, max=10, word_list=self.word_list) + print("len(predictions): {}".format(len(self.predictions))) elif self.word_idx == self.total_words - 1: self.predictions = [RANDOM_WORD_STRING] else: From fe3081365e96382885d4b31eb6bc034eacae4366 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Thu, 4 Jul 2024 16:47:55 -0400 Subject: [PATCH 034/126] SFT-2988: electrum format message signing --- .../modtrezorcrypto/modtrezorcrypto-ecdsa.h | 67 +++++++++++++ ports/stm32/boards/Passport/manifest.py | 2 + .../boards/Passport/modules/flows/__init__.py | 1 + .../flows/sign_electrum_message_flow.py | 98 +++++++++++++++++++ ports/stm32/boards/Passport/modules/menus.py | 20 +++- .../boards/Passport/modules/tasks/__init__.py | 1 + .../modules/tasks/sign_text_file_task.py | 10 +- .../tasks/validate_electrum_message_task.py | 41 ++++++++ ports/stm32/boards/Passport/modules/utils.py | 38 ++++--- .../boards/Passport/modules/wallets/utils.py | 20 ++-- 10 files changed, 272 insertions(+), 26 deletions(-) create mode 100644 ports/stm32/boards/Passport/modules/flows/sign_electrum_message_flow.py create mode 100644 ports/stm32/boards/Passport/modules/tasks/validate_electrum_message_task.py diff --git a/extmod/trezor-firmware/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-ecdsa.h b/extmod/trezor-firmware/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-ecdsa.h index 6ae8013d1..e518f72ca 100644 --- a/extmod/trezor-firmware/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-ecdsa.h +++ b/extmod/trezor-firmware/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-ecdsa.h @@ -27,6 +27,10 @@ #include "secp256k1.h" #include "bignum.h" +#ifdef USE_SECP256K1_ZKP_ECDSA +#include "zkp_ecdsa.h" +#endif + /// package: trezorcrypto.ecdsa @@ -118,12 +122,75 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( 4, mod_trezorcrypto_ecdsa_point_add); +/// def sign( +/// secret_key: bytes, +/// digest: bytes, +/// compressed: bool = True, +/// canonical: int | None = None, +/// ) -> bytes: +/// """ +/// Uses secret key to produce the signature of the digest. +/// """ +STATIC mp_obj_t mod_trezorcrypto_ecdsa_sign(size_t n_args, + const mp_obj_t *args) { + mp_buffer_info_t sk = {0}; + mp_buffer_info_t dig = {0}; + mp_get_buffer_raise(args[0], &sk, MP_BUFFER_READ); + mp_get_buffer_raise(args[1], &dig, MP_BUFFER_READ); + bool compressed = (n_args < 3) || (args[2] == mp_const_true); + int (*is_canonical)(uint8_t by, uint8_t sig[64]) = NULL; +#if !BITCOIN_ONLY + mp_int_t canonical = (n_args > 3) ? mp_obj_get_int(args[3]) : 0; + switch (canonical) { + case CANONICAL_SIG_ETHEREUM: + is_canonical = ethereum_is_canonical; + break; + case CANONICAL_SIG_EOS: + is_canonical = eos_is_canonical; + break; + } +#endif + if (sk.len != 32) { + mp_raise_ValueError(MP_ERROR_TEXT("Invalid length of secret key")); + } + if (dig.len != 32) { + mp_raise_ValueError(MP_ERROR_TEXT("Invalid length of digest")); + } + vstr_t sig = {0}; + vstr_init_len(&sig, 65); + uint8_t pby = 0; + int ret = 0; +#ifdef USE_SECP256K1_ZKP_ECDSA + if (!is_canonical) { + ret = zkp_ecdsa_sign_digest(&secp256k1, (const uint8_t *)sk.buf, + (const uint8_t *)dig.buf, + (uint8_t *)sig.buf + 1, &pby, is_canonical); + } else +#endif + { + ret = ecdsa_sign_digest(&secp256k1, (const uint8_t *)sk.buf, + (const uint8_t *)dig.buf, (uint8_t *)sig.buf + 1, + &pby, is_canonical); + } + if (0 != ret) { + vstr_clear(&sig); + mp_raise_ValueError(MP_ERROR_TEXT("Signing failed")); + } + sig.buf[0] = 27 + pby + compressed * 4; + return mp_obj_new_str_from_vstr(&mp_type_bytes, &sig); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorcrypto_ecdsa_sign_obj, + 2, 4, + mod_trezorcrypto_ecdsa_sign); + STATIC const mp_rom_map_elem_t mod_trezorcrypto_ecdsa_globals_table[] = { {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ecdsa)}, {MP_ROM_QSTR(MP_QSTR_scalar_multiply), MP_ROM_PTR(&mod_trezorcrypto_ecdsa_scalar_multiply_obj)}, {MP_ROM_QSTR(MP_QSTR_point_add), MP_ROM_PTR(&mod_trezorcrypto_ecdsa_point_add_obj)}, + {MP_ROM_QSTR(MP_QSTR_sign), + MP_ROM_PTR(&mod_trezorcrypto_ecdsa_sign_obj)}, }; STATIC MP_DEFINE_CONST_DICT(mod_trezorcrypto_ecdsa_globals, mod_trezorcrypto_ecdsa_globals_table); diff --git a/ports/stm32/boards/Passport/manifest.py b/ports/stm32/boards/Passport/manifest.py index 40f7efc8f..e5b1f0ce1 100644 --- a/ports/stm32/boards/Passport/manifest.py +++ b/ports/stm32/boards/Passport/manifest.py @@ -147,6 +147,7 @@ 'flows/set_chain_flow.py', 'flows/set_initial_pin_flow.py', 'flows/show_security_words_setting_flow.py', + 'flows/sign_electrum_message_flow.py', 'flows/sign_psbt_common_flow.py', 'flows/sign_psbt_microsd_flow.py', 'flows/sign_psbt_qr_flow.py', @@ -284,6 +285,7 @@ 'tasks/set_initial_pin_task.py', 'tasks/sign_psbt_task.py', 'tasks/sign_text_file_task.py', + 'tasks/validate_electrum_message_task.py', 'tasks/validate_psbt_task.py', 'tasks/verify_backup_task.py')) diff --git a/ports/stm32/boards/Passport/modules/flows/__init__.py b/ports/stm32/boards/Passport/modules/flows/__init__.py index 57632a124..2b8782c3e 100644 --- a/ports/stm32/boards/Passport/modules/flows/__init__.py +++ b/ports/stm32/boards/Passport/modules/flows/__init__.py @@ -66,6 +66,7 @@ from .set_chain_flow import * from .set_initial_pin_flow import * from .show_security_words_setting_flow import * +from .sign_electrum_message_flow import * from .sign_text_file_flow import * from .sign_psbt_common_flow import * from .sign_psbt_microsd_flow import * diff --git a/ports/stm32/boards/Passport/modules/flows/sign_electrum_message_flow.py b/ports/stm32/boards/Passport/modules/flows/sign_electrum_message_flow.py new file mode 100644 index 000000000..a9a47bda8 --- /dev/null +++ b/ports/stm32/boards/Passport/modules/flows/sign_electrum_message_flow.py @@ -0,0 +1,98 @@ +# SPDX-FileCopyrightText: © 2023 Foundation Devices, Inc. +# SPDX-License-Identifier: GPL-3.0-or-later +# +# sign_electrum_message_flow.py - Sign an electrum standard message via QR + +from flows import Flow, ScanQRFlow +from pages import ErrorPage, LongTextPage, QuestionPage, ShowQRPage +from tasks import sign_text_file_task, validate_electrum_message_task +from utils import spinner_task, stylize_address +from data_codecs.qr_type import QRType +import microns +from wallets.utils import get_addr_type_from_deriv +from public_constants import AF_CLASSIC +import stash +from ubinascii import b2a_base64 + + +class SignElectrumMessageFlow(Flow): + def __init__(self): + self.message = None + self.address = None + self.signature = None + super().__init__(initial_state=self.scan_message, name='Sign Electrum Message Flow') + + async def scan_message(self): + result = await ScanQRFlow(qr_types=[QRType.QR], + data_description='a message').run() + + if result is None: + self.set_result(None) + return + + self.message = result + + self.goto(self.validate_message) + + async def validate_message(self): + res = await spinner_task('Validating Message', + validate_electrum_message_task, + args=[self.message]) + (values, error) = res + + if error: + await ErrorPage(error).show() + self.set_result(False) + return + + (self.message, self.subpath) = values + + self.goto(self.show_message) + + async def show_message(self): + self.addr_format = get_addr_type_from_deriv(self.subpath) + + if self.addr_format is None: + self.addr_format = AF_CLASSIC + + with stash.SensitiveValues() as sv: + node = sv.derive_path(self.subpath) + self.address = sv.chain.address(node, self.addr_format) + + self.address = stylize_address(self.address) + + result = await LongTextPage(centered=True, + text=self.message, + card_header={'title': 'Message'}).show() + + if not result: + self.set_result(False) + return + + result = await QuestionPage(text='Sign message with this address?\n\n{}'.format(self.address), + right_micron=microns.Sign).show() + + if not result: + self.set_result(False) + return + + self.goto(self.do_sign) + + async def do_sign(self): + (sig, address, error) = await spinner_task('Signing Message', sign_text_file_task, + args=[self.message, self.subpath, self.addr_format, True]) + if error is None: + self.signature = sig + self.goto(self.show_signed) + else: + await ErrorPage(text='Error while signing message: {}'.format(error)).show() + self.set_result(False) + return + + async def show_signed(self): + qr_data = b2a_base64(self.signature).strip().decode() + + result = await ShowQRPage(qr_data=qr_data, + right_micron=microns.Checkmark).show() + + self.set_result(result) diff --git a/ports/stm32/boards/Passport/modules/menus.py b/ports/stm32/boards/Passport/modules/menus.py index 0fb2b1e5b..232d3ff23 100644 --- a/ports/stm32/boards/Passport/modules/menus.py +++ b/ports/stm32/boards/Passport/modules/menus.py @@ -29,7 +29,9 @@ def manage_account_menu(): - from flows import RenameAccountFlow, DeleteAccountFlow, ConnectWalletFlow, AddressExplorerFlow + from flows import (RenameAccountFlow, + DeleteAccountFlow, + ConnectWalletFlow) from pages import AccountDetailsPage return [ @@ -37,21 +39,31 @@ def manage_account_menu(): {'icon': 'ICON_INFO', 'label': 'Rename Account', 'flow': RenameAccountFlow}, {'icon': 'ICON_CONNECT', 'label': 'Connect Wallet', 'flow': ConnectWalletFlow, 'statusbar': {'title': 'CONNECT'}}, + {'icon': 'ICON_CANCEL', 'label': 'Delete Account', 'flow': DeleteAccountFlow}, + ] + + +def account_tools(): + from flows import VerifyAddressFlow, SignElectrumMessageFlow, AddressExplorerFlow + + return [ + {'icon': 'ICON_VERIFY_ADDRESS', 'label': 'Verify Address', 'flow': VerifyAddressFlow}, + {'icon': 'ICON_SCAN_QR', 'label': 'Sign a message', 'flow': SignElectrumMessageFlow, + 'statusbar': {'title': 'SIGN MESSAGE'}}, {'icon': 'ICON_VERIFY_ADDRESS', 'label': 'Explore Addresses', 'flow': AddressExplorerFlow, 'statusbar': {'title': 'LIST ADDRESSES'}}, - {'icon': 'ICON_CANCEL', 'label': 'Delete Account', 'flow': DeleteAccountFlow}, ] def account_menu(): - from flows import VerifyAddressFlow, SignPsbtQRFlow, SignPsbtMicroSDFlow + from flows import SignPsbtQRFlow, SignPsbtMicroSDFlow return [ {'icon': 'ICON_SCAN_QR', 'label': 'Sign with QR Code', 'flow': SignPsbtQRFlow, 'statusbar': {'title': 'SIGN'}}, {'icon': 'ICON_MICROSD', 'label': 'Sign with microSD', 'flow': SignPsbtMicroSDFlow, 'statusbar': {'title': 'SIGN'}}, - {'icon': 'ICON_VERIFY_ADDRESS', 'label': 'Verify Address', 'flow': VerifyAddressFlow}, + {'icon': 'ICON_FOLDER', 'label': 'Account Tools', 'submenu': account_tools}, {'icon': 'ICON_FOLDER', 'label': 'Manage Account', 'submenu': manage_account_menu}, ] diff --git a/ports/stm32/boards/Passport/modules/tasks/__init__.py b/ports/stm32/boards/Passport/modules/tasks/__init__.py index 4377c68f4..c8b4a1f20 100644 --- a/ports/stm32/boards/Passport/modules/tasks/__init__.py +++ b/ports/stm32/boards/Passport/modules/tasks/__init__.py @@ -52,5 +52,6 @@ from .set_initial_pin_task import set_initial_pin_task from .sign_psbt_task import sign_psbt_task from .sign_text_file_task import sign_text_file_task +from .validate_electrum_message_task import validate_electrum_message_task from .validate_psbt_task import validate_psbt_task from .verify_backup_task import verify_backup_task diff --git a/ports/stm32/boards/Passport/modules/tasks/sign_text_file_task.py b/ports/stm32/boards/Passport/modules/tasks/sign_text_file_task.py index d1f69e92c..ec2f9fdbb 100644 --- a/ports/stm32/boards/Passport/modules/tasks/sign_text_file_task.py +++ b/ports/stm32/boards/Passport/modules/tasks/sign_text_file_task.py @@ -11,16 +11,20 @@ import stash import chains -from utils import sign_message_digest +from utils import sign_message_digest, sign_message_digest_recoverable -async def sign_text_file_task(on_done, text, subpath, addr_fmt): +async def sign_text_file_task(on_done, text, subpath, addr_fmt, recoverable=False): with stash.SensitiveValues() as sv: node = sv.derive_path(subpath) address = sv.chain.address(node, addr_fmt) digest = chains.current_chain().hash_message(text.encode()) - signature = sign_message_digest(digest, subpath) + if recoverable: + # signature will be 65 bytes + signature = sign_message_digest_recoverable(digest, subpath) + else: + signature = sign_message_digest(digest, subpath) await on_done(signature, address, None) diff --git a/ports/stm32/boards/Passport/modules/tasks/validate_electrum_message_task.py b/ports/stm32/boards/Passport/modules/tasks/validate_electrum_message_task.py new file mode 100644 index 000000000..f2731ae9a --- /dev/null +++ b/ports/stm32/boards/Passport/modules/tasks/validate_electrum_message_task.py @@ -0,0 +1,41 @@ +# SPDX-FileCopyrightText: © 2024 Foundation Devices, Inc. +# SPDX-License-Identifier: GPL-3.0-or-later +# +# validate_electrum_message_task.py - Task to parse and validate an electrum message for signing + +from utils import validate_sign_text + + +async def validate_electrum_message_task(on_done, message): + try: + parts = message.split(':', 1) + message = parts[1] + header_elements = parts[0].split(' ') + + if len(header_elements) != 3: + await on_done(None, 'Message format must be "signmessage {derivation_path} ascii:{message}"') + return + + if header_elements[0] != 'signmessage': + await on_done(None, 'Not a valid message to sign') + return + + if header_elements[2] != 'ascii': + await on_done(None, 'Unsupported message type') + return + + (subpath, error) = validate_sign_text(message, + header_elements[1], + space_limit=False, + check_whitespace=False) + + if error: + await on_done(None, error) + return + + await on_done((message, subpath), None) + return + + except Exception as e: + await on_done(None, 'Invalid message format') + return diff --git a/ports/stm32/boards/Passport/modules/utils.py b/ports/stm32/boards/Passport/modules/utils.py index f9bd608aa..6b6eb480e 100644 --- a/ports/stm32/boards/Passport/modules/utils.py +++ b/ports/stm32/boards/Passport/modules/utils.py @@ -1050,6 +1050,20 @@ def sign_message_digest(digest, subpath): return rv +def sign_message_digest_recoverable(digest, subpath): + from trezorcrypto import ecdsa + # do the signature itself! + with stash.SensitiveValues() as sv: + node = sv.derive_path(subpath) + pk = node.private_key() + sv.register(pk) + + # returns 65 byte electrum message signing format + rv = ecdsa.sign(pk, digest) + + return rv + + def has_secrets(): from common import pa return not pa.is_secret_blank() @@ -1280,13 +1294,14 @@ def is_passphrase_active(): MSG_MAX_SPACES = 4 -def validate_sign_text(text, subpath): +def validate_sign_text(text, subpath, space_limit=True, check_whitespace=True): # Check for leading or trailing whitespace - if text[0] == ' ': - return (subpath, 'File contains leading whitespace.') + if check_whitespace: + if text[0] == ' ': + return (subpath, 'File contains leading whitespace.') - if text[-1] == ' ': - (subpath, 'File contains trailing whitespace.') + if text[-1] == ' ': + return (subpath, 'File contains trailing whitespace.') # Ensure characters are in range and not too many spaces run = 0 @@ -1296,12 +1311,13 @@ def validate_sign_text(text, subpath): if ord(ch) not in MSG_CHARSET: return (subpath, 'File contains non-ASCII character: 0x%02x' % ord(ch)) - if ch == ' ': - run += 1 - if run >= MSG_MAX_SPACES: - return (subpath, 'File contains more than {} spaces in a row'.format(MSG_MAX_SPACES - 1)) - else: - run = 0 + if space_limit: + if ch == ' ': + run += 1 + if run >= MSG_MAX_SPACES: + return (subpath, 'File contains more than {} spaces in a row'.format(MSG_MAX_SPACES - 1)) + else: + run = 0 # Check subpath, if given if subpath: diff --git a/ports/stm32/boards/Passport/modules/wallets/utils.py b/ports/stm32/boards/Passport/modules/wallets/utils.py index c13e1b875..1e5f6cd11 100644 --- a/ports/stm32/boards/Passport/modules/wallets/utils.py +++ b/ports/stm32/boards/Passport/modules/wallets/utils.py @@ -113,21 +113,21 @@ def get_bip_num_from_addr_type(addr_type, is_multisig): def get_addr_type_from_deriv(path): - type_str = get_addr_type_from_deriv_path(path) + addr_type = get_addr_type_from_deriv_path(path) subpath = get_part_from_deriv_path(path, 4) - if type_str == '44': + if addr_type == 44: return AF_CLASSIC - elif type_str == '49': + elif addr_type == 49: return AF_P2WPKH_P2SH - elif type_str == '84': + elif addr_type == 84: return AF_P2WPKH - elif type_str == '48': - if subpath == '1': + elif addr_type == 48: + if subpath == 1: return AF_P2WSH_P2SH - elif subpath == '2': + elif subpath == 2: return AF_P2WSH - elif type_str == '86': + elif addr_type == 86: return AF_P2TR return None @@ -237,6 +237,10 @@ def get_addr_type_from_deriv_path(path): def get_part_from_deriv_path(path, index): parts = path.split('/') + + if len(parts) <= index: + return None + if parts[index][-1] == "'": return int(parts[index][0:-1]) else: From f46bdc05a4edd1a38e587dc4965409c77d95d1da Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Thu, 4 Jul 2024 17:13:11 -0400 Subject: [PATCH 035/126] SFT-2998: reverting inconclusive changes --- extmod/vfs_blockdev.c | 9 ++------- lib/oofatfs/ff.c | 2 +- .../STM32H7xx_HAL_Driver/Src/stm32h7xx_ll_sdmmc.c | 7 ++++--- ports/stm32/boards/Passport/mpconfigboard.h | 12 +----------- ports/stm32/sdcard.c | 13 ++++++------- 5 files changed, 14 insertions(+), 29 deletions(-) diff --git a/extmod/vfs_blockdev.c b/extmod/vfs_blockdev.c index 0bd098ec6..57c83b428 100644 --- a/extmod/vfs_blockdev.c +++ b/extmod/vfs_blockdev.c @@ -54,13 +54,8 @@ int mp_vfs_blockdev_read(mp_vfs_blockdev_t *self, size_t block_num, size_t num_b mp_obj_array_t ar = {{&mp_type_bytearray}, BYTEARRAY_TYPECODE, 0, num_blocks *self->block_size, buf}; self->readblocks[2] = MP_OBJ_NEW_SMALL_INT(block_num); self->readblocks[3] = MP_OBJ_FROM_PTR(&ar); - mp_obj_t res = mp_call_method_n_kw(2, 0, self->readblocks); - - // readblocks returns true for success - bool err = !mp_obj_is_true(res); - if (err) { - return 1; - } + mp_call_method_n_kw(2, 0, self->readblocks); + // TODO handle error return return 0; } } diff --git a/lib/oofatfs/ff.c b/lib/oofatfs/ff.c index 4c8a529ad..fad1f8f21 100644 --- a/lib/oofatfs/ff.c +++ b/lib/oofatfs/ff.c @@ -3617,7 +3617,7 @@ FRESULT f_read ( FATFS *fs; DWORD clst, sect; FSIZE_t remain; - UINT rcnt = 0, cc = 0, csect = 0; + UINT rcnt, cc, csect; BYTE *rbuff = (BYTE*)buff; diff --git a/lib/stm32lib/STM32H7xx_HAL_Driver/Src/stm32h7xx_ll_sdmmc.c b/lib/stm32lib/STM32H7xx_HAL_Driver/Src/stm32h7xx_ll_sdmmc.c index cd27a84a5..5d39b9f28 100644 --- a/lib/stm32lib/STM32H7xx_HAL_Driver/Src/stm32h7xx_ll_sdmmc.c +++ b/lib/stm32lib/STM32H7xx_HAL_Driver/Src/stm32h7xx_ll_sdmmc.c @@ -1243,15 +1243,16 @@ static uint32_t SDMMC_GetCmdResp1(SDMMC_TypeDef *SDMMCx, uint8_t SD_CMD, uint32_ return SDMMC_ERROR_TIMEOUT; } sta_reg = SDMMCx->STA; - }while(((sta_reg & (SDMMC_FLAG_CCRCFAIL | SDMMC_FLAG_CMDREND /*| SDMMC_FLAG_CTIMEOUT*/ | SDMMC_FLAG_BUSYD0END)) == 0U) || + }while(((sta_reg & (SDMMC_FLAG_CCRCFAIL | SDMMC_FLAG_CMDREND | SDMMC_FLAG_CTIMEOUT | SDMMC_FLAG_BUSYD0END)) == 0U) || ((sta_reg & SDMMC_FLAG_CMDACT) != 0U )); if(__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CTIMEOUT)) { __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CTIMEOUT); - } - if(__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CCRCFAIL)) + return SDMMC_ERROR_CMD_RSP_TIMEOUT; + } + else if(__SDMMC_GET_FLAG(SDMMCx, SDMMC_FLAG_CCRCFAIL)) { __SDMMC_CLEAR_FLAG(SDMMCx, SDMMC_FLAG_CCRCFAIL); diff --git a/ports/stm32/boards/Passport/mpconfigboard.h b/ports/stm32/boards/Passport/mpconfigboard.h index 77556431f..6045ea126 100644 --- a/ports/stm32/boards/Passport/mpconfigboard.h +++ b/ports/stm32/boards/Passport/mpconfigboard.h @@ -108,7 +108,7 @@ void Passport_board_init(void); // SD card detect switch #define MICROPY_HW_SDCARD_DETECT_PIN (pin_E3) -#define MICROPY_HW_SDCARD_DETECT_PULL (GPIO_NOPULL) +#define MICROPY_HW_SDCARD_DETECT_PULL (GPIO_PULLUP) #define MICROPY_HW_SDCARD_DETECT_PRESENT (GPIO_PIN_RESET) // BQ27520 fuel gauge @@ -118,13 +118,3 @@ void Passport_board_init(void); // LCD Teariing Effect output line #define MICROPY_HW_LCD_TE_PIN (pin_B5) #define MICROPY_HW_LCD_TE_PULL (GPIO_NOPULL) - -// SD/MMC card driver interface bus width (defaults to 4 bits) -#ifndef MICROPY_HW_SDCARD_BUS_WIDTH -#define MICROPY_HW_SDCARD_BUS_WIDTH (1) -#endif - -// Whether to automatically mount (and boot from) the SD card if it's present -#ifndef MICROPY_HW_SDCARD_MOUNT_AT_BOOT -#define MICROPY_HW_SDCARD_MOUNT_AT_BOOT (0) -#endif diff --git a/ports/stm32/sdcard.c b/ports/stm32/sdcard.c index 79f9713bd..ea69e7285 100644 --- a/ports/stm32/sdcard.c +++ b/ports/stm32/sdcard.c @@ -159,14 +159,13 @@ void sdcard_init(void) { // Note: the mp_hal_pin_config function will configure the GPIO in // fast mode which can do up to 50MHz. This should be plenty for SDIO // which clocks up to 25MHz maximum. - - mp_hal_pin_config_alt_static_speed(MICROPY_HW_SDCARD_CK, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_SDCARD_CK); - mp_hal_pin_config_alt_static_speed(MICROPY_HW_SDCARD_CMD, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_SDCARD_CMD); - mp_hal_pin_config_alt_static_speed(MICROPY_HW_SDCARD_D0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_SDCARD_D0); + mp_hal_pin_config_alt_static(MICROPY_HW_SDCARD_CK, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDCARD_CK); + mp_hal_pin_config_alt_static(MICROPY_HW_SDCARD_CMD, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDCARD_CMD); + mp_hal_pin_config_alt_static(MICROPY_HW_SDCARD_D0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDCARD_D0); #if MICROPY_HW_SDCARD_BUS_WIDTH == 4 - mp_hal_pin_config_alt_static_speed(MICROPY_HW_SDCARD_D1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_SDCARD_D1); - mp_hal_pin_config_alt_static_speed(MICROPY_HW_SDCARD_D2, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_SDCARD_D2); - mp_hal_pin_config_alt_static_speed(MICROPY_HW_SDCARD_D3, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_VERY_HIGH, STATIC_AF_SDCARD_D3); + mp_hal_pin_config_alt_static(MICROPY_HW_SDCARD_D1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDCARD_D1); + mp_hal_pin_config_alt_static(MICROPY_HW_SDCARD_D2, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDCARD_D2); + mp_hal_pin_config_alt_static(MICROPY_HW_SDCARD_D3, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDCARD_D3); #endif // configure the SD card detect pin From 87be389cfa61f9c1fe476169e5e37489a5f10c88 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Thu, 4 Jul 2024 17:18:41 -0400 Subject: [PATCH 036/126] SFT-2998: preserving reasonable changes --- extmod/vfs_blockdev.c | 9 +++++++-- lib/oofatfs/ff.c | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/extmod/vfs_blockdev.c b/extmod/vfs_blockdev.c index 57c83b428..0bd098ec6 100644 --- a/extmod/vfs_blockdev.c +++ b/extmod/vfs_blockdev.c @@ -54,8 +54,13 @@ int mp_vfs_blockdev_read(mp_vfs_blockdev_t *self, size_t block_num, size_t num_b mp_obj_array_t ar = {{&mp_type_bytearray}, BYTEARRAY_TYPECODE, 0, num_blocks *self->block_size, buf}; self->readblocks[2] = MP_OBJ_NEW_SMALL_INT(block_num); self->readblocks[3] = MP_OBJ_FROM_PTR(&ar); - mp_call_method_n_kw(2, 0, self->readblocks); - // TODO handle error return + mp_obj_t res = mp_call_method_n_kw(2, 0, self->readblocks); + + // readblocks returns true for success + bool err = !mp_obj_is_true(res); + if (err) { + return 1; + } return 0; } } diff --git a/lib/oofatfs/ff.c b/lib/oofatfs/ff.c index fad1f8f21..4c8a529ad 100644 --- a/lib/oofatfs/ff.c +++ b/lib/oofatfs/ff.c @@ -3617,7 +3617,7 @@ FRESULT f_read ( FATFS *fs; DWORD clst, sect; FSIZE_t remain; - UINT rcnt, cc, csect; + UINT rcnt = 0, cc = 0, csect = 0; BYTE *rbuff = (BYTE*)buff; From 1eb60354407ce1e23fc93e008845cc6ae69cc75b Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Mon, 8 Jul 2024 13:43:39 -0400 Subject: [PATCH 037/126] SFT-2988: capitalized Message --- ports/stm32/boards/Passport/modules/menus.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/boards/Passport/modules/menus.py b/ports/stm32/boards/Passport/modules/menus.py index 232d3ff23..0d4f35db8 100644 --- a/ports/stm32/boards/Passport/modules/menus.py +++ b/ports/stm32/boards/Passport/modules/menus.py @@ -48,7 +48,7 @@ def account_tools(): return [ {'icon': 'ICON_VERIFY_ADDRESS', 'label': 'Verify Address', 'flow': VerifyAddressFlow}, - {'icon': 'ICON_SCAN_QR', 'label': 'Sign a message', 'flow': SignElectrumMessageFlow, + {'icon': 'ICON_SCAN_QR', 'label': 'Sign a Message', 'flow': SignElectrumMessageFlow, 'statusbar': {'title': 'SIGN MESSAGE'}}, {'icon': 'ICON_VERIFY_ADDRESS', 'label': 'Explore Addresses', 'flow': AddressExplorerFlow, 'statusbar': {'title': 'LIST ADDRESSES'}}, From b584b836480fcfe5909532085b18d2383e2b1696 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Tue, 9 Jul 2024 16:36:06 +0200 Subject: [PATCH 038/126] SFT-2417: Fix error kind when UR is unsupported. * extmod/foundation-rust/src/ur/mod.rs (UR_Error) : Remove message argument. * extmod/foundation-rust/src/ur/registry.rs (UR_Value) : Catch Error::UnsupportedResource and return UR_Error::unsupported in that case. --- extmod/foundation-rust/src/ur/mod.rs | 7 +++++-- extmod/foundation-rust/src/ur/registry.rs | 13 ++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/extmod/foundation-rust/src/ur/mod.rs b/extmod/foundation-rust/src/ur/mod.rs index 90566d344..855474502 100644 --- a/extmod/foundation-rust/src/ur/mod.rs +++ b/extmod/foundation-rust/src/ur/mod.rs @@ -68,8 +68,11 @@ impl UR_Error { /// # Safety /// /// The same as in [`UR_Error::new`]. - pub unsafe fn unsupported(message: &dyn fmt::Display) -> Self { - Self::new(message, UR_ErrorKind::UR_ERROR_KIND_UNSUPPORTED) + pub unsafe fn unsupported() -> Self { + Self::new( + &"Unsupported uniform resource", + UR_ErrorKind::UR_ERROR_KIND_UNSUPPORTED, + ) } } diff --git a/extmod/foundation-rust/src/ur/registry.rs b/extmod/foundation-rust/src/ur/registry.rs index fd0ae520b..e0df082c1 100644 --- a/extmod/foundation-rust/src/ur/registry.rs +++ b/extmod/foundation-rust/src/ur/registry.rs @@ -11,6 +11,7 @@ use foundation_urtypes::{ PassportResponse, PathComponents, }, supply_chain_validation::{Challenge, Solution}, + value, value::Value, }; @@ -48,8 +49,10 @@ impl UR_Value { ur_type: &str, message: &[u8], ) -> Result { - let value = Value::from_ur(ur_type, message) - .map_err(|e| UR_Error::other(&e))?; + let value = Value::from_ur(ur_type, message).map_err(|e| match e { + value::Error::UnsupportedResource => UR_Error::unsupported(), + _ => UR_Error::other(&e), + })?; let value = match value { Value::Bytes(bytes) => UR_Value::Bytes { @@ -63,11 +66,7 @@ impl UR_Value { Value::PassportRequest(passport_request) => { UR_Value::PassportRequest(passport_request.into()) } - _ => { - return Err(UR_Error::unsupported( - &"Unsupported uniform resource", - )); - } + _ => return Err(UR_Error::unsupported()), }; Ok(value) From ec79e010d6202882d44374054d5c283995e7443e Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Tue, 9 Jul 2024 17:15:57 +0200 Subject: [PATCH 039/126] SFT-2417: Fix exception names in except blocks. * ports/stm32/boards/Passport/modules/data_codecs/ur2_codec.py (UR2Decoder) : Fix the names of the UR exceptions. --- .../boards/Passport/modules/data_codecs/ur2_codec.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/data_codecs/ur2_codec.py b/ports/stm32/boards/Passport/modules/data_codecs/ur2_codec.py index 1710da6db..432347f97 100644 --- a/ports/stm32/boards/Passport/modules/data_codecs/ur2_codec.py +++ b/ports/stm32/boards/Passport/modules/data_codecs/ur2_codec.py @@ -59,12 +59,12 @@ def decode(self): try: if self.value is None: self.value = ur.decoder_decode_message() - except ur.Other as exc: + except ur.OtherError as exc: raise DecodeError(str(exc)) - except ur.Unsupported as exc: + except ur.UnsupportedError as exc: raise DecodeError("Unsupported UR.\n\n{}".format(str(exc))) - finally: - return self.value + + return self.value def qr_type(self): return QRType.UR2 From 7b2583bae33d672738f93ea6c20395f722fda5b3 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Tue, 9 Jul 2024 17:35:09 +0200 Subject: [PATCH 040/126] SFT-2417: Remove pass_error from ScanQRFlow. * ports/stm32/boards/Passport/modules/flows/scan_qr_flow.py (ScanQRFlow): Remove use of pass_error altogether. * ports/stm32/boards/Passport/modules/flows/sign_psbt_qr_flow.py (SignPsbtQRFlow) : Do not pass pass_error. --- ports/stm32/boards/Passport/modules/flows/scan_qr_flow.py | 8 +------- .../boards/Passport/modules/flows/sign_psbt_qr_flow.py | 3 +-- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/scan_qr_flow.py b/ports/stm32/boards/Passport/modules/flows/scan_qr_flow.py index 4785b0b5b..9c1476dd6 100644 --- a/ports/stm32/boards/Passport/modules/flows/scan_qr_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/scan_qr_flow.py @@ -17,8 +17,7 @@ def __init__(self, explicit_type=None, data_description=None, max_frames=None, - failure_message=None, - pass_error=False): + failure_message=None): """ Initialize the scan QR flow. @@ -43,7 +42,6 @@ def __init__(self, self.data = None self.max_frames = max_frames self.failure_message = failure_message or 'Unable to scan QR code.\n\n{}' - self.pass_error = pass_error if len(self.qr_types) == 0: raise ValueError('At least one QR type must be provided') @@ -66,10 +64,6 @@ async def scan(self): return if result.is_failure(): - if self.pass_error: - self.set_result(Error.QR_TOO_LARGE) - return - await LongErrorPage(text=self.failure_message.format(result.error)).show() self.set_result(None) return diff --git a/ports/stm32/boards/Passport/modules/flows/sign_psbt_qr_flow.py b/ports/stm32/boards/Passport/modules/flows/sign_psbt_qr_flow.py index f3774ab70..e0489c1a3 100644 --- a/ports/stm32/boards/Passport/modules/flows/sign_psbt_qr_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/sign_psbt_qr_flow.py @@ -35,8 +35,7 @@ async def scan_transaction(self): data_description='a PSBT file', max_frames=self.max_frames, failure_message="Unable to Scan QR code, \ -try signing using the microSD card.\n\n{}", - pass_error=True).run() +try signing using the microSD card.\n\n{}").run() if result is None: # User canceled the scan self.set_result(False) From 70170d183b522eb3488211dbe94e4a7368e154a4 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Tue, 2 Jul 2024 10:56:56 +0200 Subject: [PATCH 041/126] SFT-2417: Create upgrade your Passport alert. * ports/stm32/boards/Passport/modules/data_codecs/ur2_codec.py (UR2Decoder) : Create upgrade your Passport alert when encountering unknown UR types. --- .../Passport/modules/data_codecs/ur2_codec.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/data_codecs/ur2_codec.py b/ports/stm32/boards/Passport/modules/data_codecs/ur2_codec.py index 432347f97..7de70c880 100644 --- a/ports/stm32/boards/Passport/modules/data_codecs/ur2_codec.py +++ b/ports/stm32/boards/Passport/modules/data_codecs/ur2_codec.py @@ -31,15 +31,17 @@ def add_data(self, data): if ur.decoder_is_empty(): try: self.value = ur.decode_single_part(data) - except ur.UnsupportedError as exc: - raise DecodeError("Unsupported UR.\n\n{}".format(str(exc))) + except ur.UnsupportedError: + raise DecodeError("""\ +Please check for updates to Passport and your software wallet.""") except ur.OtherError as exc: raise DecodeError(str(exc)) else: raise DecodeError("""\ Received single-part UR when multi-part reception was already in place""") - except ur.UnsupportedError as exc: - raise DecodeError("Unsupported UR.\n\n{}".format(str(exc))) + except ur.UnsupportedError: + raise DecodeError("""\ +Please check for updates to Passport and your software wallet.""") except ur.OtherError as exc: raise DecodeError(str(exc)) @@ -61,8 +63,9 @@ def decode(self): self.value = ur.decoder_decode_message() except ur.OtherError as exc: raise DecodeError(str(exc)) - except ur.UnsupportedError as exc: - raise DecodeError("Unsupported UR.\n\n{}".format(str(exc))) + except ur.UnsupportedError: + raise DecodeError("""\ +Please check for updates to Passport and your software wallet.""") return self.value From a1c931fac186d4041da2d21c863d0f35867b0af0 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Wed, 10 Jul 2024 10:53:40 -0400 Subject: [PATCH 042/126] SFT-2988: used setup icon for account tools --- ports/stm32/boards/Passport/modules/menus.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/boards/Passport/modules/menus.py b/ports/stm32/boards/Passport/modules/menus.py index 0d4f35db8..bf0cbf71a 100644 --- a/ports/stm32/boards/Passport/modules/menus.py +++ b/ports/stm32/boards/Passport/modules/menus.py @@ -63,7 +63,7 @@ def account_menu(): 'statusbar': {'title': 'SIGN'}}, {'icon': 'ICON_MICROSD', 'label': 'Sign with microSD', 'flow': SignPsbtMicroSDFlow, 'statusbar': {'title': 'SIGN'}}, - {'icon': 'ICON_FOLDER', 'label': 'Account Tools', 'submenu': account_tools}, + {'icon': 'ICON_SETUP', 'label': 'Account Tools', 'submenu': account_tools}, {'icon': 'ICON_FOLDER', 'label': 'Manage Account', 'submenu': manage_account_menu}, ] From 8370b8c1b7d6c3cba53c6691d12370de92579036 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Wed, 10 Jul 2024 16:56:27 -0400 Subject: [PATCH 043/126] SFT-3756: updated fully noded wallet to use sparrow/json export format --- ports/stm32/boards/Passport/modules/wallets/fullynoded.py | 7 +++---- ports/stm32/boards/Passport/modules/wallets/sw_wallets.py | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/wallets/fullynoded.py b/ports/stm32/boards/Passport/modules/wallets/fullynoded.py index 28d297408..8a942ebe4 100644 --- a/ports/stm32/boards/Passport/modules/wallets/fullynoded.py +++ b/ports/stm32/boards/Passport/modules/wallets/fullynoded.py @@ -4,16 +4,15 @@ # fullynoded.py - FullyNoded wallet support # -from .electrum import create_electrum_export +from .generic_json_wallet import create_generic_json_wallet from .multisig_json import create_multisig_json_wallet from .multisig_import import read_multisig_config_from_qr, read_multisig_config_from_microsd from data_codecs.qr_type import QRType -from public_constants import AF_P2WPKH FullyNodedWallet = { - 'label': 'FullyNoded', + 'label': 'Fully Noded', 'sig_types': [ - {'id': 'single-sig', 'label': 'Single-sig', 'addr_type': AF_P2WPKH, 'create_wallet': create_electrum_export}, + {'id': 'single-sig', 'label': 'Single-sig', 'addr_type': None, 'create_wallet': create_generic_json_wallet}, {'id': 'multisig', 'label': 'Multisig', 'addr_type': None, 'create_wallet': create_multisig_json_wallet, 'import_qr': read_multisig_config_from_qr, 'import_microsd': read_multisig_config_from_microsd} ], diff --git a/ports/stm32/boards/Passport/modules/wallets/sw_wallets.py b/ports/stm32/boards/Passport/modules/wallets/sw_wallets.py index 9ee552189..943151057 100644 --- a/ports/stm32/boards/Passport/modules/wallets/sw_wallets.py +++ b/ports/stm32/boards/Passport/modules/wallets/sw_wallets.py @@ -14,7 +14,7 @@ # from .dux_reserve import DuxReserveWallet from .electrum import ElectrumWallet from .envoy import EnvoyWallet -# from .fullynoded import FullyNodedWallet +from .fullynoded import FullyNodedWallet # from .gordian import GordianWallet # from .lily import LilyWallet from .nunchuk import NunchukWallet @@ -37,7 +37,7 @@ CoinbitsWallet, # DuxReserveWallet, ElectrumWallet, - # FullyNodedWallet, + FullyNodedWallet, # GordianWallet, # LilyWallet, NunchukWallet, From 6a602ee138425103bb55328401aec6862d9dce80 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Mon, 15 Jul 2024 11:59:29 +0200 Subject: [PATCH 044/126] SFT-3812: Update foundation-urtypes to 0.4.1. * extmod/foundation-rust/Cargo.lock: Regenerate. * extmod/foundation-rust/Cargo.toml (dependencies) : Update to 0.4.1. * extmod/foundation-rust/include/foundation.h: Regenerate. * extmod/foundation-rust/src/ur/registry.rs: Fix breaking changes. * extmod/foundation/modfoundation-ur.h: Likewise. * ports/stm32/boards/Passport/modules/flows/sign_psbt_qr_flow.py: Likewise. * ports/stm32/boards/Passport/modules/wallets/casa.py: Likewise. --- extmod/foundation-rust/Cargo.lock | 4 +- extmod/foundation-rust/Cargo.toml | 2 +- extmod/foundation-rust/include/foundation.h | 47 +++++---- extmod/foundation-rust/src/ur/registry.rs | 94 +++++++++--------- extmod/foundation/modfoundation-ur.h | 98 +++++++++---------- .../modules/flows/sign_psbt_qr_flow.py | 10 +- .../boards/Passport/modules/wallets/casa.py | 20 ++-- 7 files changed, 135 insertions(+), 140 deletions(-) diff --git a/extmod/foundation-rust/Cargo.lock b/extmod/foundation-rust/Cargo.lock index 0d322ba08..e27afe455 100644 --- a/extmod/foundation-rust/Cargo.lock +++ b/extmod/foundation-rust/Cargo.lock @@ -157,9 +157,9 @@ dependencies = [ [[package]] name = "foundation-urtypes" -version = "0.3.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a59d9988ac2826c9c49d02364b907ee6dfc2385ddf0dcbc8c76e43532fcf87" +checksum = "e40662e96946962894b2ff376b848af3e8beb2939be091358dfe2df9db8eaa55" dependencies = [ "foundation-arena", "heapless", diff --git a/extmod/foundation-rust/Cargo.toml b/extmod/foundation-rust/Cargo.toml index 1d82e95bc..161fc4a37 100644 --- a/extmod/foundation-rust/Cargo.toml +++ b/extmod/foundation-rust/Cargo.toml @@ -33,7 +33,7 @@ version = "0.2" default-features = false [dependencies.foundation-urtypes] -version = "0.3" +version = "0.4.1" default-features = false [target.'cfg(target_arch = "arm")'.dependencies.cortex-m] diff --git a/extmod/foundation-rust/include/foundation.h b/extmod/foundation-rust/include/foundation.h index 7696c3dbc..eb0cc5abc 100644 --- a/extmod/foundation-rust/include/foundation.h +++ b/extmod/foundation-rust/include/foundation.h @@ -117,7 +117,7 @@ typedef struct { typedef struct { UR_CoinType coin_type; uint64_t network; -} UR_CryptoCoinInfo; +} UR_CoinInfo; /** * Metadata for the complete or partial derivation path of a key. @@ -139,10 +139,10 @@ typedef struct { * Whether `depth` is present. */ bool has_depth; -} UR_CryptoKeypath; +} UR_Keypath; /** - * Derived `crypto-hdkey`. + * Derived `hdkey`. */ typedef struct { /** @@ -164,7 +164,7 @@ typedef struct { /** * How the key should be used. */ - UR_CryptoCoinInfo use_info; + UR_CoinInfo use_info; /** * Whether `use_info` is present. */ @@ -172,7 +172,7 @@ typedef struct { /** * How the key was derived. */ - UR_CryptoKeypath origin; + UR_Keypath origin; /** * Whether `origin` is present. */ @@ -186,7 +186,7 @@ typedef struct { } UR_DerivedKey; /** - * A `crypto-hdkey`. + * A `hdkey`. */ typedef enum { DerivedKey, @@ -216,7 +216,7 @@ typedef struct { } UR_Challenge; /** - * Passport custom `crypto-request`. + * Passport custom `x-passport-request`. */ typedef struct { /** @@ -256,7 +256,7 @@ typedef struct { } UR_Solution; /** - * Passport custom `crypto-request`. + * Passport custom `x-passport-response`. */ typedef struct { /** @@ -302,19 +302,19 @@ typedef enum { */ Bytes, /** - * `crypto-hdkey`. + * `hdkey`. */ - CryptoHDKey, + HDKey, /** - * `crypto-psbt`. + * `psbt`. */ - CryptoPSBT, + Psbt, /** - * Passport custom `crypto-request`. + * Passport custom `x-passport-request`. */ PassportRequest, /** - * Passport custom `crypto-response`. + * Passport custom `x-passport-response`. */ PassportResponse, } UR_Value_Tag; @@ -327,16 +327,16 @@ typedef struct { typedef struct { const uint8_t *data; size_t len; -} CryptoPSBT_Body; +} Psbt_Body; typedef struct { UR_Value_Tag tag; union { Bytes_Body bytes; struct { - UR_HDKey crypto_hd_key; + UR_HDKey hd_key; }; - CryptoPSBT_Body crypto_psbt; + Psbt_Body psbt; struct { UR_PassportRequest passport_request; }; @@ -491,29 +491,28 @@ void ur_encoder_start(UR_Encoder *encoder, void ur_encoder_next_part(UR_Encoder *encoder, const char **ur, size_t *ur_len); /** - * Passport custom `crypto-response`. * Create a new `bytes` UR. */ void ur_registry_new_bytes(UR_Value *value, uint8_t *data, size_t len); /** - * Create a new derived `crypto-hdkey` UR. + * Create a new derived `hdkey` UR. */ void ur_registry_new_derived_key(UR_Value *value, bool is_private, const uint8_t (*key_data)[33], const uint8_t (*chain_code)[32], - const UR_CryptoCoinInfo *use_info, - const UR_CryptoKeypath *origin, + const UR_CoinInfo *use_info, + const UR_Keypath *origin, uint32_t parent_fingerprint); /** - * Create a new `crypto-psbt` UR. + * Create a new `psbt` UR. */ -void ur_registry_new_crypto_psbt(UR_Value *value, uint8_t *data, size_t len); +void ur_registry_new_psbt(UR_Value *value, uint8_t *data, size_t len); /** - * Create a new Passport ustom `crypto-response` UR. + * Create a new Passport custom `x-passport-response` UR. */ void ur_registry_new_passport_response(UR_Value *value, const uint8_t (*transaction_id)[16], diff --git a/extmod/foundation-rust/src/ur/registry.rs b/extmod/foundation-rust/src/ur/registry.rs index e0df082c1..b852dadac 100644 --- a/extmod/foundation-rust/src/ur/registry.rs +++ b/extmod/foundation-rust/src/ur/registry.rs @@ -7,8 +7,8 @@ use foundation_urtypes::{ passport::Model, registry::PassportRequest, registry::{ - CoinType, CryptoCoinInfo, CryptoHDKey, CryptoKeypath, DerivedKey, - PassportResponse, PathComponents, + CoinInfo, CoinType, DerivedKey, HDKey, Keypath, PassportResponse, + PathComponents, }, supply_chain_validation::{Challenge, Solution}, value, @@ -30,13 +30,13 @@ pub const UR_NETWORK_TESTNET: u32 = 1; pub enum UR_Value { /// `bytes`. Bytes { data: *const u8, len: usize }, - /// `crypto-hdkey`. - CryptoHDKey(UR_HDKey), - /// `crypto-psbt`. - CryptoPSBT { data: *const u8, len: usize }, - /// Passport custom `crypto-request`. + /// `hdkey`. + HDKey(UR_HDKey), + /// `psbt`. + Psbt { data: *const u8, len: usize }, + /// Passport custom `x-passport-request`. PassportRequest(UR_PassportRequest), - /// Passport custom `crypto-response`. + /// Passport custom `x-passport-response`. PassportResponse(UR_PassportResponse), } @@ -59,7 +59,7 @@ impl UR_Value { data: bytes.as_ptr(), len: bytes.len(), }, - Value::CryptoPsbt(psbt) => UR_Value::CryptoPSBT { + Value::Psbt(psbt) => UR_Value::Psbt { data: psbt.as_ptr(), len: psbt.len(), }, @@ -78,18 +78,18 @@ impl UR_Value { /// value is: /// /// - `UR_Value::Bytes`. - /// - `UR_Value::CryptoPSBT`. + /// - `UR_Value::Psbt`. pub unsafe fn to_value(&self) -> Value<'_> { match self { UR_Value::Bytes { data, len } => { let buf = unsafe { slice::from_raw_parts(*data, *len) }; Value::Bytes(buf) } - UR_Value::CryptoPSBT { data, len } => { + UR_Value::Psbt { data, len } => { let buf = unsafe { slice::from_raw_parts(*data, *len) }; - Value::CryptoPsbt(buf) + Value::Psbt(buf) } - UR_Value::CryptoHDKey(v) => Value::CryptoHDKey(v.into()), + UR_Value::HDKey(v) => Value::HDKey(v.into()), UR_Value::PassportRequest(_) => panic!( "Not implemented as it isn't needed. Should be unreachable" ), @@ -98,23 +98,21 @@ impl UR_Value { } } -/// A `crypto-hdkey`. +/// A `hdkey`. #[repr(C)] pub enum UR_HDKey { DerivedKey(UR_DerivedKey), } -impl<'a> From<&'a UR_HDKey> for CryptoHDKey<'a> { - fn from(value: &'a UR_HDKey) -> CryptoHDKey<'a> { +impl<'a> From<&'a UR_HDKey> for HDKey<'a> { + fn from(value: &'a UR_HDKey) -> HDKey<'a> { match value { - UR_HDKey::DerivedKey(v) => { - CryptoHDKey::DerivedKey(DerivedKey::from(v)) - } + UR_HDKey::DerivedKey(v) => HDKey::DerivedKey(DerivedKey::from(v)), } } } -/// Derived `crypto-hdkey`. +/// Derived `hdkey`. #[repr(C)] pub struct UR_DerivedKey { /// `true` if this is a private key. @@ -126,11 +124,11 @@ pub struct UR_DerivedKey { /// Whether `chain_code` is present. pub has_chain_code: bool, /// How the key should be used. - pub use_info: UR_CryptoCoinInfo, + pub use_info: UR_CoinInfo, /// Whether `use_info` is present. pub has_use_info: bool, /// How the key was derived. - pub origin: UR_CryptoKeypath, + pub origin: UR_Keypath, /// Whether `origin` is present. pub has_origin: bool, /// The fingerprint of this key's direct ancestor. @@ -150,12 +148,12 @@ impl<'a> From<&'a UR_DerivedKey> for DerivedKey<'a> { None }, use_info: if value.has_use_info { - Some(CryptoCoinInfo::from(&value.use_info)) + Some(CoinInfo::from(&value.use_info)) } else { None }, origin: if value.has_origin { - Some(CryptoKeypath::from(&value.origin)) + Some(Keypath::from(&value.origin)) } else { None }, @@ -183,14 +181,14 @@ impl From for CoinType { #[repr(C)] #[derive(Clone)] -pub struct UR_CryptoCoinInfo { +pub struct UR_CoinInfo { pub coin_type: UR_CoinType, pub network: u64, } -impl From<&UR_CryptoCoinInfo> for CryptoCoinInfo { - fn from(v: &UR_CryptoCoinInfo) -> CryptoCoinInfo { - CryptoCoinInfo { +impl From<&UR_CoinInfo> for CoinInfo { + fn from(v: &UR_CoinInfo) -> CoinInfo { + CoinInfo { coin_type: v.coin_type.into(), network: v.network, } @@ -200,7 +198,7 @@ impl From<&UR_CryptoCoinInfo> for CryptoCoinInfo { /// Metadata for the complete or partial derivation path of a key. #[repr(C)] #[derive(Clone)] -pub struct UR_CryptoKeypath { +pub struct UR_Keypath { /// The fingerprint of this key's direct ancestor. /// /// A value of `0` means that the fingerprint is not present. @@ -213,9 +211,9 @@ pub struct UR_CryptoKeypath { pub has_depth: bool, } -impl<'a> From> for UR_CryptoKeypath { - fn from(v: CryptoKeypath<'a>) -> UR_CryptoKeypath { - UR_CryptoKeypath { +impl<'a> From> for UR_Keypath { + fn from(v: Keypath<'a>) -> UR_Keypath { + UR_Keypath { source_fingerprint: v .source_fingerprint .map(|v| v.get()) @@ -226,9 +224,9 @@ impl<'a> From> for UR_CryptoKeypath { } } -impl<'a> From<&'a UR_CryptoKeypath> for CryptoKeypath<'a> { - fn from(v: &UR_CryptoKeypath) -> CryptoKeypath<'a> { - CryptoKeypath { +impl<'a> From<&'a UR_Keypath> for Keypath<'a> { + fn from(v: &UR_Keypath) -> Keypath<'a> { + Keypath { components: PathComponents::from(&[]), source_fingerprint: NonZeroU32::new(v.source_fingerprint), depth: if v.has_depth { Some(v.depth) } else { None }, @@ -236,7 +234,7 @@ impl<'a> From<&'a UR_CryptoKeypath> for CryptoKeypath<'a> { } } -/// Passport custom `crypto-request`. +/// Passport custom `x-passport-request`. #[repr(C)] pub struct UR_PassportRequest { /// Transaction ID. @@ -287,7 +285,7 @@ impl From for UR_Challenge { } } -/// Passport custom `crypto-request`. +/// Passport custom `x-passport-response`. #[repr(C)] pub struct UR_PassportResponse { /// Transaction ID. @@ -405,8 +403,6 @@ impl From for Model { } } -/// Passport custom `crypto-response`. - /// Create a new `bytes` UR. #[no_mangle] pub extern "C" fn ur_registry_new_bytes( @@ -417,28 +413,28 @@ pub extern "C" fn ur_registry_new_bytes( *value = UR_Value::Bytes { data, len }; } -/// Create a new derived `crypto-hdkey` UR. +/// Create a new derived `hdkey` UR. #[no_mangle] pub extern "C" fn ur_registry_new_derived_key( value: &mut UR_Value, is_private: bool, key_data: &[u8; 33], chain_code: Option<&[u8; 32]>, - use_info: Option<&UR_CryptoCoinInfo>, - origin: Option<&UR_CryptoKeypath>, + use_info: Option<&UR_CoinInfo>, + origin: Option<&UR_Keypath>, parent_fingerprint: u32, ) { - *value = UR_Value::CryptoHDKey(UR_HDKey::DerivedKey(UR_DerivedKey { + *value = UR_Value::HDKey(UR_HDKey::DerivedKey(UR_DerivedKey { is_private, key_data: *key_data, chain_code: chain_code.copied().unwrap_or([0u8; 32]), has_chain_code: chain_code.is_some(), - use_info: use_info.cloned().unwrap_or(UR_CryptoCoinInfo { + use_info: use_info.cloned().unwrap_or(UR_CoinInfo { coin_type: UR_CoinType::BTC, network: 0, }), has_use_info: use_info.is_some(), - origin: origin.cloned().unwrap_or(UR_CryptoKeypath { + origin: origin.cloned().unwrap_or(UR_Keypath { source_fingerprint: 0, depth: 0, has_depth: false, @@ -448,17 +444,17 @@ pub extern "C" fn ur_registry_new_derived_key( })); } -/// Create a new `crypto-psbt` UR. +/// Create a new `psbt` UR. #[no_mangle] -pub extern "C" fn ur_registry_new_crypto_psbt( +pub extern "C" fn ur_registry_new_psbt( value: &mut UR_Value, data: *mut u8, len: usize, ) { - *value = UR_Value::CryptoPSBT { data, len }; + *value = UR_Value::Psbt { data, len }; } -/// Create a new Passport ustom `crypto-response` UR. +/// Create a new Passport custom `x-passport-response` UR. #[no_mangle] pub extern "C" fn ur_registry_new_passport_response( value: &mut UR_Value, diff --git a/extmod/foundation/modfoundation-ur.h b/extmod/foundation/modfoundation-ur.h index 8187006d1..5fbce1ba9 100644 --- a/extmod/foundation/modfoundation-ur.h +++ b/extmod/foundation/modfoundation-ur.h @@ -11,8 +11,8 @@ STATIC const mp_obj_type_t mod_foundation_ur_Value_type; STATIC const mp_obj_type_t mod_foundation_ur_CoinType_type; -STATIC const mp_obj_type_t mod_foundation_ur_CryptoCoinInfo_type; -STATIC const mp_obj_type_t mod_foundation_ur_CryptoKeypath_type; +STATIC const mp_obj_type_t mod_foundation_ur_CoinInfo_type; +STATIC const mp_obj_type_t mod_foundation_ur_Keypath_type; STATIC const mp_obj_type_t mod_foundation_ur_PassportRequest_type; STATIC struct _mp_obj_PassportRequest_t *mod_foundation_ur_PassportRequest_new(UR_PassportRequest *value); @@ -80,11 +80,11 @@ STATIC void mod_foundation_ur_Value_print(const mp_print_t *print, case Bytes: mp_print_str(print, "UR_Value::Bytes"); break; - case CryptoHDKey: - mp_print_str(print, "UR_Value::CryptoHDKey"); + case HDKey: + mp_print_str(print, "UR_Value::HDKey"); break; - case CryptoPSBT: - mp_print_str(print, "UR_Value::CryptoPSBT"); + case Psbt: + mp_print_str(print, "UR_Value::Psbt"); break; case PassportRequest: mp_print_str(print, "UR_Value::PassportRequest"); @@ -126,17 +126,17 @@ STATIC mp_obj_t mod_foundation_ur_Value_unwrap_bytes(mp_obj_t self_in) { STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_foundation_ur_Value_unwrap_bytes_obj, mod_foundation_ur_Value_unwrap_bytes); -/// def unwrap_crypto_psbt(self) -> bytearray: +/// def unwrap_psbt(self) -> bytearray: /// """ /// """ -STATIC mp_obj_t mod_foundation_ur_Value_unwrap_crypto_psbt(mp_obj_t self_in) { +STATIC mp_obj_t mod_foundation_ur_Value_unwrap_psbt(mp_obj_t self_in) { mp_obj_Value_t *self = MP_OBJ_TO_PTR(self_in); - mp_check_self(self->value.tag == CryptoPSBT); + mp_check_self(self->value.tag == Psbt); - return mp_obj_new_bytearray_by_ref(self->value.crypto_psbt.len, (void *)self->value.crypto_psbt.data); + return mp_obj_new_bytearray_by_ref(self->value.psbt.len, (void *)self->value.psbt.data); } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_foundation_ur_Value_unwrap_crypto_psbt_obj, - mod_foundation_ur_Value_unwrap_crypto_psbt); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_foundation_ur_Value_unwrap_psbt_obj, + mod_foundation_ur_Value_unwrap_psbt); /// def unwrap_passport_request(self) -> PassportRequest: /// """ @@ -152,13 +152,13 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_foundation_ur_Value_unwrap_passport_request STATIC const mp_rom_map_elem_t mod_foundation_ur_Value_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_BYTES), MP_ROM_INT(Bytes) }, - { MP_ROM_QSTR(MP_QSTR_CRYPTO_HDKEY), MP_ROM_INT(CryptoHDKey) }, - { MP_ROM_QSTR(MP_QSTR_CRYPTO_PSBT), MP_ROM_INT(CryptoPSBT) }, + { MP_ROM_QSTR(MP_QSTR_HDKEY), MP_ROM_INT(HDKey) }, + { MP_ROM_QSTR(MP_QSTR_PSBT), MP_ROM_INT(Psbt) }, { MP_ROM_QSTR(MP_QSTR_PASSPORT_REQUEST), MP_ROM_INT(PassportRequest) }, { MP_ROM_QSTR(MP_QSTR_ur_type), MP_ROM_PTR(&mod_foundation_ur_Value_ur_type_obj) }, { MP_ROM_QSTR(MP_QSTR_unwrap_bytes), MP_ROM_PTR(&mod_foundation_ur_Value_unwrap_bytes_obj) }, - { MP_ROM_QSTR(MP_QSTR_unwrap_crypto_psbt), MP_ROM_PTR(&mod_foundation_ur_Value_unwrap_crypto_psbt_obj) }, + { MP_ROM_QSTR(MP_QSTR_unwrap_psbt), MP_ROM_PTR(&mod_foundation_ur_Value_unwrap_psbt_obj) }, { MP_ROM_QSTR(MP_QSTR_unwrap_passport_request), MP_ROM_PTR(&mod_foundation_ur_Value_unwrap_passport_request_obj) }, }; STATIC MP_DEFINE_CONST_DICT(mod_foundation_ur_Value_locals_dict, mod_foundation_ur_Value_locals_dict_table); @@ -188,22 +188,22 @@ STATIC const mp_obj_type_t mod_foundation_ur_CoinType_type = { .locals_dict = (mp_obj_dict_t*)&mod_foundation_ur_CoinType_locals_dict, }; -/// class CryptoCoinInfo: +/// class CoinInfo: /// """ /// """ -typedef struct _mp_obj_CryptoCoinInfo_t { +typedef struct _mp_obj_CoinInfo_t { mp_obj_base_t base; - UR_CryptoCoinInfo info; -} mp_obj_CryptoCoinInfo_t; + UR_CoinInfo info; +} mp_obj_CoinInfo_t; -STATIC mp_obj_t mod_foundation_ur_CryptoCoinInfo_make_new(const mp_obj_type_t *type, +STATIC mp_obj_t mod_foundation_ur_CoinInfo_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 2, 2, false); - mp_obj_CryptoCoinInfo_t *o = m_new_obj(mp_obj_CryptoCoinInfo_t); - o->base.type = &mod_foundation_ur_CryptoCoinInfo_type; + mp_obj_CoinInfo_t *o = m_new_obj(mp_obj_CoinInfo_t); + o->base.type = &mod_foundation_ur_CoinInfo_type; o->info.coin_type = mp_obj_int_get_uint_checked(args[0]); o->info.network = mp_obj_int_get_uint_checked(args[1]); @@ -211,21 +211,21 @@ STATIC mp_obj_t mod_foundation_ur_CryptoCoinInfo_make_new(const mp_obj_type_t *t return MP_OBJ_FROM_PTR(o); } -STATIC const mp_obj_type_t mod_foundation_ur_CryptoCoinInfo_type = { +STATIC const mp_obj_type_t mod_foundation_ur_CoinInfo_type = { { &mp_type_type }, - .name = MP_QSTR_CryptoCoinInfo, - .make_new = mod_foundation_ur_CryptoCoinInfo_make_new, + .name = MP_QSTR_CoinInfo, + .make_new = mod_foundation_ur_CoinInfo_make_new, }; -/// class CryptoKeypath: +/// class Keypath: /// """ /// """ -typedef struct _mp_obj_CryptoKeypath_t { +typedef struct _mp_obj_Keypath_t { mp_obj_base_t base; - UR_CryptoKeypath keypath; -} mp_obj_CryptoKeypath_t; + UR_Keypath keypath; +} mp_obj_Keypath_t; -STATIC mp_obj_t mod_foundation_ur_CryptoKeypath_make_new(const mp_obj_type_t *type, +STATIC mp_obj_t mod_foundation_ur_Keypath_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { @@ -237,8 +237,8 @@ STATIC mp_obj_t mod_foundation_ur_CryptoKeypath_make_new(const mp_obj_type_t *ty mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - mp_obj_CryptoKeypath_t *o = m_new_obj(mp_obj_CryptoKeypath_t); - o->base.type = &mod_foundation_ur_CryptoKeypath_type; + mp_obj_Keypath_t *o = m_new_obj(mp_obj_Keypath_t); + o->base.type = &mod_foundation_ur_Keypath_type; if (args[0].u_obj != MP_OBJ_NULL) { o->keypath.source_fingerprint = mp_obj_int_get_uint_checked(args[0].u_obj); @@ -257,10 +257,10 @@ STATIC mp_obj_t mod_foundation_ur_CryptoKeypath_make_new(const mp_obj_type_t *ty return MP_OBJ_FROM_PTR(o); } -STATIC const mp_obj_type_t mod_foundation_ur_CryptoKeypath_type = { +STATIC const mp_obj_type_t mod_foundation_ur_Keypath_type = { { &mp_type_type }, - .name = MP_QSTR_CryptoKeypath, - .make_new = mod_foundation_ur_CryptoKeypath_make_new, + .name = MP_QSTR_Keypath, + .make_new = mod_foundation_ur_Keypath_make_new, }; /// class PassportRequest: @@ -352,8 +352,8 @@ STATIC mp_obj_t mod_foundation_ur_new_derived_key(size_t n_args, { mp_buffer_info_t key_data = {0}; mp_buffer_info_t chain_code_info = {0}; - mp_obj_CryptoCoinInfo_t *use_info_obj = NULL; - mp_obj_CryptoKeypath_t *origin_obj = NULL; + mp_obj_CoinInfo_t *use_info_obj = NULL; + mp_obj_Keypath_t *origin_obj = NULL; UR_Value value = {0}; static const mp_arg_t allowed_args[] = { @@ -383,18 +383,18 @@ STATIC mp_obj_t mod_foundation_ur_new_derived_key(size_t n_args, } if (args[3].u_obj != MP_OBJ_NULL) { - if (!mp_obj_is_type(args[3].u_obj, &mod_foundation_ur_CryptoCoinInfo_type)) { + if (!mp_obj_is_type(args[3].u_obj, &mod_foundation_ur_CoinInfo_type)) { mp_raise_msg(&mp_type_ValueError, - MP_ERROR_TEXT("use_info should be of type CryptoCoinInfo")); + MP_ERROR_TEXT("use_info should be of type CoinInfo")); } use_info_obj = MP_OBJ_TO_PTR(args[3].u_obj); } if (args[4].u_obj != MP_OBJ_NULL) { - if (!mp_obj_is_type(args[4].u_obj, &mod_foundation_ur_CryptoKeypath_type)) { + if (!mp_obj_is_type(args[4].u_obj, &mod_foundation_ur_Keypath_type)) { mp_raise_msg(&mp_type_ValueError, - MP_ERROR_TEXT("origin should be of type CryptoKeypath")); + MP_ERROR_TEXT("origin should be of type Keypath")); } origin_obj = MP_OBJ_TO_PTR(args[4].u_obj); @@ -419,20 +419,20 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_foundation_ur_new_derived_key_obj, 1, mod_foundation_ur_new_derived_key); -/// def new_crypto_psbt(data: bytes) -> Value: +/// def new_psbt(data: bytes) -> Value: /// """ /// """ -STATIC mp_obj_t mod_foundation_ur_new_crypto_psbt(mp_obj_t data_in) +STATIC mp_obj_t mod_foundation_ur_new_psbt(mp_obj_t data_in) { mp_buffer_info_t data = {0}; UR_Value value; mp_get_buffer_raise(data_in, &data, MP_BUFFER_READ); - ur_registry_new_crypto_psbt(&value, data.buf, data.len); + ur_registry_new_psbt(&value, data.buf, data.len); return MP_OBJ_FROM_PTR(mod_foundation_ur_Value_new(&value)); } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_foundation_ur_new_crypto_psbt_obj, - mod_foundation_ur_new_crypto_psbt); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_foundation_ur_new_psbt_obj, + mod_foundation_ur_new_psbt); /// def new_passport_response(data: bytes) -> Value: @@ -670,12 +670,12 @@ STATIC const mp_rom_map_elem_t mod_foundation_ur_globals_table[] = { {MP_ROM_QSTR(MP_QSTR_PASSPORT_MODEL_BATCH2), MP_ROM_INT(PASSPORT_MODEL_BATCH2)}, {MP_ROM_QSTR(MP_QSTR_Value), MP_ROM_PTR(&mod_foundation_ur_Value_type)}, {MP_ROM_QSTR(MP_QSTR_CoinType), MP_ROM_PTR(&mod_foundation_ur_CoinType_type)}, - {MP_ROM_QSTR(MP_QSTR_CryptoCoinInfo), MP_ROM_PTR(&mod_foundation_ur_CryptoCoinInfo_type)}, - {MP_ROM_QSTR(MP_QSTR_CryptoKeypath), MP_ROM_PTR(&mod_foundation_ur_CryptoKeypath_type)}, + {MP_ROM_QSTR(MP_QSTR_CoinInfo), MP_ROM_PTR(&mod_foundation_ur_CoinInfo_type)}, + {MP_ROM_QSTR(MP_QSTR_Keypath), MP_ROM_PTR(&mod_foundation_ur_Keypath_type)}, {MP_ROM_QSTR(MP_QSTR_PassportRequest), MP_ROM_PTR(&mod_foundation_ur_PassportRequest_type)}, {MP_ROM_QSTR(MP_QSTR_new_bytes), MP_ROM_PTR(&mod_foundation_ur_new_bytes_obj)}, {MP_ROM_QSTR(MP_QSTR_new_derived_key), MP_ROM_PTR(&mod_foundation_ur_new_derived_key_obj)}, - {MP_ROM_QSTR(MP_QSTR_new_crypto_psbt), MP_ROM_PTR(&mod_foundation_ur_new_crypto_psbt_obj)}, + {MP_ROM_QSTR(MP_QSTR_new_psbt), MP_ROM_PTR(&mod_foundation_ur_new_psbt_obj)}, {MP_ROM_QSTR(MP_QSTR_new_passport_response), MP_ROM_PTR(&mod_foundation_ur_new_passport_response_obj)}, // Encoder. diff --git a/ports/stm32/boards/Passport/modules/flows/sign_psbt_qr_flow.py b/ports/stm32/boards/Passport/modules/flows/sign_psbt_qr_flow.py index e0489c1a3..f75eb5f26 100644 --- a/ports/stm32/boards/Passport/modules/flows/sign_psbt_qr_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/sign_psbt_qr_flow.py @@ -31,7 +31,7 @@ async def scan_transaction(self): import passport result = await ScanQRFlow(qr_types=[QRType.QR, QRType.UR2], - ur_types=[ur.Value.CRYPTO_PSBT, ur.Value.BYTES], + ur_types=[ur.Value.PSBT, ur.Value.BYTES], data_description='a PSBT file', max_frames=self.max_frames, failure_message="Unable to Scan QR code, \ @@ -70,8 +70,8 @@ async def scan_transaction(self): if isinstance(result, ur.Value): self.ur_type = result.ur_type() - if self.ur_type == ur.Value.CRYPTO_PSBT: - self.raw_psbt = result.unwrap_crypto_psbt() + if self.ur_type == ur.Value.PSBT: + self.raw_psbt = result.unwrap_psbt() elif self.ur_type == ur.Value.BYTES: self.raw_psbt = result.unwrap_bytes() else: @@ -158,8 +158,8 @@ async def show_signed_transaction(self): qr_data = b2a_hex(self.signed_bytes) else: qr_type = QRType.UR2 - if self.ur_type == ur.Value.CRYPTO_PSBT: - qr_data = ur.new_crypto_psbt(self.signed_bytes) + if self.ur_type == ur.Value.PSBT: + qr_data = ur.new_psbt(self.signed_bytes) elif self.ur_type == ur.Value.BYTES: qr_data = ur.new_bytes(self.signed_bytes) else: diff --git a/ports/stm32/boards/Passport/modules/wallets/casa.py b/ports/stm32/boards/Passport/modules/wallets/casa.py index 06fa273a6..f8626c6e8 100644 --- a/ports/stm32/boards/Passport/modules/wallets/casa.py +++ b/ports/stm32/boards/Passport/modules/wallets/casa.py @@ -34,17 +34,17 @@ def create_casa_export(sw_wallet=None, is_mainnet = chain.ctype == 'BTC' network = ur.NETWORK_MAINNET if is_mainnet else ur.NETWORK_TESTNET - use_info = ur.CryptoCoinInfo(ur.CoinType.BTC, network) - origin = ur.CryptoKeypath(source_fingerprint=int(xfp2str(settings.get('xfp')), 16), - depth=0) + use_info = ur.CoinInfo(ur.CoinType.BTC, network) + origin = ur.Keypath(source_fingerprint=int(xfp2str(settings.get('xfp')), 16), + depth=0) - crypto_hdkey = ur.new_derived_key(sv.node.public_key(), - is_private=False, - chain_code=sv.node.chain_code(), - use_info=use_info, - origin=origin) + hdkey = ur.new_derived_key(sv.node.public_key(), + is_private=False, + chain_code=sv.node.chain_code(), + use_info=use_info, + origin=origin) - return (crypto_hdkey, None) + return (hdkey, None) else: with stash.SensitiveValues() as sv: s = '''\ @@ -77,7 +77,7 @@ def create_casa_export(sw_wallet=None, 'import_microsd': read_multisig_config_from_microsd} ], 'export_modes': [ - {'id': 'qr', 'label': 'QR Code', 'qr_type': QRType.UR2, 'ur_type': 'crypto-hdkey', 'is_cbor': True}, + {'id': 'qr', 'label': 'QR Code', 'qr_type': QRType.UR2, 'ur_type': 'hdkey', 'is_cbor': True}, {'id': 'microsd', 'label': 'microSD', 'filename_pattern': '{xfp}-casa.txt', 'ext': '.txt', 'filename_pattern_multisig': '{xfp}-casa-multisig.txt', 'ext_multisig': '.txt'} ], From 0426e9fac6e8e50cea94050abdafcc5706fc6990 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Mon, 15 Jul 2024 12:00:44 +0200 Subject: [PATCH 045/126] SFT-3812: Update foundation-ur to 0.3.0. * extmod/foundation-rust/Cargo.lock: Regenerate. * extmod/foundation-rust/Cargo.toml (dependencies) : Update to 0.3.0 --- extmod/foundation-rust/Cargo.lock | 4 ++-- extmod/foundation-rust/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extmod/foundation-rust/Cargo.lock b/extmod/foundation-rust/Cargo.lock index e27afe455..d9f47174d 100644 --- a/extmod/foundation-rust/Cargo.lock +++ b/extmod/foundation-rust/Cargo.lock @@ -142,9 +142,9 @@ checksum = "7e6bdea1eeaa5edb5355234d02f81c6dbdd4bc5146887990dd29a213029bbeae" [[package]] name = "foundation-ur" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a803ede27fdd585c226d4d70d3da984f5ed01efeafeed1a1eea8bc62e8c2e622" +checksum = "71ead93a082dfa0a11c2bb43242a1cdc93b1e7976d768465b54a0703dbd3b003" dependencies = [ "bitcoin_hashes 0.14.0", "crc", diff --git a/extmod/foundation-rust/Cargo.toml b/extmod/foundation-rust/Cargo.toml index 161fc4a37..47bdd0ddc 100644 --- a/extmod/foundation-rust/Cargo.toml +++ b/extmod/foundation-rust/Cargo.toml @@ -29,7 +29,7 @@ version = "1" default-features = false [dependencies.foundation-ur] -version = "0.2" +version = "0.3" default-features = false [dependencies.foundation-urtypes] From 1932a8b705f3aad5339a674dc05a0cf54e209548 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Mon, 15 Jul 2024 12:04:42 +0200 Subject: [PATCH 046/126] SFT-3812: Update Cargo.lock. * extmod/foundation-rust/Cargo.lock: Regenerate. --- extmod/foundation-rust/Cargo.lock | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/extmod/foundation-rust/Cargo.lock b/extmod/foundation-rust/Cargo.lock index d9f47174d..c3a475c42 100644 --- a/extmod/foundation-rust/Cargo.lock +++ b/extmod/foundation-rust/Cargo.lock @@ -39,7 +39,7 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" dependencies = [ - "hex-conservative 0.2.0", + "hex-conservative 0.2.1", ] [[package]] @@ -56,9 +56,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.0.98" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "324c74f2155653c90b04f25b2a47a8a631360cb908f92a772695f430c7e31052" [[package]] name = "cfg-if" @@ -102,9 +102,9 @@ checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" [[package]] name = "either" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "embedded-hal" @@ -212,9 +212,9 @@ checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20" [[package]] name = "hex-conservative" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1aa273bf451e37ed35ced41c71a5e2a4e29064afb104158f2514bcd71c2c986" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" dependencies = [ "arrayvec", ] @@ -236,9 +236,9 @@ checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "minicbor" -version = "0.24.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a1359d4b3ec786cdc1e44382c8c0e10965d95a173059590f62314c73be72969" +checksum = "5f8e213c36148d828083ae01948eed271d03f95f7e72571fa242d78184029af2" dependencies = [ "minicbor-derive", ] @@ -335,9 +335,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.83" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -447,9 +447,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "syn" -version = "2.0.65" +version = "2.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" +checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" dependencies = [ "proc-macro2", "quote", @@ -464,9 +464,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "uuid" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" [[package]] name = "vcell" From cef8825c94181e8bdc35e9cb62f8a5150148e69a Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Mon, 15 Jul 2024 12:05:08 +0200 Subject: [PATCH 047/126] SFT-3812: Remove unused fields. * ports/stm32/boards/Passport/modules/wallets/casa.py (CasaWallet): Remove ur_type and is_cbor since these are not used in the codebase. --- ports/stm32/boards/Passport/modules/wallets/casa.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/boards/Passport/modules/wallets/casa.py b/ports/stm32/boards/Passport/modules/wallets/casa.py index f8626c6e8..09655bffb 100644 --- a/ports/stm32/boards/Passport/modules/wallets/casa.py +++ b/ports/stm32/boards/Passport/modules/wallets/casa.py @@ -77,7 +77,7 @@ def create_casa_export(sw_wallet=None, 'import_microsd': read_multisig_config_from_microsd} ], 'export_modes': [ - {'id': 'qr', 'label': 'QR Code', 'qr_type': QRType.UR2, 'ur_type': 'hdkey', 'is_cbor': True}, + {'id': 'qr', 'label': 'QR Code', 'qr_type': QRType.UR2}, {'id': 'microsd', 'label': 'microSD', 'filename_pattern': '{xfp}-casa.txt', 'ext': '.txt', 'filename_pattern_multisig': '{xfp}-casa-multisig.txt', 'ext_multisig': '.txt'} ], From 8df7ef04c094169824357c967008cf8c6a4e6046 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Wed, 10 Jul 2024 17:37:59 +0200 Subject: [PATCH 048/126] SFT-3800: Fix wrong exception type. * extmod/foundation/modfoundation-ur.h (mod_foundation_ur_globals_table) : Use mp_type_NotMultiPartError. --- extmod/foundation/modfoundation-ur.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extmod/foundation/modfoundation-ur.h b/extmod/foundation/modfoundation-ur.h index 8187006d1..c2c30e8fc 100644 --- a/extmod/foundation/modfoundation-ur.h +++ b/extmod/foundation/modfoundation-ur.h @@ -661,7 +661,7 @@ STATIC const mp_rom_map_elem_t mod_foundation_ur_globals_table[] = { /// Errors. {MP_ROM_QSTR(MP_QSTR_OtherError), MP_ROM_PTR(&mp_type_OtherError)}, {MP_ROM_QSTR(MP_QSTR_UnsupportedError), MP_ROM_PTR(&mp_type_UnsupportedError)}, - {MP_ROM_QSTR(MP_QSTR_NotMultiPartError), MP_ROM_PTR(&mp_type_UnsupportedError)}, + {MP_ROM_QSTR(MP_QSTR_NotMultiPartError), MP_ROM_PTR(&mp_type_NotMultiPartError)}, // Value. {MP_ROM_QSTR(MP_QSTR_NETWORK_MAINNET), MP_ROM_INT(UR_NETWORK_MAINNET)}, From 97e7d22d96bfc4cbbdcbf0cce2de2c1fbd0b4322 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Wed, 10 Jul 2024 17:53:15 +0200 Subject: [PATCH 049/126] SFT-3800: Add "QR too large" error message. * extmod/foundation-rust/include/foundation.h: Regenerate. * extmod/foundation-rust/src/ur/decoder.rs (UR_ErrorKind) : New variant. * extmod/foundation-rust/src/ur/mod.rs (UR_Error) : New procedure. * extmod/foundation/modfoundation-ur.h (mp_type_TooBigError): New exception. * ports/stm32/boards/Passport/modules/data_codecs/ur2_codec.py (UR2Decoder): Add specific message for ur.TooBigError. * ports/stm32/boards/Passport/modules/flows/sign_psbt_qr_flow.py (SignPsbtQrFlow) : Remove QR too large condition. --- extmod/foundation-rust/include/foundation.h | 1 + extmod/foundation-rust/src/ur/decoder.rs | 19 ++---------- extmod/foundation-rust/src/ur/mod.rs | 30 +++++++++++++++++++ extmod/foundation/modfoundation-ur.h | 9 ++++++ .../Passport/modules/data_codecs/ur2_codec.py | 2 ++ .../Passport/modules/flows/scan_qr_flow.py | 4 +++ .../modules/flows/sign_psbt_qr_flow.py | 4 +-- .../Passport/modules/pages/scan_qr_page.py | 6 +++- 8 files changed, 54 insertions(+), 21 deletions(-) diff --git a/extmod/foundation-rust/include/foundation.h b/extmod/foundation-rust/include/foundation.h index 7696c3dbc..57fa75be9 100644 --- a/extmod/foundation-rust/include/foundation.h +++ b/extmod/foundation-rust/include/foundation.h @@ -80,6 +80,7 @@ typedef enum { typedef enum { UR_ERROR_KIND_OTHER, + UR_ERROR_KIND_TOO_BIG, UR_ERROR_KIND_UNSUPPORTED, UR_ERROR_KIND_NOT_MULTI_PART, } UR_ErrorKind; diff --git a/extmod/foundation-rust/src/ur/decoder.rs b/extmod/foundation-rust/src/ur/decoder.rs index 17e22fb99..1b643bb8d 100644 --- a/extmod/foundation-rust/src/ur/decoder.rs +++ b/extmod/foundation-rust/src/ur/decoder.rs @@ -3,7 +3,7 @@ //! Decoder. -use core::{fmt, ptr, slice, str}; +use core::{ptr, slice, str}; use foundation_ur::{ bytewords, bytewords::Style, decoder::Error, max_fragment_len, @@ -96,7 +96,7 @@ pub unsafe extern "C" fn ur_decoder_receive( .and_then(|ur| match ur.sequence_count() { Some(n) if n > UR_DECODER_MAX_SEQUENCE_COUNT as u32 => { Err(unsafe { - UR_Error::other(&TooManySequences { sequence_count: n }) + UR_Error::too_big(n, UR_DECODER_MAX_SEQUENCE_COUNT) }) } _ => Ok(ur), @@ -281,18 +281,3 @@ pub unsafe extern "C" fn ur_decode_single_part( true } - -struct TooManySequences { - sequence_count: u32, -} - -impl fmt::Display for TooManySequences { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "The UR contains more sequences than we can handle.\n\nMaximum sequence count supported: {}.\n\nMessage sequence count: {}.", - UR_DECODER_MAX_SEQUENCE_COUNT, - self.sequence_count, - ) - } -} diff --git a/extmod/foundation-rust/src/ur/mod.rs b/extmod/foundation-rust/src/ur/mod.rs index 855474502..fb5170516 100644 --- a/extmod/foundation-rust/src/ur/mod.rs +++ b/extmod/foundation-rust/src/ur/mod.rs @@ -24,6 +24,7 @@ const fn max_message_len(max_characters: usize) -> usize { #[repr(C)] pub enum UR_ErrorKind { UR_ERROR_KIND_OTHER, + UR_ERROR_KIND_TOO_BIG, UR_ERROR_KIND_UNSUPPORTED, UR_ERROR_KIND_NOT_MULTI_PART, } @@ -65,6 +66,35 @@ impl UR_Error { Self::new(message, UR_ErrorKind::UR_ERROR_KIND_OTHER) } + /// # Safety + /// + /// The same as in [`UR_Error::new`]. + pub unsafe fn too_big(sequence_count: u32, max: usize) -> Self { + struct Error { + sequence_count: u32, + max: usize, + } + + impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "The UR contains more sequences than we can handle.\n\nMaximum sequence count supported: {}.\n\nMessage sequence count: {}.", + self.max, + self.sequence_count, + ) + } + } + + Self::new( + &Error { + sequence_count, + max, + }, + UR_ErrorKind::UR_ERROR_KIND_TOO_BIG, + ) + } + /// # Safety /// /// The same as in [`UR_Error::new`]. diff --git a/extmod/foundation/modfoundation-ur.h b/extmod/foundation/modfoundation-ur.h index c2c30e8fc..b9671eadd 100644 --- a/extmod/foundation/modfoundation-ur.h +++ b/extmod/foundation/modfoundation-ur.h @@ -23,6 +23,11 @@ STATIC NORETURN void mod_foundation_ur_raise(UR_Error *error); /// """ STATIC MP_DEFINE_EXCEPTION(OtherError, Exception); +/// class TooBigError(Exception): +/// """ +/// """ +STATIC MP_DEFINE_EXCEPTION(TooBigError, Exception); + /// class UnsupportedError(Exception): /// """ /// """ @@ -40,6 +45,9 @@ STATIC NORETURN void mod_foundation_ur_raise(UR_Error *error) { case UR_ERROR_KIND_OTHER: type = &mp_type_OtherError; break; + case UR_ERROR_KIND_TOO_BIG: + type = &mp_type_TooBigError; + break; case UR_ERROR_KIND_UNSUPPORTED: type = &mp_type_UnsupportedError; break; @@ -660,6 +668,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_foundation_ur_decode_single_part_obj, STATIC const mp_rom_map_elem_t mod_foundation_ur_globals_table[] = { /// Errors. {MP_ROM_QSTR(MP_QSTR_OtherError), MP_ROM_PTR(&mp_type_OtherError)}, + {MP_ROM_QSTR(MP_QSTR_TooBigError), MP_ROM_PTR(&mp_type_TooBigError)}, {MP_ROM_QSTR(MP_QSTR_UnsupportedError), MP_ROM_PTR(&mp_type_UnsupportedError)}, {MP_ROM_QSTR(MP_QSTR_NotMultiPartError), MP_ROM_PTR(&mp_type_NotMultiPartError)}, diff --git a/ports/stm32/boards/Passport/modules/data_codecs/ur2_codec.py b/ports/stm32/boards/Passport/modules/data_codecs/ur2_codec.py index 7de70c880..48fc9e68b 100644 --- a/ports/stm32/boards/Passport/modules/data_codecs/ur2_codec.py +++ b/ports/stm32/boards/Passport/modules/data_codecs/ur2_codec.py @@ -42,6 +42,8 @@ def add_data(self, data): except ur.UnsupportedError: raise DecodeError("""\ Please check for updates to Passport and your software wallet.""") + except ur.TooBigError as exc: + raise exc # Re-raise as ScanQRResult has a method to check it. except ur.OtherError as exc: raise DecodeError(str(exc)) diff --git a/ports/stm32/boards/Passport/modules/flows/scan_qr_flow.py b/ports/stm32/boards/Passport/modules/flows/scan_qr_flow.py index 9c1476dd6..ecaf87dc4 100644 --- a/ports/stm32/boards/Passport/modules/flows/scan_qr_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/scan_qr_flow.py @@ -63,6 +63,10 @@ async def scan(self): self.set_result(None) return + if result.is_ur_too_big(): + self.set_result(Error.QR_TOO_LARGE) + return + if result.is_failure(): await LongErrorPage(text=self.failure_message.format(result.error)).show() self.set_result(None) diff --git a/ports/stm32/boards/Passport/modules/flows/sign_psbt_qr_flow.py b/ports/stm32/boards/Passport/modules/flows/sign_psbt_qr_flow.py index e0489c1a3..775ab0b8b 100644 --- a/ports/stm32/boards/Passport/modules/flows/sign_psbt_qr_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/sign_psbt_qr_flow.py @@ -61,9 +61,7 @@ async def scan_transaction(self): return # Run it again with no max frames if the user wants if result == Error.QR_TOO_LARGE: - await ErrorPage("This transaction is too large for QR signing. \ -\nSpend fewer coins or sign via microSD card.").show() - + await ErrorPage("QR code is too large, please sign with microSD.").show() self.set_result(False) return diff --git a/ports/stm32/boards/Passport/modules/pages/scan_qr_page.py b/ports/stm32/boards/Passport/modules/pages/scan_qr_page.py index 14c2a76c0..57a18ec34 100644 --- a/ports/stm32/boards/Passport/modules/pages/scan_qr_page.py +++ b/ports/stm32/boards/Passport/modules/pages/scan_qr_page.py @@ -4,6 +4,7 @@ import lvgl as lv from data_codecs.data_decoder import DecodeError +from foundation import ur from pages import Page from styles.colors import TEXT_GREY from styles.style import Stylize @@ -121,7 +122,7 @@ def update(self): qr_type = self.camera.qr_decoder.qr_type() self.set_result(QRScanResult(data=data, qr_type=qr_type)) - except DecodeError as exc: + except Exception as exc: self.set_result(QRScanResult(error=exc)) # Just return None. @@ -153,5 +154,8 @@ def __init__(self, data=None, error=None, qr_type=None, num_frames=None, max_fra def is_failure(self): return self.error is not None + def is_ur_too_big(self): + return isinstance(self.error, ur.TooBigError) + def is_oversized(self): return self.max_frames is not None and self.num_frames > self.max_frames From 736b522092f746520a0a596d2801035255813673 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Mon, 8 Apr 2024 16:58:18 +0200 Subject: [PATCH 050/126] SFT-3536: Add foundation-firmware crate. * Cargo.lock: Update lockfile. * Cargo.toml (dependencies) : New dependency. --- extmod/foundation-rust/Cargo.lock | 63 +++++++++++++++++++------------ extmod/foundation-rust/Cargo.toml | 6 ++- 2 files changed, 43 insertions(+), 26 deletions(-) diff --git a/extmod/foundation-rust/Cargo.lock b/extmod/foundation-rust/Cargo.lock index c3a475c42..a4a9d73ef 100644 --- a/extmod/foundation-rust/Cargo.lock +++ b/extmod/foundation-rust/Cargo.lock @@ -17,29 +17,13 @@ dependencies = [ "rustc_version", ] -[[package]] -name = "bitcoin-internals" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" - -[[package]] -name = "bitcoin_hashes" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" -dependencies = [ - "bitcoin-internals", - "hex-conservative 0.1.2", -] - [[package]] name = "bitcoin_hashes" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" dependencies = [ - "hex-conservative 0.2.1", + "hex-conservative", ] [[package]] @@ -120,9 +104,10 @@ dependencies = [ name = "foundation" version = "0.1.0" dependencies = [ - "bitcoin_hashes 0.13.0", + "bitcoin_hashes", "cortex-m", "critical-section", + "foundation-firmware", "foundation-ur", "foundation-urtypes", "heapless", @@ -140,13 +125,25 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e6bdea1eeaa5edb5355234d02f81c6dbdd4bc5146887990dd29a213029bbeae" +[[package]] +name = "foundation-firmware" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a0d5d35a9470dea4fc94d5b04fafdf587d4c642e1f8de97879bb0f99fff96a" +dependencies = [ + "bitcoin_hashes", + "heapless", + "nom", + "secp256k1", +] + [[package]] name = "foundation-ur" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71ead93a082dfa0a11c2bb43242a1cdc93b1e7976d768465b54a0703dbd3b003" dependencies = [ - "bitcoin_hashes 0.14.0", + "bitcoin_hashes", "crc", "heapless", "itertools", @@ -204,12 +201,6 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -[[package]] -name = "hex-conservative" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20" - [[package]] name = "hex-conservative" version = "0.2.1" @@ -234,6 +225,12 @@ version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + [[package]] name = "minicbor" version = "0.24.2" @@ -254,6 +251,12 @@ dependencies = [ "syn", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "nb" version = "0.1.3" @@ -269,6 +272,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" +[[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 = "once_cell" version = "1.19.0" diff --git a/extmod/foundation-rust/Cargo.toml b/extmod/foundation-rust/Cargo.toml index 47bdd0ddc..3e20a7830 100644 --- a/extmod/foundation-rust/Cargo.toml +++ b/extmod/foundation-rust/Cargo.toml @@ -12,7 +12,7 @@ name = "sizes" required-features = ["std"] [dependencies.bitcoin_hashes] -version = "0.13" +version = "0.14" features = ["small-hash"] default-features = false @@ -28,6 +28,10 @@ default-features = false version = "1" default-features = false +[dependencies.foundation-firmware] +version = "0.1" +default-features = false + [dependencies.foundation-ur] version = "0.3" default-features = false From 7003eaa498867cbc9245aeba2920a69953a78bd8 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Fri, 26 Apr 2024 17:15:32 +0200 Subject: [PATCH 051/126] SFT-3536: Validate header with foundation-firmware. * extmod/foundation-rust/include/foundation.h: Update bindings. * extmod/foundation-rust/src/firmware.rs: New module. * extmod/foundation-rust/src/lib.rs (firmware): Register module. * ports/stm32/boards/Passport/modpassport-system.h (mod_passport_System_validate_firmware_header): Remove procedure. (mod_passport_System_validate_firmware_header_obj): Remove variable. (mod_passport_System_locals_dict_table): Remove validate_firmware_header. * ports/stm32/boards/Passport/modpassport.c (mod_passport_verify_update_header): New procedure. (mod_passport_verify_update_header_obj): New variable. (passport_module_globals_table): Add verify_update_header. * ports/stm32/boards/Passport/modules/flows/update_firmware_flow.py (UpdateFirmwareFlow) : Use verify_update_header instead of validate_firmware_header. * ports/stm32/boards/Passport/modules/tasks/verify_firmware_signature_task.py: Add new task. --- extmod/foundation-rust/include/foundation.h | 129 +++++++++++ extmod/foundation-rust/src/firmware.rs | 212 ++++++++++++++++++ extmod/foundation-rust/src/lib.rs | 1 + extmod/foundation-rust/src/secp256k1.rs | 2 +- ports/stm32/boards/Passport/manifest.py | 3 +- .../boards/Passport/modpassport-system.h | 116 +--------- ports/stm32/boards/Passport/modpassport.c | 187 +++++++++++++++ .../boards/Passport/modules/constants.py | 1 + .../modules/flows/update_firmware_flow.py | 41 +++- .../boards/Passport/modules/tasks/__init__.py | 1 + .../tasks/verify_firmware_signature_task.py | 72 ++++++ 11 files changed, 641 insertions(+), 124 deletions(-) create mode 100644 extmod/foundation-rust/src/firmware.rs create mode 100644 ports/stm32/boards/Passport/modules/tasks/verify_firmware_signature_task.py diff --git a/extmod/foundation-rust/include/foundation.h b/extmod/foundation-rust/include/foundation.h index eb0cc5abc..f39dac472 100644 --- a/extmod/foundation-rust/include/foundation.h +++ b/extmod/foundation-rust/include/foundation.h @@ -18,6 +18,8 @@ #include #include +#define VERSION_LEN 8 + /** * Maximum size of an encoded Uniform Resource. * @@ -108,6 +110,118 @@ typedef struct UR_Decoder UR_Decoder; */ typedef struct UR_Encoder UR_Encoder; +/** + * The result of the firmware update verification. + */ +typedef enum { + /** + * The firmware validation succeed. + */ + FIRMWARE_RESULT_HEADER_OK, + /** + * The header format is not valid. + */ + FIRMWARE_RESULT_INVALID_HEADER, + /** + * Unknown magic number. + */ + FIRMWARE_RESULT_UNKNOWN_MAGIC, + /** + * The timestamp field is invalid. + */ + FIRMWARE_RESULT_INVALID_TIMESTAMP, + /** + * The firmware is too small. + */ + FIRMWARE_RESULT_TOO_SMALL, + /** + * The firmware is too big. + */ + FIRMWARE_RESULT_TOO_BIG, + /** + * The firmware is older than the current firmware. + */ + FIRMWARE_RESULT_TOO_OLD, + /** + * Public Key 1 is out of range. + */ + FIRMWARE_RESULT_INVALID_PUBLIC_KEY1_INDEX, + /** + * Public Key 2 is out of range. + */ + FIRMWARE_RESULT_INVALID_PUBLIC_KEY2_INDEX, + /** + * The same public key was used for the two signatures. + */ + FIRMWARE_RESULT_SAME_PUBLIC_KEY, + /** + * Signature verification succeed. + */ + FIRMWARE_RESULT_SIGNATURES_OK, + /** + * The user signed firmware is not valid. + */ + FIRMWARE_RESULT_INVALID_USER_SIGNATURE, + /** + * The first signature verification failed. + */ + FIRMWARE_RESULT_FAILED_SIGNATURE1, + /** + * The second signature verification failed. + */ + FIRMWARE_RESULT_FAILED_SIGNATURE2, +} FirmwareResult_Tag; + +typedef struct { + char version[VERSION_LEN]; + bool signed_by_user; +} FirmwareResult_HeaderOk_Body; + +typedef struct { + uint32_t magic; +} FirmwareResult_UnknownMagic_Body; + +typedef struct { + uint32_t len; +} FirmwareResult_TooSmall_Body; + +typedef struct { + uint32_t len; +} FirmwareResult_TooBig_Body; + +typedef struct { + uint32_t timestamp; +} FirmwareResult_TooOld_Body; + +typedef struct { + uint32_t index; +} FirmwareResult_InvalidPublicKey1Index_Body; + +typedef struct { + uint32_t index; +} FirmwareResult_InvalidPublicKey2Index_Body; + +typedef struct { + /** + * Index of the duplicated key. + */ + uint32_t index; +} FirmwareResult_SamePublicKey_Body; + +typedef struct { + FirmwareResult_Tag tag; + union { + FirmwareResult_HeaderOk_Body HEADER_OK; + FirmwareResult_UnknownMagic_Body UNKNOWN_MAGIC; + FirmwareResult_TooSmall_Body TOO_SMALL; + FirmwareResult_TooBig_Body TOO_BIG; + FirmwareResult_TooOld_Body TOO_OLD; + FirmwareResult_InvalidPublicKey1Index_Body INVALID_PUBLIC_KEY1_INDEX; + FirmwareResult_InvalidPublicKey2Index_Body INVALID_PUBLIC_KEY2_INDEX; + FirmwareResult_SamePublicKey_Body SAME_PUBLIC_KEY; + }; +} FirmwareResult; + typedef struct { UR_ErrorKind kind; const char *message; @@ -360,6 +474,21 @@ extern UR_Decoder UR_DECODER; extern UR_Encoder UR_ENCODER; +/** + * Verify the header of a firmware update. + */ +void foundation_firmware_verify_update_header(const uint8_t *header, + size_t header_len, + uint32_t current_timestamp, + FirmwareResult *result); + +void foundation_firmware_verify_update_signatures(const uint8_t *header, + size_t header_len, + uint32_t current_timestamp, + const uint8_t (*hash)[32], + const uint8_t (*user_public_key)[64], + FirmwareResult *result); + /** * Calculate a "Schnorr" public key from the secret key. * diff --git a/extmod/foundation-rust/src/firmware.rs b/extmod/foundation-rust/src/firmware.rs new file mode 100644 index 000000000..87f943032 --- /dev/null +++ b/extmod/foundation-rust/src/firmware.rs @@ -0,0 +1,212 @@ +// SPDX-FileCopyrightText: 2024 Foundation Devices, Inc. +// SPDX-License-Identifier: GPL-3.0-or-later + +use crate::secp256k1::PRE_ALLOCATED_CTX; +use bitcoin_hashes::{sha256d, Hash}; +use core::{ffi::c_char, slice}; +use foundation_firmware::{VerifyHeaderError, VerifySignatureError}; +use secp256k1::PublicKey; + +pub const VERSION_LEN: usize = 8; + +/// The result of the firmware update verification. +/// cbindgen:rename-all=ScreamingSnakeCase +/// cbindgen:prefix-with-name +#[repr(C)] +pub enum FirmwareResult { + // Header. + /// The firmware validation succeed. + HeaderOk { + version: [c_char; VERSION_LEN], + signed_by_user: bool, + }, + /// The header format is not valid. + InvalidHeader, + /// Unknown magic number. + UnknownMagic { magic: u32 }, + /// The timestamp field is invalid. + InvalidTimestamp, + /// The firmware is too small. + TooSmall { len: u32 }, + /// The firmware is too big. + TooBig { len: u32 }, + /// The firmware is older than the current firmware. + TooOld { + timestamp: u32, + // version: *const c_char, + }, + /// Public Key 1 is out of range. + InvalidPublicKey1Index { index: u32 }, + /// Public Key 2 is out of range. + InvalidPublicKey2Index { index: u32 }, + /// The same public key was used for the two signatures. + SamePublicKey { + /// Index of the duplicated key. + index: u32, + }, + // Signatures. + /// Signature verification succeed. + SignaturesOk, + /// The user signed firmware is not valid. + InvalidUserSignature, + /// The first signature verification failed. + FailedSignature1, + /// The second signature verification failed. + FailedSignature2, +} + +impl From for FirmwareResult { + fn from(e: VerifyHeaderError) -> Self { + use FirmwareResult::*; + + match e { + VerifyHeaderError::UnknownMagic(magic) => UnknownMagic { magic }, + VerifyHeaderError::InvalidTimestamp => InvalidTimestamp, + VerifyHeaderError::FirmwareTooSmall(len) => TooSmall { len }, + VerifyHeaderError::FirmwareTooBig(len) => TooBig { len }, + VerifyHeaderError::InvalidPublicKey1Index(index) => { + InvalidPublicKey1Index { index } + } + VerifyHeaderError::InvalidPublicKey2Index(index) => { + InvalidPublicKey2Index { index } + } + VerifyHeaderError::SamePublicKeys(index) => SamePublicKey { index }, + } + } +} + +impl From for FirmwareResult { + fn from(e: VerifySignatureError) -> Self { + use FirmwareResult::*; + + match e { + VerifySignatureError::InvalidUserSignature { .. } => { + InvalidUserSignature + } + VerifySignatureError::FailedSignature1 { .. } => FailedSignature1, + VerifySignatureError::FailedSignature2 { .. } => FailedSignature2, + // No need to implement this, see comment on + // verify_update_signatures. + VerifySignatureError::MissingUserPublicKey => unimplemented!(), + } + } +} + +fn verify_update_header_impl( + header: &[u8], + current_timestamp: u32, + result: &mut FirmwareResult, +) -> Option { + let header = match foundation_firmware::header(header) { + Ok((_, header)) => header, + Err(_) => { + *result = FirmwareResult::InvalidHeader; + return None; + } + }; + + if let Err(e) = header.verify() { + *result = FirmwareResult::from(e); + return None; + } + + if header.information.timestamp < current_timestamp { + *result = FirmwareResult::TooOld { + timestamp: header.information.timestamp, + }; + return None; + } + + Some(header) +} + +/// Verify the header of a firmware update. +#[export_name = "foundation_firmware_verify_update_header"] +pub extern "C" fn verify_update_header( + header: *const u8, + header_len: usize, + current_timestamp: u32, + result: &mut FirmwareResult, +) { + let header = unsafe { slice::from_raw_parts(header, header_len) }; + + match verify_update_header_impl(header, current_timestamp, result) { + Some(header) => { + let version_bytes = header.information.version.as_bytes(); + let mut version = [0; VERSION_LEN]; + for (i, &b) in version_bytes.iter().enumerate() { + version[i] = b as c_char; + } + version[version_bytes.len()] = b'\0' as c_char; + + *result = FirmwareResult::HeaderOk { + version, + signed_by_user: header.is_signed_by_user(), + }; + } + // Verification failed. + None => (), + } +} + +#[export_name = "foundation_firmware_verify_update_signatures"] +pub extern "C" fn verify_update_signatures( + header: *const u8, + header_len: usize, + current_timestamp: u32, + hash: &[u8; 32], + user_public_key: Option<&[u8; 64]>, + result: &mut FirmwareResult, +) { + let header = unsafe { slice::from_raw_parts(header, header_len) }; + let firmware_hash = sha256d::Hash::from_slice(hash) + .expect("hash should be of correct length"); + + let user_public_key = user_public_key + .map(|v| { + let mut buf = [0; 65]; + buf[0] = 0x04; + (&mut buf[1..]).copy_from_slice(v); + buf + }) + .map(|v| { + PublicKey::from_slice(&v).expect("user public key should be valid") + }); + + let header = + match verify_update_header_impl(header, current_timestamp, result) { + Some(header) => header, + None => return, + }; + + match foundation_firmware::verify_signature( + &PRE_ALLOCATED_CTX, + &header, + &firmware_hash, + user_public_key.as_ref(), + ) { + Ok(()) => { + *result = FirmwareResult::SignaturesOk; + } + // The code calling this function must make sure that there's an user + // public key provided to us before verifying signatures. + // + // When verifying signatures is because we are committed to the + // update, i.e. the user has accepted to do it, so we must have + // presented an error if there was not an user public key earlier. + Err(VerifySignatureError::MissingUserPublicKey) => { + unreachable!("we always provide a user public key") + } + Err(e) => *result = FirmwareResult::from(e), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn sanity_test() { + assert_eq!(VERSION_LEN, foundation_firmware::VERSION_LEN); + } +} diff --git a/extmod/foundation-rust/src/lib.rs b/extmod/foundation-rust/src/lib.rs index c6b3751a7..144f1d155 100644 --- a/extmod/foundation-rust/src/lib.rs +++ b/extmod/foundation-rust/src/lib.rs @@ -7,6 +7,7 @@ #[cfg(target_arch = "arm")] use cortex_m as _; +pub mod firmware; pub mod secp256k1; pub mod ur; diff --git a/extmod/foundation-rust/src/secp256k1.rs b/extmod/foundation-rust/src/secp256k1.rs index 10982c081..c3ec7b387 100644 --- a/extmod/foundation-rust/src/secp256k1.rs +++ b/extmod/foundation-rust/src/secp256k1.rs @@ -12,7 +12,7 @@ use secp256k1::{ static mut PRE_ALLOCATED_CTX_BUF: [AlignedType; 20] = [AlignedType::ZERO; 20]; /// cbindgen:ignore -static PRE_ALLOCATED_CTX: Lazy>> = +pub static PRE_ALLOCATED_CTX: Lazy>> = Lazy::new(|| { // SAFETY: // diff --git a/ports/stm32/boards/Passport/manifest.py b/ports/stm32/boards/Passport/manifest.py index e5b1f0ce1..62cb6d2eb 100644 --- a/ports/stm32/boards/Passport/manifest.py +++ b/ports/stm32/boards/Passport/manifest.py @@ -287,7 +287,8 @@ 'tasks/sign_text_file_task.py', 'tasks/validate_electrum_message_task.py', 'tasks/validate_psbt_task.py', - 'tasks/verify_backup_task.py')) + 'tasks/verify_backup_task.py', + 'tasks/verify_firmware_signature_task.py')) # Translations freeze('$(MPY_DIR)/ports/stm32/boards/Passport/modules', diff --git a/ports/stm32/boards/Passport/modpassport-system.h b/ports/stm32/boards/Passport/modpassport-system.h index c34c4d30c..d6578af14 100644 --- a/ports/stm32/boards/Passport/modpassport-system.h +++ b/ports/stm32/boards/Passport/modpassport-system.h @@ -23,6 +23,8 @@ #include "adc.h" #include "utils.h" +#include "foundation.h" + #define SYSTEM_DEBUG 0 #define SECRETS_FLASH_START (0x81C0000) #define SECRETS_FLASH_SIZE (0x20000) @@ -366,117 +368,6 @@ STATIC mp_obj_t mod_passport_System_busy_wait(mp_obj_t self, mp_obj_t _delay_ms) } STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_passport_System_busy_wait_obj, mod_passport_System_busy_wait); -// Simple header verification -STATIC bool verify_header(passport_firmware_header_t* hdr) { -#ifdef SCREEN_MODE_MONO - if (hdr->info.magic != FW_HEADER_MAGIC) return false; -#elif SCREEN_MODE_COLOR - if (hdr->info.magic != FW_HEADER_MAGIC_COLOR) return false; -#endif - if (hdr->info.timestamp == 0) return false; - if (hdr->info.fwversion[0] == 0x0) return false; - if (hdr->info.fwlength < FW_HEADER_SIZE) return false; - if (hdr->info.fwlength > FW_MAX_SIZE) return false; - - // Make sure pubkey indices are in range and are not the same pubkey - if ((hdr->signature.pubkey1 != FW_USER_KEY) && (hdr->signature.pubkey1 >= FW_MAX_PUB_KEYS)) return false; - if (hdr->signature.pubkey2 >= FW_MAX_PUB_KEYS) return false; - if (hdr->signature.pubkey1 == hdr->signature.pubkey2) return false; - - return true; -} - -/// def validate_firmware_header(self, header: bytearray) -> None: -/// ''' -/// Validate the given firmware header bytes as a potential candidate to be -/// installed. -/// ''' -STATIC mp_obj_t mod_passport_System_validate_firmware_header(mp_obj_t self, mp_obj_t header) { - mp_buffer_info_t header_info; - mp_get_buffer_raise(header, &header_info, MP_BUFFER_READ); - - // Existing header - passport_firmware_header_t* fwhdr = (passport_firmware_header_t*)FW_HDR; - - // New header - passport_firmware_header_t* new_fwhdr = (passport_firmware_header_t*)header_info.buf; - - mp_obj_t tuple[4]; - - bool is_valid = verify_header(header_info.buf); - - if (is_valid) { - // Build the current board hash so we can get the minimum firmware timestamp - uint8_t current_board_hash[HASH_LEN] = {0}; - get_current_board_hash(current_board_hash); - - uint32_t firmware_timestamp = se_get_firmware_timestamp(current_board_hash); - - // Ensure they are not trying to install an older version of firmware, but allow - // a reinstall of the same version. Also allow installation of user firmware regardless of - // timestamp and then allow installing a Foundation-signed build. - if ((new_fwhdr->signature.pubkey1 != FW_USER_KEY) && (new_fwhdr->info.timestamp < firmware_timestamp)) { - tuple[0] = mp_const_false; - tuple[1] = mp_obj_new_str_copy(&mp_type_str, (const uint8_t*)new_fwhdr->info.fwversion, - strlen((const char*)new_fwhdr->info.fwversion)); - - // Include an error string - vstr_t vstr; - vstr_init(&vstr, 120); - - char* msg = "The selected firmware is older than the currently installed firmware."; - vstr_add_strn(&vstr, (const char*)msg, strlen(msg)); - - vstr_add_strn(&vstr, (const char*)fwhdr->info.fwdate, strlen((const char*)new_fwhdr->info.fwdate)); - - msg = "\n\nSelected Version:\n "; - vstr_add_strn(&vstr, (const char*)msg, strlen(msg)); - - vstr_add_strn(&vstr, (const char*)new_fwhdr->info.fwdate, strlen((const char*)new_fwhdr->info.fwdate)); - tuple[2] = mp_obj_new_str_from_vstr(&mp_type_str, &vstr); - - // Is this user-signed firmware? - tuple[3] = mp_const_false; - - return mp_obj_new_tuple(4, tuple); - } - } else { - // Invalid header - tuple[0] = mp_const_false; - tuple[1] = mp_obj_new_str_copy(&mp_type_str, (const uint8_t*)new_fwhdr->info.fwversion, - strlen((const char*)new_fwhdr->info.fwversion)); - - // Include an error string - vstr_t vstr; - vstr_init(&vstr, 80); - char* msg = "The selected firmware header is invalid and cannot be installed."; - vstr_add_strn(&vstr, (const char*)msg, strlen(msg)); - tuple[2] = mp_obj_new_str_from_vstr(&mp_type_str, &vstr); - - // No header = no user signed firmware - tuple[3] = mp_const_false; - - return mp_obj_new_tuple(4, tuple); - } - - // is_valid - tuple[0] = mp_const_true; - - // Firmware version - tuple[1] = mp_obj_new_str_copy(&mp_type_str, (const uint8_t*)new_fwhdr->info.fwversion, - strlen((const char*)new_fwhdr->info.fwversion)); - - // No error message - tuple[2] = mp_const_none; - - // Is this user-signed firmware? - tuple[3] = (new_fwhdr->signature.pubkey1 == FW_USER_KEY) ? mp_const_true : mp_const_false; - - return mp_obj_new_tuple(4, tuple); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_passport_System_validate_firmware_header_obj, - mod_passport_System_validate_firmware_header); - /// def mod_passport_System_enable_lv_refresh(self, Int) -> None /// """ /// Enable or disable the lv refresh. @@ -506,7 +397,6 @@ STATIC const mp_rom_map_elem_t mod_passport_System_locals_dict_table[] = { {MP_ROM_QSTR(MP_QSTR_get_screen_brightness), MP_ROM_PTR(&mod_passport_System_get_screen_brightness_obj)}, {MP_ROM_QSTR(MP_QSTR_set_screen_brightness), MP_ROM_PTR(&mod_passport_System_set_screen_brightness_obj)}, {MP_ROM_QSTR(MP_QSTR_busy_wait), MP_ROM_PTR(&mod_passport_System_busy_wait_obj)}, - {MP_ROM_QSTR(MP_QSTR_validate_firmware_header), MP_ROM_PTR(&mod_passport_System_validate_firmware_header_obj)}, {MP_ROM_QSTR(MP_QSTR_enable_lv_refresh), MP_ROM_PTR(&mod_passport_System_enable_lv_refresh_obj)}, }; STATIC MP_DEFINE_CONST_DICT(mod_passport_System_locals_dict, mod_passport_System_locals_dict_table); @@ -546,4 +436,4 @@ STATIC void _hmac_sha256(uint8_t* key, uint32_t key_len, uint8_t* msg, uint32_t sha256_update(&ctx, o_key_pad, SHA256_BLOCK_LENGTH); sha256_update(&ctx, hmac, SHA256_DIGEST_LENGTH); sha256_final(&ctx, hmac); -} \ No newline at end of file +} diff --git a/ports/stm32/boards/Passport/modpassport.c b/ports/stm32/boards/Passport/modpassport.c index 137afacfe..d785d9902 100644 --- a/ports/stm32/boards/Passport/modpassport.c +++ b/ports/stm32/boards/Passport/modpassport.c @@ -21,6 +21,8 @@ /// package: passport +STATIC MP_DEFINE_EXCEPTION(InvalidFirmwareUpdate, Exception); + /// def supply_chain_challenge(self, challenge: bytearray, response: bytearray) -> boolean: /// """ /// Perform the supply chain challenge (HMAC) @@ -61,13 +63,198 @@ STATIC mp_obj_t mod_passport_verify_supply_chain_server_signature(mp_obj_t hash_ STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_passport_verify_supply_chain_server_signature_obj, mod_passport_verify_supply_chain_server_signature); +/// def verify_update_header(self, header: bytearray) -> None: +/// ''' +/// Verify the given firmware header bytes as a potential candidate to be +/// installed. +/// ''' +STATIC mp_obj_t mod_passport_verify_update_header(mp_obj_t header) { + mp_obj_t tuple[2]; + + mp_buffer_info_t header_info; + mp_get_buffer_raise(header, &header_info, MP_BUFFER_READ); + + // Build the current board hash so we can get the minimum firmware + // timestamp + uint8_t current_board_hash[HASH_LEN] = {0}; + get_current_board_hash(current_board_hash); + + uint32_t firmware_timestamp = se_get_firmware_timestamp(current_board_hash); + + FirmwareResult result = {0}; + foundation_firmware_verify_update_header(header_info.buf, + header_info.len, + firmware_timestamp, + &result); + + switch (result.tag) { + case FIRMWARE_RESULT_HEADER_OK: + tuple[0] = mp_obj_new_str_copy(&mp_type_str, + (const uint8_t*)result.HEADER_OK.version, + strlen((const char*)result.HEADER_OK.version)); + tuple[1] = result.HEADER_OK.signed_by_user ? mp_const_true : mp_const_false; + return mp_obj_new_tuple(2, tuple); + case FIRMWARE_RESULT_INVALID_HEADER: + mp_raise_msg(&mp_type_InvalidFirmwareUpdate, + MP_ERROR_TEXT("Invalid firmware header")); + case FIRMWARE_RESULT_UNKNOWN_MAGIC: + mp_raise_msg(&mp_type_InvalidFirmwareUpdate, + MP_ERROR_TEXT("Unknown firmware magic bytes")); + break; + case FIRMWARE_RESULT_INVALID_TIMESTAMP: + mp_raise_msg(&mp_type_InvalidFirmwareUpdate, + MP_ERROR_TEXT("Invalid firmware timestamp")); + break; + case FIRMWARE_RESULT_TOO_SMALL: + mp_raise_msg_varg(&mp_type_InvalidFirmwareUpdate, + MP_ERROR_TEXT("Firmware size is too small: %"PRIu32" bytes."), + result.TOO_SMALL.len); + break; + case FIRMWARE_RESULT_TOO_BIG: + mp_raise_msg_varg(&mp_type_InvalidFirmwareUpdate, + MP_ERROR_TEXT("Firmware size is too big: %"PRIu32" bytes."), + result.TOO_BIG.len); + break; + case FIRMWARE_RESULT_TOO_OLD: + mp_raise_msg_varg(&mp_type_InvalidFirmwareUpdate, + MP_ERROR_TEXT("Firmware is older than current, timestamp is: %"PRIu32"."), + result.TOO_OLD.timestamp); + break; + case FIRMWARE_RESULT_INVALID_PUBLIC_KEY1_INDEX: + mp_raise_msg_varg(&mp_type_InvalidFirmwareUpdate, + MP_ERROR_TEXT("Public Key is out of range (%"PRIu32")."), + result.INVALID_PUBLIC_KEY1_INDEX.index); + break; + case FIRMWARE_RESULT_INVALID_PUBLIC_KEY2_INDEX: + mp_raise_msg_varg(&mp_type_InvalidFirmwareUpdate, + MP_ERROR_TEXT("Public Key is out of range (%"PRIu32")."), + result.INVALID_PUBLIC_KEY2_INDEX.index); + break; + case FIRMWARE_RESULT_SAME_PUBLIC_KEY: + mp_raise_msg_varg(&mp_type_InvalidFirmwareUpdate, + MP_ERROR_TEXT("Same Public Key was used for both signatures (%"PRIu32")."), + result.SAME_PUBLIC_KEY.index); + break; + default: + break; + } + + mp_raise_msg(&mp_type_InvalidFirmwareUpdate, + MP_ERROR_TEXT("Unhandled case in firmware verification")); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_passport_verify_update_header_obj, + mod_passport_verify_update_header); + +STATIC mp_obj_t mod_passport_verify_update_signatures(mp_obj_t header, mp_obj_t validation_hash) { + uint8_t buf[72]; + uint8_t public_key[64]; + + mp_buffer_info_t header_info; + mp_get_buffer_raise(header, &header_info, MP_BUFFER_READ); + + mp_buffer_info_t validation_hash_info; + mp_get_buffer_raise(validation_hash, &validation_hash_info, MP_BUFFER_READ); + + // Build the current board hash so we can get the minimum firmware + // timestamp + uint8_t current_board_hash[HASH_LEN] = {0}; + get_current_board_hash(current_board_hash); + + uint32_t firmware_timestamp = se_get_firmware_timestamp(current_board_hash); + + se_pair_unlock(); + bool has_user_key = se_read_data_slot(KEYNUM_user_fw_pubkey, buf, sizeof(buf)) == 0; + memcpy(public_key, buf, sizeof(public_key)); + + FirmwareResult result = {0}; + foundation_firmware_verify_update_signatures(header_info.buf, + header_info.len, + firmware_timestamp, + validation_hash_info.buf, + has_user_key ? &public_key : NULL, + &result); + + // Signature verification also validates the header again, so, we have + // to handle the errors again. + // + // TODO: This should be factored out. + switch (result.tag) { + case FIRMWARE_RESULT_SIGNATURES_OK: + return mp_const_true; + case FIRMWARE_RESULT_INVALID_HEADER: + mp_raise_msg(&mp_type_InvalidFirmwareUpdate, + MP_ERROR_TEXT("Invalid firmware header")); + case FIRMWARE_RESULT_UNKNOWN_MAGIC: + mp_raise_msg(&mp_type_InvalidFirmwareUpdate, + MP_ERROR_TEXT("Unknown firmware magic bytes")); + break; + case FIRMWARE_RESULT_INVALID_TIMESTAMP: + mp_raise_msg(&mp_type_InvalidFirmwareUpdate, + MP_ERROR_TEXT("Invalid firmware timestamp")); + break; + case FIRMWARE_RESULT_TOO_SMALL: + mp_raise_msg_varg(&mp_type_InvalidFirmwareUpdate, + MP_ERROR_TEXT("Firmware size is too small: %"PRIu32" bytes."), + result.TOO_SMALL.len); + break; + case FIRMWARE_RESULT_TOO_BIG: + mp_raise_msg_varg(&mp_type_InvalidFirmwareUpdate, + MP_ERROR_TEXT("Firmware size is too big: %"PRIu32" bytes."), + result.TOO_BIG.len); + break; + case FIRMWARE_RESULT_TOO_OLD: + mp_raise_msg_varg(&mp_type_InvalidFirmwareUpdate, + MP_ERROR_TEXT("Firmware is older than current, timestamp is: %"PRIu32"."), + result.TOO_OLD.timestamp); + break; + case FIRMWARE_RESULT_INVALID_PUBLIC_KEY1_INDEX: + mp_raise_msg_varg(&mp_type_InvalidFirmwareUpdate, + MP_ERROR_TEXT("Public Key 1 is out of range (%"PRIu32")."), + result.INVALID_PUBLIC_KEY1_INDEX.index); + break; + case FIRMWARE_RESULT_INVALID_PUBLIC_KEY2_INDEX: + mp_raise_msg_varg(&mp_type_InvalidFirmwareUpdate, + MP_ERROR_TEXT("Public Key 2 is out of range (%"PRIu32")."), + result.INVALID_PUBLIC_KEY2_INDEX.index); + break; + case FIRMWARE_RESULT_SAME_PUBLIC_KEY: + mp_raise_msg_varg(&mp_type_InvalidFirmwareUpdate, + MP_ERROR_TEXT("Same Public Key was used for both signatures (%"PRIu32")."), + result.SAME_PUBLIC_KEY.index); + break; + case FIRMWARE_RESULT_INVALID_USER_SIGNATURE: + mp_raise_msg(&mp_type_InvalidFirmwareUpdate, + MP_ERROR_TEXT("User signature verification failed")); + break; + case FIRMWARE_RESULT_FAILED_SIGNATURE1: + mp_raise_msg(&mp_type_InvalidFirmwareUpdate, + MP_ERROR_TEXT("First signature verification failed")); + case FIRMWARE_RESULT_FAILED_SIGNATURE2: + mp_raise_msg(&mp_type_InvalidFirmwareUpdate, + MP_ERROR_TEXT("Second signature verification failed")); + default: + break; + } + + mp_raise_msg(&mp_type_InvalidFirmwareUpdate, + MP_ERROR_TEXT("Unhandled case in firmware signatures verification")); + return mp_const_false; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_passport_verify_update_signatures_obj, + mod_passport_verify_update_signatures); + STATIC const mp_rom_map_elem_t passport_module_globals_table[] = { {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_passport)}, + {MP_ROM_QSTR(MP_QSTR_InvalidFirmwareUpdate), MP_ROM_PTR(&mp_type_InvalidFirmwareUpdate)}, {MP_ROM_QSTR(MP_QSTR_camera), MP_ROM_PTR(&mod_passport_camera_module)}, {MP_ROM_QSTR(MP_QSTR_IS_SIMULATOR), MP_ROM_FALSE}, {MP_ROM_QSTR(MP_QSTR_supply_chain_challenge), MP_ROM_PTR(&mod_passport_supply_chain_challenge_obj)}, {MP_ROM_QSTR(MP_QSTR_verify_supply_chain_server_signature), MP_ROM_PTR(&mod_passport_verify_supply_chain_server_signature_obj)}, + {MP_ROM_QSTR(MP_QSTR_verify_update_header), + MP_ROM_PTR(&mod_passport_verify_update_header_obj)}, + {MP_ROM_QSTR(MP_QSTR_verify_update_signatures), + MP_ROM_PTR(&mod_passport_verify_update_signatures_obj)}, #ifdef SCREEN_MODE_COLOR {MP_ROM_QSTR(MP_QSTR_IS_COLOR), MP_ROM_TRUE}, #else diff --git a/ports/stm32/boards/Passport/modules/constants.py b/ports/stm32/boards/Passport/modules/constants.py index 7c49a1924..b1af08b56 100644 --- a/ports/stm32/boards/Passport/modules/constants.py +++ b/ports/stm32/boards/Passport/modules/constants.py @@ -33,6 +33,7 @@ FW_MAX_SIZE = (1792 * 1024) - 256 FW_START = 0 FW_HEADER_SIZE = 2048 +FW_HEADER_INFORMATION_SIZE = 34 # sizeof(fw_info_t) FW_ACTUAL_HEADER_SIZE = 170 # passport_firmware_header_t uses this many bytes # PSBT signing (uses same memory as firmware updates) diff --git a/ports/stm32/boards/Passport/modules/flows/update_firmware_flow.py b/ports/stm32/boards/Passport/modules/flows/update_firmware_flow.py index 21133de19..59176bb72 100644 --- a/ports/stm32/boards/Passport/modules/flows/update_firmware_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/update_firmware_flow.py @@ -8,10 +8,11 @@ from constants import FW_HEADER_SIZE, FW_MAX_SIZE import machine from pages import ErrorPage, ProgressPage, QuestionPage, SuccessPage, InsertMicroSDPage, InfoPage -from tasks import copy_firmware_to_spi_flash_task +from tasks import copy_firmware_to_spi_flash_task, verify_firmware_signature_task from flows import Flow, FilePickerFlow from utils import read_user_firmware_pubkey, is_all_zero, start_task from errors import Error +import passport class UpdateFirmwareFlow(Flow): @@ -77,13 +78,11 @@ async def show_firmware_details(self): self.set_result(False) return - # Validate the header - is_valid, version, error_msg, is_user_signed = common.system.validate_firmware_header(header) - # print('is_valid={}, version={}, error_msg={}, is_user_signed={}'.format( - # is_valid, version, error_msg, is_user_signed)) - self.version = version - if not is_valid: - await ErrorPage(text='Firmware header is invalid.\n\n{}'.format(error_msg)).show() + try: + version, is_user_signed = passport.verify_update_header(header) + self.version = version + except passport.InvalidFirmwareUpdate as e: + await ErrorPage(text='Firmware update is invalid.\n\n{}'.format(str(e))).show() self.set_result(False) return @@ -100,10 +99,34 @@ async def show_firmware_details(self): result = await QuestionPage(text='Install this firmware update?\n\nVersion:\n{}'.format(version)).show() if result: - self.goto(self.copy_to_flash) + self.goto(self.verify_firmware_signature) else: self.set_result(False) + async def verify_firmware_signature(self): + from common import ui + + self.progress_page = ProgressPage(text='Veryfing Signatures', left_micron=None, right_micron=None) + + self.verify_task = start_task(verify_firmware_signature_task( + self.update_file_path, self.size, self.progress_page.set_progress, self.on_done)) + + prev_top_level = ui.set_is_top_level(False) + result = await self.progress_page.show() + if result: + ui.set_is_top_level(prev_top_level) + self.goto(self.copy_to_flash) + elif self.error is Error.MICROSD_CARD_MISSING: + ui.set_is_top_level(prev_top_level) + result = await InsertMicroSDPage().show() + if not result: + self.back() + else: + ui.set_is_top_level(prev_top_level) + await ErrorPage("Failed to copy new firmware:\n\n{}".format(self.error_message)).show() + await InfoPage("Passport will continue using the current firmware").show() + self.set_result(False) + async def copy_to_flash(self): from common import settings, system, ui diff --git a/ports/stm32/boards/Passport/modules/tasks/__init__.py b/ports/stm32/boards/Passport/modules/tasks/__init__.py index c8b4a1f20..c8859178d 100644 --- a/ports/stm32/boards/Passport/modules/tasks/__init__.py +++ b/ports/stm32/boards/Passport/modules/tasks/__init__.py @@ -54,4 +54,5 @@ from .sign_text_file_task import sign_text_file_task from .validate_electrum_message_task import validate_electrum_message_task from .validate_psbt_task import validate_psbt_task +from .verify_firmware_signature_task import verify_firmware_signature_task from .verify_backup_task import verify_backup_task diff --git a/ports/stm32/boards/Passport/modules/tasks/verify_firmware_signature_task.py b/ports/stm32/boards/Passport/modules/tasks/verify_firmware_signature_task.py new file mode 100644 index 000000000..c29b95cd6 --- /dev/null +++ b/ports/stm32/boards/Passport/modules/tasks/verify_firmware_signature_task.py @@ -0,0 +1,72 @@ +# Task to verify the signatures of a firmware update before copying to +# SPI flash. +# +# SPDX-FileCopyrightText: © 2024 Foundation Devices, Inc. +# SPDX-License-Identifier: GPL-3.0-or-later + +import passport +import trezorcrypto +import foundation +from uasyncio import sleep_ms +from ubinascii import hexlify as b2a_hex +from constants import FW_HEADER_SIZE, FW_HEADER_INFORMATION_SIZE +from files import CardSlot, CardMissingError +from errors import Error + + +async def verify_firmware_signature_task(file_path, size, on_progress, on_done): + header = None + s = trezorcrypto.sha256() + + try: + with CardSlot() as card: + with open(file_path, 'rb') as fp: + # This is assumed to have been validated before, TOCTOU + # attacks are not an issue here since if the information data + # changes so does the validation hash making the signature + # verification to fail. + header = fp.read(FW_HEADER_SIZE) + s.update(header[:FW_HEADER_INFORMATION_SIZE]) + + buf = bytearray(1024) + pos = FW_HEADER_SIZE + update_display = 0 + while pos < size: + # Update every 50 reads. + if update_display % 50 == 0: + percent = int((pos / size) * 100) + on_progress(percent) + + # Allow UI to update progress. + await sleep_ms(1) + + here = fp.readinto(buf) + s.update(buf[:here]) + pos += here + + update_display += 1 + except CardMissingError: + await on_done(Error.MICROSD_CARD_MISSING, None) + return + except Exception as e: + error_message = "Firmware update error: {}, Info: {}".format(e.__class__.__name__, + e.args[0] if len(e.args) == 1 else e.args) + await on_done(Error.FIRMWARE_UPDATE_FAILED, error_message) + return + + single_sha = s.digest() + double_sha = bytearray(32) + foundation.sha256(single_sha, double_sha) + + try: + passport.verify_update_signatures(header, double_sha) + except passport.InvalidFirmwareUpdate as e: + await on_done(Error.FIRMWARE_UPDATE_FAILED, "Invalid firmware signatures") + return + except Exception as e: + error_message = "Firmware update error: {}, Info: {}".format(e.__class__.__name__, + e.args[0] if len(e.args) == 1 else e.args) + await on_done(Error.FIRMWARE_UPDATE_FAILED, error_message) + return + + await on_done(None, None) From 5e37d396d3d5a946b2c43dfb41a49b526e7622b1 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Tue, 16 Jul 2024 15:10:43 +0200 Subject: [PATCH 052/126] SFT-3764: Fix error message. * extmod/foundation/modfoundation-ur.h (mod_foundation_ur_new_passport_response): Fix UUID error message. --- extmod/foundation/modfoundation-ur.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extmod/foundation/modfoundation-ur.h b/extmod/foundation/modfoundation-ur.h index 5fbce1ba9..b2088ea95 100644 --- a/extmod/foundation/modfoundation-ur.h +++ b/extmod/foundation/modfoundation-ur.h @@ -464,7 +464,7 @@ STATIC mp_obj_t mod_foundation_ur_new_passport_response(size_t n_args, mp_get_buffer_raise(args[0].u_obj, &uuid, MP_BUFFER_READ); if (uuid.len != 16) { mp_raise_msg(&mp_type_ValueError, - MP_ERROR_TEXT("uuid should be 33 bytes")); + MP_ERROR_TEXT("uuid should be 16 bytes")); } GET_STR_DATA_LEN(args[1].u_obj, word1, word1_len); From 2cf606be8c887c2b1ebfb680c7047d92f524f714 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Tue, 16 Jul 2024 15:01:08 +0200 Subject: [PATCH 053/126] SFT-3764: Use correct value for is_private in hdkey. * extmod/foundation/modfoundation-ur.h (mod_foundation_ur_new_derived_key): Use correct value for is_private in hdkey. --- extmod/foundation/modfoundation-ur.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extmod/foundation/modfoundation-ur.h b/extmod/foundation/modfoundation-ur.h index b2088ea95..be33bf549 100644 --- a/extmod/foundation/modfoundation-ur.h +++ b/extmod/foundation/modfoundation-ur.h @@ -406,7 +406,7 @@ STATIC mp_obj_t mod_foundation_ur_new_derived_key(size_t n_args, } ur_registry_new_derived_key(&value, - args[0].u_bool, + args[1].u_bool, key_data.buf, chain_code_info.buf, &use_info_obj->info, From d22c92381901ec27e530ca2797a27af251887112 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Tue, 16 Jul 2024 15:21:56 +0200 Subject: [PATCH 054/126] SFT-3764: Derive Debug for FFI UR types. Only used for debugging. * extmod/foundation-rust/src/ur/registry.rs: Derive debug for FFI UR types. --- extmod/foundation-rust/src/ur/registry.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/extmod/foundation-rust/src/ur/registry.rs b/extmod/foundation-rust/src/ur/registry.rs index b852dadac..c40d56636 100644 --- a/extmod/foundation-rust/src/ur/registry.rs +++ b/extmod/foundation-rust/src/ur/registry.rs @@ -113,6 +113,7 @@ impl<'a> From<&'a UR_HDKey> for HDKey<'a> { } /// Derived `hdkey`. +#[derive(Debug)] #[repr(C)] pub struct UR_DerivedKey { /// `true` if this is a private key. @@ -166,7 +167,7 @@ impl<'a> From<&'a UR_DerivedKey> for DerivedKey<'a> { } #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Debug, Clone, Copy)] pub enum UR_CoinType { BTC, } @@ -180,7 +181,7 @@ impl From for CoinType { } #[repr(C)] -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct UR_CoinInfo { pub coin_type: UR_CoinType, pub network: u64, @@ -197,7 +198,7 @@ impl From<&UR_CoinInfo> for CoinInfo { /// Metadata for the complete or partial derivation path of a key. #[repr(C)] -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct UR_Keypath { /// The fingerprint of this key's direct ancestor. /// From a3cd3abdbdd7425ae524c91f9bc032d85326b843 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Tue, 16 Jul 2024 16:20:40 -0400 Subject: [PATCH 055/126] SFT-3407: updated postmix menu with new account tools submenu --- ports/stm32/boards/Passport/modules/menus.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/menus.py b/ports/stm32/boards/Passport/modules/menus.py index bf0cbf71a..ac8d63395 100644 --- a/ports/stm32/boards/Passport/modules/menus.py +++ b/ports/stm32/boards/Passport/modules/menus.py @@ -124,9 +124,7 @@ def postmix_menu(): 'statusbar': {'title': 'SIGN'}}, {'icon': 'ICON_MICROSD', 'label': 'Sign with microSD', 'flow': SignPsbtMicroSDFlow, 'statusbar': {'title': 'SIGN'}}, - {'icon': 'ICON_VERIFY_ADDRESS', 'label': 'Verify Address', 'flow': VerifyAddressFlow}, - {'icon': 'ICON_VERIFY_ADDRESS', 'label': 'Explore Addresses', 'flow': AddressExplorerFlow, - 'statusbar': {'title': 'LIST ADDRESSES'}}, + {'icon': 'ICON_SETUP', 'label': 'Account Tools', 'submenu': account_tools}, {'icon': 'ICON_CONNECT', 'label': 'Connect Wallet', 'flow': ConnectWalletFlow, 'statusbar': {'title': 'CONNECT'}}, ] From 820dd51422fca08e1293a23d25f70635df3c0d26 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Tue, 16 Jul 2024 16:58:17 -0400 Subject: [PATCH 056/126] SFT-2988: fixed parsing newlines --- .../Passport/modules/tasks/validate_electrum_message_task.py | 3 ++- ports/stm32/boards/Passport/modules/utils.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/tasks/validate_electrum_message_task.py b/ports/stm32/boards/Passport/modules/tasks/validate_electrum_message_task.py index f2731ae9a..44201e898 100644 --- a/ports/stm32/boards/Passport/modules/tasks/validate_electrum_message_task.py +++ b/ports/stm32/boards/Passport/modules/tasks/validate_electrum_message_task.py @@ -27,7 +27,8 @@ async def validate_electrum_message_task(on_done, message): (subpath, error) = validate_sign_text(message, header_elements[1], space_limit=False, - check_whitespace=False) + check_whitespace=False, + check_ascii=False) if error: await on_done(None, error) diff --git a/ports/stm32/boards/Passport/modules/utils.py b/ports/stm32/boards/Passport/modules/utils.py index 0b95c8aa2..31811dc63 100644 --- a/ports/stm32/boards/Passport/modules/utils.py +++ b/ports/stm32/boards/Passport/modules/utils.py @@ -1295,7 +1295,7 @@ def is_passphrase_active(): MSG_MAX_SPACES = 4 -def validate_sign_text(text, subpath, space_limit=True, check_whitespace=True): +def validate_sign_text(text, subpath, space_limit=True, check_whitespace=True, check_ascii=True): # Check for leading or trailing whitespace if check_whitespace: if text[0] == ' ': @@ -1309,7 +1309,7 @@ def validate_sign_text(text, subpath, space_limit=True, check_whitespace=True): # print('text="{}"'.format(text)) for ch in text: # print('ch="{}"'.format(ch)) - if ord(ch) not in MSG_CHARSET: + if check_ascii and ord(ch) not in MSG_CHARSET: return (subpath, 'File contains non-ASCII character: 0x%02x' % ord(ch)) if space_limit: From 44439c43cd6b5252ece817875ebe2e6ba8ccf139 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Tue, 16 Jul 2024 17:02:46 -0400 Subject: [PATCH 057/126] SFT-2988: added spacing to message review page --- .../boards/Passport/modules/flows/sign_electrum_message_flow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/boards/Passport/modules/flows/sign_electrum_message_flow.py b/ports/stm32/boards/Passport/modules/flows/sign_electrum_message_flow.py index a9a47bda8..6e522acc3 100644 --- a/ports/stm32/boards/Passport/modules/flows/sign_electrum_message_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/sign_electrum_message_flow.py @@ -62,7 +62,7 @@ async def show_message(self): self.address = stylize_address(self.address) result = await LongTextPage(centered=True, - text=self.message, + text=('\n' + self.message), card_header={'title': 'Message'}).show() if not result: From 44222d73efa024444ba6ea8ccd4a5ae1ef75c94a Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Tue, 16 Jul 2024 14:26:15 +0200 Subject: [PATCH 058/126] SFT-2417: Update upgrade passport message. * ports/stm32/boards/Passport/modules/data_codecs/ur2_codec.py (UR2Decoder): Re-raise ur.UnsupportedError message. * ports/stm32/boards/Passport/modules/flows/scan_qr_flow.py (ScanQRFlow) : Handle when the scanned QR code is unsupported. * ports/stm32/boards/Passport/modules/pages/scan_qr_page.py (QRScanResult) : New method. --- .../Passport/modules/data_codecs/ur2_codec.py | 15 ++++++--------- .../boards/Passport/modules/flows/scan_qr_flow.py | 7 +++++++ .../boards/Passport/modules/pages/scan_qr_page.py | 3 +++ 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/data_codecs/ur2_codec.py b/ports/stm32/boards/Passport/modules/data_codecs/ur2_codec.py index 48fc9e68b..9750f8e12 100644 --- a/ports/stm32/boards/Passport/modules/data_codecs/ur2_codec.py +++ b/ports/stm32/boards/Passport/modules/data_codecs/ur2_codec.py @@ -31,17 +31,15 @@ def add_data(self, data): if ur.decoder_is_empty(): try: self.value = ur.decode_single_part(data) - except ur.UnsupportedError: - raise DecodeError("""\ -Please check for updates to Passport and your software wallet.""") + except ur.UnsupportedError as exc: + raise exc except ur.OtherError as exc: raise DecodeError(str(exc)) else: raise DecodeError("""\ Received single-part UR when multi-part reception was already in place""") - except ur.UnsupportedError: - raise DecodeError("""\ -Please check for updates to Passport and your software wallet.""") + except ur.UnsupportedError as exc: + raise exc except ur.TooBigError as exc: raise exc # Re-raise as ScanQRResult has a method to check it. except ur.OtherError as exc: @@ -65,9 +63,8 @@ def decode(self): self.value = ur.decoder_decode_message() except ur.OtherError as exc: raise DecodeError(str(exc)) - except ur.UnsupportedError: - raise DecodeError("""\ -Please check for updates to Passport and your software wallet.""") + except ur.UnsupportedError as exc: + raise exc return self.value diff --git a/ports/stm32/boards/Passport/modules/flows/scan_qr_flow.py b/ports/stm32/boards/Passport/modules/flows/scan_qr_flow.py index ecaf87dc4..4da733708 100644 --- a/ports/stm32/boards/Passport/modules/flows/scan_qr_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/scan_qr_flow.py @@ -63,6 +63,13 @@ async def scan(self): self.set_result(None) return + if result.is_unsupported(): + await LongErrorPage(text="""Unsupported QR type. + +Check for updates to Passport and your software wallet.""").show() + self.set_result(None) + return + if result.is_ur_too_big(): self.set_result(Error.QR_TOO_LARGE) return diff --git a/ports/stm32/boards/Passport/modules/pages/scan_qr_page.py b/ports/stm32/boards/Passport/modules/pages/scan_qr_page.py index 57a18ec34..d9c4c3b66 100644 --- a/ports/stm32/boards/Passport/modules/pages/scan_qr_page.py +++ b/ports/stm32/boards/Passport/modules/pages/scan_qr_page.py @@ -151,6 +151,9 @@ def __init__(self, data=None, error=None, qr_type=None, num_frames=None, max_fra self.num_frames = num_frames self.max_frames = max_frames + def is_unsupported(self): + return isinstance(self.error, ur.UnsupportedError) + def is_failure(self): return self.error is not None From 9970a3b845116ac1b854c6f4a4f301ce2a24f895 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Wed, 17 Jul 2024 13:10:20 -0400 Subject: [PATCH 059/126] SFT-2988: fixed cutoff question icon --- .../Passport/modules/flows/sign_electrum_message_flow.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/sign_electrum_message_flow.py b/ports/stm32/boards/Passport/modules/flows/sign_electrum_message_flow.py index 6e522acc3..bed816e10 100644 --- a/ports/stm32/boards/Passport/modules/flows/sign_electrum_message_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/sign_electrum_message_flow.py @@ -4,7 +4,7 @@ # sign_electrum_message_flow.py - Sign an electrum standard message via QR from flows import Flow, ScanQRFlow -from pages import ErrorPage, LongTextPage, QuestionPage, ShowQRPage +from pages import ErrorPage, LongTextPage, LongQuestionPage, ShowQRPage from tasks import sign_text_file_task, validate_electrum_message_task from utils import spinner_task, stylize_address from data_codecs.qr_type import QRType @@ -69,8 +69,8 @@ async def show_message(self): self.set_result(False) return - result = await QuestionPage(text='Sign message with this address?\n\n{}'.format(self.address), - right_micron=microns.Sign).show() + result = await LongQuestionPage(text='Sign message with this address?\n\n{}'.format(self.address), + right_micron=microns.Sign).show() if not result: self.set_result(False) From cc024f10a485bdf0ce9badaeee0778ad8ce84966 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Wed, 17 Jul 2024 13:29:49 -0400 Subject: [PATCH 060/126] SFT-2988: removed scrollbar when signing for segwit addresses --- .../modules/flows/sign_electrum_message_flow.py | 6 ++++-- .../Passport/modules/pages/long_question_page.py | 8 ++++++-- .../boards/Passport/modules/pages/long_text_page.py | 10 ++++++---- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/sign_electrum_message_flow.py b/ports/stm32/boards/Passport/modules/flows/sign_electrum_message_flow.py index bed816e10..b83f3ccf8 100644 --- a/ports/stm32/boards/Passport/modules/flows/sign_electrum_message_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/sign_electrum_message_flow.py @@ -10,7 +10,7 @@ from data_codecs.qr_type import QRType import microns from wallets.utils import get_addr_type_from_deriv -from public_constants import AF_CLASSIC +from public_constants import AF_CLASSIC, MARGIN_FOR_ADDRESSES import stash from ubinascii import b2a_base64 @@ -70,7 +70,9 @@ async def show_message(self): return result = await LongQuestionPage(text='Sign message with this address?\n\n{}'.format(self.address), - right_micron=microns.Sign).show() + right_micron=microns.Sign, + margins=MARGIN_FOR_ADDRESSES, + top_margin=8).show() if not result: self.set_result(False) diff --git a/ports/stm32/boards/Passport/modules/pages/long_question_page.py b/ports/stm32/boards/Passport/modules/pages/long_question_page.py index 136d2e730..019a71d2e 100644 --- a/ports/stm32/boards/Passport/modules/pages/long_question_page.py +++ b/ports/stm32/boards/Passport/modules/pages/long_question_page.py @@ -18,7 +18,9 @@ def __init__( card_header=None, statusbar=None, left_micron=microns.Cancel, - right_micron=microns.Checkmark + right_micron=microns.Checkmark, + margins=None, + top_margin=None, ): super().__init__( text=text, @@ -28,4 +30,6 @@ def __init__( statusbar=statusbar, centered=True, left_micron=left_micron, - right_micron=right_micron) + right_micron=right_micron, + margins=margins, + top_margin=top_margin) diff --git a/ports/stm32/boards/Passport/modules/pages/long_text_page.py b/ports/stm32/boards/Passport/modules/pages/long_text_page.py index 052d349b9..d10c6e870 100644 --- a/ports/stm32/boards/Passport/modules/pages/long_text_page.py +++ b/ports/stm32/boards/Passport/modules/pages/long_text_page.py @@ -22,7 +22,8 @@ def __init__(self, statusbar=None, left_micron=microns.Back, right_micron=microns.Forward, - margins=None): + margins=None, + top_margin=None): super().__init__( card_header=card_header, statusbar=statusbar, @@ -33,7 +34,8 @@ def __init__(self, self.icon = icon self.icon_color = icon_color self.centered = centered - self.margins = (margins if margins is not None else 12) + self.side_margins = (margins if margins is not None else 12) + self.top_margin = (top_margin if top_margin is not None else 16) # Set non-style props self.set_width(lv.pct(100)) @@ -48,14 +50,14 @@ def __init__(self, if self.icon is not None: self.icon_view = Icon(self.icon, color=self.icon_color) with Stylize(self.icon_view) as default: - default.pad(top=16, bottom=8) + default.pad(top=self.top_margin, bottom=8) self.add_child(self.icon_view) self.container = View(flex_flow=lv.FLEX_FLOW.COLUMN) self.container.set_width(lv.pct(100)) with Stylize(self.container) as default: default.flex_fill() - default.pad(left=self.margins, right=self.margins) + default.pad(left=self.side_margins, right=self.side_margins) with Stylize(self.container, selector=lv.PART.SCROLLBAR) as scrollbar: if not passport.IS_COLOR: From d5ca11660da6818a85d671b2832659611e818010 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Mon, 22 Jul 2024 00:34:59 -0400 Subject: [PATCH 061/126] SFT-3847: removed btcpay taproot --- ports/stm32/boards/Passport/modules/wallets/btcpay.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/wallets/btcpay.py b/ports/stm32/boards/Passport/modules/wallets/btcpay.py index bacde003b..c67f78856 100644 --- a/ports/stm32/boards/Passport/modules/wallets/btcpay.py +++ b/ports/stm32/boards/Passport/modules/wallets/btcpay.py @@ -19,6 +19,6 @@ {'id': 'microsd', 'label': 'microSD', 'filename_pattern': '{xfp}-btcpay.json', 'filename_pattern_multisig': '{xfp}-btcpay-multisig.json'} ], - 'select_addr_type': True, - 'addr_options': [AF_P2WPKH, AF_P2TR], + # 'select_addr_type': True, + # 'addr_options': [AF_P2WPKH, AF_P2TR], } From caaa283a967f1ec0c61df3ea32c2153a879e9d49 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Mon, 22 Jul 2024 17:15:38 +0200 Subject: [PATCH 062/126] SFT-3854: Enable debug logging when signing firmware. No private key material is leaked, only the calculated hash that is then verified by the bootloader is printed. * ports/stm32/Justfile (sign): Pass -d option to cosign. --- ports/stm32/Justfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/Justfile b/ports/stm32/Justfile index d7517292f..4ef440638 100644 --- a/ports/stm32/Justfile +++ b/ports/stm32/Justfile @@ -98,7 +98,7 @@ sign version="1.0.0" screen="mono" dev="" ext="": (build screen dev ext) fi echo -e "\nAdding user signature...\n" - cosign -t {{lowercase(screen)}} -f $COSIGN_FILEPATH -k {{cosign_keypath}} -v {{version}} + cosign -d -t {{lowercase(screen)}} -f $COSIGN_FILEPATH -k {{cosign_keypath}} -v {{version}} # Remove .bin and append the signed filename string if [ {{screen}} == "color" ] From 661053b9625ded1bd635069dadd72116c286a1f7 Mon Sep 17 00:00:00 2001 From: Jean-Pierre De Jesus DIAZ Date: Mon, 22 Jul 2024 18:40:26 +0200 Subject: [PATCH 063/126] SFT-3480: Update foundation-firmware to 0.1.2. * Cargo.lock: Regenerate. * Cargo.toml (dependencies) : Bump to 0.1.2. --- extmod/foundation-rust/Cargo.lock | 4 ++-- extmod/foundation-rust/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extmod/foundation-rust/Cargo.lock b/extmod/foundation-rust/Cargo.lock index a4a9d73ef..a775fd99f 100644 --- a/extmod/foundation-rust/Cargo.lock +++ b/extmod/foundation-rust/Cargo.lock @@ -127,9 +127,9 @@ checksum = "7e6bdea1eeaa5edb5355234d02f81c6dbdd4bc5146887990dd29a213029bbeae" [[package]] name = "foundation-firmware" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3a0d5d35a9470dea4fc94d5b04fafdf587d4c642e1f8de97879bb0f99fff96a" +checksum = "accae5ad04dd608e86b41e605ae7fc8e976cf182a6ce2d35c48463b1d5a5b783" dependencies = [ "bitcoin_hashes", "heapless", diff --git a/extmod/foundation-rust/Cargo.toml b/extmod/foundation-rust/Cargo.toml index 3e20a7830..0f7becf34 100644 --- a/extmod/foundation-rust/Cargo.toml +++ b/extmod/foundation-rust/Cargo.toml @@ -29,7 +29,7 @@ version = "1" default-features = false [dependencies.foundation-firmware] -version = "0.1" +version = "0.1.2" default-features = false [dependencies.foundation-ur] From fbfd59ec62dc18c5778c20e6d972703828feb3ec Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Wed, 24 Jul 2024 13:12:44 -0400 Subject: [PATCH 064/126] SFT-1943: fixed predictive text page holding onto previous entries --- .../Passport/modules/flows/restore_backup_flow.py | 7 +++++-- .../Passport/modules/flows/restore_seed_flow.py | 9 ++++++--- .../modules/pages/predictive_text_input_page.py | 11 +++++------ 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/restore_backup_flow.py b/ports/stm32/boards/Passport/modules/flows/restore_backup_flow.py index c767706eb..2750b31bf 100644 --- a/ports/stm32/boards/Passport/modules/flows/restore_backup_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/restore_backup_flow.py @@ -99,14 +99,17 @@ async def enter_backup_password(self): result = await PredictiveTextInputPage( word_list='bytewords', total_words=NUM_BACKUP_PASSWORD_WORDS, - initial_words=self.backup_password_words).show() + initial_words=self.backup_password_words, + initial_prefixes=self.backup_password_prefixes).show() + + (backup_password_words, self.backup_password_prefixes, _) = result if result is None: cancel = await QuestionPage(text='Cancel password entry? ' + 'All progress will be lost.').show() if cancel: self.back() else: - self.backup_password_words, self.backup_password_prefixes = result + self.backup_password_words = backup_password_words self.decryption_password = (' ').join(self.backup_password_words) # print('6 words: decryption_password={}'.format(self.decryption_password)) self.goto(self.do_restore) diff --git a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py index 64824777e..c2a84d725 100644 --- a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py @@ -21,6 +21,7 @@ def __init__(self, refresh_cards_when_done=False, autobackup=True, full_backup=F self.validate_text = None self.index = 0 self.seed_words = [] + self.prefixes = [] self.full_backup = full_backup self.autobackup = autobackup self.statusbar = {'title': 'IMPORT SEED', 'icon': 'ICON_SEED'} @@ -103,9 +104,12 @@ async def enter_seed_words(self): word_list='bip39', total_words=self.seed_length, initial_words=self.seed_words, + initial_prefixes=self.prefixes, start_index=self.index).show() - if result is None: + seed_words, self.prefixes, get_last_word = result + + if seed_words is None: cancel = await QuestionPage( text='Cancel seed entry? All progress will be lost.').show() @@ -116,8 +120,7 @@ async def enter_seed_words(self): self.index = 0 return - self.seed_words, self.prefixes, get_last_word = result - + self.seed_words = seed_words if get_last_word: last_word = await RandomFinalWordFlow(self.seed_words).run() diff --git a/ports/stm32/boards/Passport/modules/pages/predictive_text_input_page.py b/ports/stm32/boards/Passport/modules/pages/predictive_text_input_page.py index 84c1c4e67..ea760fdef 100644 --- a/ports/stm32/boards/Passport/modules/pages/predictive_text_input_page.py +++ b/ports/stm32/boards/Passport/modules/pages/predictive_text_input_page.py @@ -29,8 +29,8 @@ def __init__(self, total_words=24, card_header=None, statusbar=None, - initial_words=[], - initial_prefixes=[], + initial_words=None, + initial_prefixes=None, left_micron=microns.Back, right_micron=microns.Forward, start_index=None): @@ -45,8 +45,8 @@ def __init__(self, self.total_words = total_words self.word_idx = start_index if (start_index is not None and start_index < self.total_words) else 0 self.prediction_idx = 0 - self.selected_words = initial_words - self.prefixes = initial_prefixes + self.selected_words = initial_words or [] + self.prefixes = initial_prefixes or [] self.predictions = [] with Stylize(self) as default: @@ -97,7 +97,6 @@ def update_predictions(self): # print('Lookup words for {}'.format(prefix)) set_list(self.prefixes, self.word_idx, prefix) self.predictions = get_words_matching_prefix(prefix, max=10, word_list=self.word_list) - print("len(predictions): {}".format(len(self.predictions))) elif self.word_idx == self.total_words - 1: self.predictions = [RANDOM_WORD_STRING] else: @@ -208,7 +207,7 @@ def left_action(self, is_pressed): self.update_title() self.update_predictions() else: - self.set_result(None) + self.set_result((None, self.prefixes, False)) def attach(self, group): super().attach(group) From ad9d1774a5cf7bfe1755dc79a65daf6c9be805e3 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Thu, 4 Apr 2024 21:19:31 -0400 Subject: [PATCH 065/126] SFT-1728: starting with temporary settings mode --- ports/stm32/boards/Passport/modules/export.py | 7 +- .../boards/Passport/modules/ext_settings.py | 94 ++++++++++++++++--- 2 files changed, 88 insertions(+), 13 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/export.py b/ports/stm32/boards/Passport/modules/export.py index 8f7e866ed..0e14ed7ec 100644 --- a/ports/stm32/boards/Passport/modules/export.py +++ b/ports/stm32/boards/Passport/modules/export.py @@ -86,7 +86,12 @@ def ADD(key, val): # user preferences - sort so that accounts is processed before multisig multisig_ids = [] - for k, v in sorted(settings.current.items()): + settings_dict = None + if settings.temporary_mode: + settings_dict = settings.temporary_settings + else: + settings_dict = settings.current + for k, v in sorted(settings_dict.items()): # print('render handling key "{}"'.format(k)) if k[0] == '_': continue # debug stuff in simulator diff --git a/ports/stm32/boards/Passport/modules/ext_settings.py b/ports/stm32/boards/Passport/modules/ext_settings.py index 884293cae..7972afc63 100644 --- a/ports/stm32/boards/Passport/modules/ext_settings.py +++ b/ports/stm32/boards/Passport/modules/ext_settings.py @@ -21,6 +21,7 @@ import trezorcrypto import ustruct import gc +import ucopy from uio import BytesIO from sffile import SFFile from utils import to_str, call_later_ms @@ -45,6 +46,9 @@ def __init__(self, slots=None, slot_size=0, loop=None, name=None): self.current = self.default_values() self.overrides = {} # volatile overide values self.last_save_slots = [-1, -1] + self.temporary_mode = False + self.temporary_settings = {} + self.temporary_overrides = {} # NOTE: We don't load the settings initially since we don't have the AES key until # the user logs in successfully. @@ -206,13 +210,13 @@ def load(self): sf.write(pos + i, h) def get(self, kn, default=None): - if kn in self.overrides: - return self.overrides.get(kn) + if self.in_overrides(kn): + return self.get_from_overrides(kn) else: # Special case for xfp and xpub -- make sure they exist and create if not - if kn not in self.current: - if kn == 'root_xfp' and 'xfp' in self.current: - return self.current.get('xfp', default) + if not self.in_current(kn): + if kn == 'root_xfp' and self.in_current('xfp'): + return self.get_from_current('xfp', default) if kn == 'xfp' or kn == 'xpub' or kn == 'root_xfp': try: # Update xpub/xfp in settings after creating new wallet @@ -229,9 +233,9 @@ def get(self, kn, default=None): finally: # system.hide_busy_bar() # These are overrides, so return them from there - return self.overrides.get(kn) + return self.get_from_overrides(kn) - return self.current.get(kn, default) + return self.get_from_current(kn, default) def changed(self): self.is_dirty += 1 @@ -240,26 +244,64 @@ def changed(self): def set(self, kn, v): # print('set({}, {}'.format(kn, v)) + if self.temporary_mode: + self.temporary_settings[kn] = v + return + self.current[kn] = v self.changed() + def get_from_current(kn, default): + if self.temporary_mode: + return self.temporary_settings.get(kn, default) + return self.current.get(kn, default) + + def get_from_overrides(kn, default): + if self.temporary_mode: + return self.temporary_overrides.get(kn, default) + return self.overrides.get(kn, default) + def set_volatile(self, kn, v): + if self.temporary_mode: + self.temporary_overrides[kn] = v + return self.overrides[kn] = v + def in_current(self, kn): + if self.temporary_mode: + return kn in self.temporary_settings + return kn in self.current + + def in_overrides(self, kn): + if self.temporary_mode: + return kn in self.temporary_overrides + return kn in self.overrides + def clear_volatile(self, kn): + if self.temporary_mode: + if kn in self.temporary_overrides: + del self.temporary_overrides[kn] + return if kn in self.overrides: del self.overrides[kn] def remove(self, kn): # print('remove(\'{}\') called!'.format(kn)) - if kn in self.current: + if self.in_current(kn): + if self.temporary_mode: + self.temporary_settings.pop(kn, None) + return self.current.pop(kn, None) self.changed() def remove_regex(self, pattern): import re pattern = re.compile(pattern) - matches = [k for k in self.current if pattern.search(k)] + matches = [] + if self.temporary_mode: + matches = [k for k in self.temporary_settings if pattern.search(k)] + else: + matches = [k for k in self.current if pattern.search(k)] for k in matches: self.remove(k) @@ -268,9 +310,14 @@ def clear(self): # could be just: # self.current = {} # but accomodating the simulator here - rk = [k for k in self.current if k[0] != '_'] - for k in rk: - del self.current[k] + if self.temporary_mode: + rk = [k for k in self.temporary_settings if k[0] != '_'] + for k in rk: + del self.temporary_settings[k] + else: + rk = [k for k in self.current if k[0] != '_'] + for k in rk: + del self.current[k] self.changed() @@ -338,11 +385,17 @@ def erase_cache_entry(self, start_pos): sf.wait_done() def erase_all(self): + if self.temporary_mode: + return + for pos in self.slots: self.erase_cache_entry(pos) self.blank() def save(self): + if self.temporary_mode: + return + # Make two saves in case one is corrupted self.do_save(erase_old_pos=True) self.do_save(erase_old_pos=False) @@ -426,6 +479,23 @@ def blank(self): self.current.clear() self.is_dirty = 0 + def enter_temporary_mode(self): + + # Avoid resetting temporary settings if already in temporary mode + if self.temporary_mode: + return + + self.temporary_mode = True + + # Merge the overrides and persistent settings into a new dict + self.temporary_settings = copy.deepcopy(self.current) + self.temporary_overrides = copy.deepcopy(self.overrides) + + def exit_temporary_mode(self): + self.temporary_mode = False + self.temporary_settings = {} + self.temporary_overrides = {} + @staticmethod def default_values(): # Please try to avoid defaults here... It's better to put into code From 8ac3e31766d8e4f5bf01caf4dee8f010c65723f8 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Thu, 4 Apr 2024 21:58:40 -0400 Subject: [PATCH 066/126] SFT-1728: fixed copying and added device settings exception --- ports/stm32/boards/Passport/modules/ext_settings.py | 10 +++++----- .../stm32/boards/Passport/modules/public_constants.py | 7 +++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/ext_settings.py b/ports/stm32/boards/Passport/modules/ext_settings.py index 7972afc63..707d9c476 100644 --- a/ports/stm32/boards/Passport/modules/ext_settings.py +++ b/ports/stm32/boards/Passport/modules/ext_settings.py @@ -21,12 +21,12 @@ import trezorcrypto import ustruct import gc -import ucopy from uio import BytesIO from sffile import SFFile from utils import to_str, call_later_ms from constants import SPI_FLASH_SECTOR_SIZE from passport import mem +from public_constants import DEVICE_SETTINGS class ExtSettings: @@ -244,14 +244,14 @@ def changed(self): def set(self, kn, v): # print('set({}, {}'.format(kn, v)) - if self.temporary_mode: + if self.temporary_mode and kn not in DEVICE_SETTINGS: self.temporary_settings[kn] = v return self.current[kn] = v self.changed() - def get_from_current(kn, default): + def get_from_current(self, kn, default): if self.temporary_mode: return self.temporary_settings.get(kn, default) return self.current.get(kn, default) @@ -488,8 +488,8 @@ def enter_temporary_mode(self): self.temporary_mode = True # Merge the overrides and persistent settings into a new dict - self.temporary_settings = copy.deepcopy(self.current) - self.temporary_overrides = copy.deepcopy(self.overrides) + self.temporary_settings.update(self.current) + self.temporary_overrides.update(self.overrides) def exit_temporary_mode(self): self.temporary_mode = False diff --git a/ports/stm32/boards/Passport/modules/public_constants.py b/ports/stm32/boards/Passport/modules/public_constants.py index 5d4ea026e..15dd7e4d0 100644 --- a/ports/stm32/boards/Passport/modules/public_constants.py +++ b/ports/stm32/boards/Passport/modules/public_constants.py @@ -123,6 +123,13 @@ MARGIN_FOR_ADDRESSES = 0 +# Device Settings, not saved to backups in temporary mode +DEVICE_SETTINGS = [ + 'screen_brightness', + 'shutdown_timeout', + 'device_name' +] + # Size of a pin prefix: NUM_DIGITS_FOR_SECURITY_WORDS = const(4) From a832451897fe4a6fee348630bd31ef3867f4c6c7 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Fri, 5 Apr 2024 13:57:00 -0400 Subject: [PATCH 067/126] SFT-1728: importing temporary seed working --- .../boards/Passport/modules/ext_settings.py | 23 +++++++++------- .../modules/flows/initial_seed_setup_flow.py | 27 ++++++++++++++----- ports/stm32/boards/Passport/modules/stash.py | 20 +++++++++----- .../modules/tasks/get_seed_words_task.py | 2 +- .../Passport/modules/tasks/save_seed_task.py | 17 +++++++----- ports/stm32/boards/Passport/modules/utils.py | 5 +++- 6 files changed, 62 insertions(+), 32 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/ext_settings.py b/ports/stm32/boards/Passport/modules/ext_settings.py index 707d9c476..7fc451511 100644 --- a/ports/stm32/boards/Passport/modules/ext_settings.py +++ b/ports/stm32/boards/Passport/modules/ext_settings.py @@ -211,7 +211,7 @@ def load(self): def get(self, kn, default=None): if self.in_overrides(kn): - return self.get_from_overrides(kn) + return self.get_from_overrides(kn, default) else: # Special case for xfp and xpub -- make sure they exist and create if not if not self.in_current(kn): @@ -233,7 +233,7 @@ def get(self, kn, default=None): finally: # system.hide_busy_bar() # These are overrides, so return them from there - return self.get_from_overrides(kn) + return self.get_from_overrides(kn, default) return self.get_from_current(kn, default) @@ -244,9 +244,10 @@ def changed(self): def set(self, kn, v): # print('set({}, {}'.format(kn, v)) - if self.temporary_mode and kn not in DEVICE_SETTINGS: + if self.temporary_mode: self.temporary_settings[kn] = v - return + if kn not in DEVICE_SETTINGS: + return self.current[kn] = v self.changed() @@ -256,7 +257,7 @@ def get_from_current(self, kn, default): return self.temporary_settings.get(kn, default) return self.current.get(kn, default) - def get_from_overrides(kn, default): + def get_from_overrides(self, kn, default): if self.temporary_mode: return self.temporary_overrides.get(kn, default) return self.overrides.get(kn, default) @@ -342,7 +343,7 @@ async def write_out(self): # Was sometimes running low on memory in this area: recover try: gc.collect() - self.save() + self.internal_save() except MemoryError: call_later_ms(250, self.write_out()) @@ -392,14 +393,16 @@ def erase_all(self): self.erase_cache_entry(pos) self.blank() - def save(self): - if self.temporary_mode: - return - + def internal_save(self): # Make two saves in case one is corrupted self.do_save(erase_old_pos=True) self.do_save(erase_old_pos=False) + def save(self): + if self.temporary_mode: + return + self.internal_save() + def do_save(self, erase_old_pos=True): # print('do_save({})'.format(erase_old_pos)) # render as JSON, encrypt and write it. diff --git a/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py b/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py index 5c9838680..6bcfcac07 100644 --- a/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py @@ -5,13 +5,17 @@ import lvgl as lv from flows import Flow +from common import settings class InitialSeedSetupFlow(Flow): - def __init__(self, allow_backtrack=True): + def __init__(self, allow_backtrack=True, temporary=False): super().__init__(initial_state=self.show_intro, name='InitialSeedSetupFlow') self.statusbar = {'title': 'CREATE SEED', 'icon': 'ICON_SEED'} self.allow_backtrack = allow_backtrack + self.temporary = temporary + if temporary: + settings.enter_temporary_mode() async def show_intro(self): from pages import InfoPage @@ -19,7 +23,7 @@ async def show_intro(self): import microns # Pass silently if seed already exists - if has_seed(): + if has_seed() and not self.temporary: self.set_result(True) return @@ -35,6 +39,8 @@ async def show_intro(self): if result: self.goto(self.show_seed_setup_menu) else: + if self.temporary: + settings.exit_temporary_mode() self.set_result(None) async def show_seed_setup_menu(self): @@ -42,10 +48,19 @@ async def show_seed_setup_menu(self): from flows import NewSeedFlow, RestoreSeedFlow, RestoreBackupFlow import microns - options = [{'label': 'Create New Seed', - 'value': lambda: NewSeedFlow(full_backup=True)}, - {'label': 'Import Seed', 'value': lambda: RestoreSeedFlow(full_backup=True)}, - {'label': 'Restore Backup', 'value': lambda: RestoreBackupFlow(full_backup=True)}] + options = [] + + if not self.temporary: + options.append({'label': 'Create New Seed', + 'value': lambda: NewSeedFlow(full_backup=True)}) + + options.extend([{'label': 'Import Seed', 'value': lambda: RestoreSeedFlow(full_backup=True)}, + {'label': 'Restore Backup', 'value': lambda: RestoreBackupFlow(full_backup=True)}]) + + if not self.temporary: + options.append({'label': 'Temporary Seed', + 'value': lambda: InitialSeedSetupFlow(allow_backtrack=self.allow_backtrack, + temporary=True)}) flow = await ChooserPage( text=None, diff --git a/ports/stm32/boards/Passport/modules/stash.py b/ports/stm32/boards/Passport/modules/stash.py index 04a58feaa..480b199b4 100644 --- a/ports/stm32/boards/Passport/modules/stash.py +++ b/ports/stm32/boards/Passport/modules/stash.py @@ -130,17 +130,23 @@ class SensitiveValues: # be a context manager, and holder to secrets in-memory def __init__(self, secret=None, for_backup=False): - from common import system + from common import system, settings if secret is None: - # fetch the secret from bootloader/atecc508a - from common import pa + if settings.temporary_mode: + if settings.get('temporary_seed', None) is None: + raise ValueError('no temporary secrets yet') + self.secret = settings.get('temporary_seed', None) + self.spots = [] + else: + # fetch the secret from bootloader/atecc508a + from common import pa - if pa.is_secret_blank(): - raise ValueError('no secrets yet') + if pa.is_secret_blank(): + raise ValueError('no secrets yet') - self.secret = pa.fetch() - self.spots = [self.secret] + self.secret = pa.fetch() + self.spots = [self.secret] else: # sometimes we already know it # assert set(secret) != {0} diff --git a/ports/stm32/boards/Passport/modules/tasks/get_seed_words_task.py b/ports/stm32/boards/Passport/modules/tasks/get_seed_words_task.py index 383ef83e6..ff88449af 100644 --- a/ports/stm32/boards/Passport/modules/tasks/get_seed_words_task.py +++ b/ports/stm32/boards/Passport/modules/tasks/get_seed_words_task.py @@ -19,4 +19,4 @@ async def get_seed_words_task(on_done): except Exception as e: # print('get_seed_words_task(): Exception: {}'.format(e)) # Unable to read seed! - await on_done(None, None, '{}'.format(e)) + await on_done(None, '{}'.format(e)) diff --git a/ports/stm32/boards/Passport/modules/tasks/save_seed_task.py b/ports/stm32/boards/Passport/modules/tasks/save_seed_task.py index 6f06e815b..e0812e4a1 100644 --- a/ports/stm32/boards/Passport/modules/tasks/save_seed_task.py +++ b/ports/stm32/boards/Passport/modules/tasks/save_seed_task.py @@ -13,20 +13,23 @@ async def save_seed_task(on_done, seed_bits): - from common import pa + from common import pa, settings import stash try: secret = SecretStash.encode(seed_bits=seed_bits) - pa.change(new_secret=secret) + if settings.temporary_mode: + settings.set_volatile('temporary_seed', secret) + else: + pa.change(new_secret=secret) - # Recapture XFP, etc. for new secret - await pa.new_main_secret(secret) + # Recapture XFP, etc. for new secret + await pa.new_main_secret(secret) - # Check and reload secret - pa.reset() - pa.login() + # Check and reload secret + pa.reset() + pa.login() with stash.SensitiveValues() as sv: sv.capture_xpub(save=True) diff --git a/ports/stm32/boards/Passport/modules/utils.py b/ports/stm32/boards/Passport/modules/utils.py index 31811dc63..f79d7461e 100644 --- a/ports/stm32/boards/Passport/modules/utils.py +++ b/ports/stm32/boards/Passport/modules/utils.py @@ -1155,7 +1155,10 @@ def set_list(lst, index, value): def has_seed(): - from common import pa + from common import pa, settings + + if settings.temporary_mode: + return settings.get('temporary_seed', None) is not None # pa.is_secret_blank() function returns True before we are logged in, which is not right. if not is_logged_in(): From 11184e4fb152c6e3d3b8a0c1ee39e4e655460fd6 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Fri, 5 Apr 2024 17:25:11 -0400 Subject: [PATCH 068/126] SFT-1728: temporarily restored from backups --- ports/stm32/boards/Passport/modules/ext_settings.py | 5 +---- .../Passport/modules/flows/restore_backup_flow.py | 3 +++ .../Passport/modules/tasks/restore_backup_task.py | 13 ++++++++----- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/ext_settings.py b/ports/stm32/boards/Passport/modules/ext_settings.py index 7fc451511..a5c28938b 100644 --- a/ports/stm32/boards/Passport/modules/ext_settings.py +++ b/ports/stm32/boards/Passport/modules/ext_settings.py @@ -489,10 +489,7 @@ def enter_temporary_mode(self): return self.temporary_mode = True - - # Merge the overrides and persistent settings into a new dict - self.temporary_settings.update(self.current) - self.temporary_overrides.update(self.overrides) + self.temporary_settings = self.default_values() def exit_temporary_mode(self): self.temporary_mode = False diff --git a/ports/stm32/boards/Passport/modules/flows/restore_backup_flow.py b/ports/stm32/boards/Passport/modules/flows/restore_backup_flow.py index c767706eb..9be60deed 100644 --- a/ports/stm32/boards/Passport/modules/flows/restore_backup_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/restore_backup_flow.py @@ -125,6 +125,7 @@ async def do_restore(self): from utils import start_task from flows import AutoBackupFlow, BackupFlow from pages import InfoPage + from common import settings # TODO: Change from spinner to ProgressPage and pass on_progress instead of None below. (error,) = await spinner_task( @@ -142,6 +143,8 @@ async def do_restore(self): if error_2 is not None or self.backup_code != new_backup_code: await InfoPage("You will receive a new Backup Code to use with your new Passport.").show() await BackupFlow(initial_backup=True).run() + else: # No error and self.backup_code == new_backup_code + settings.set('backup_quiz', True) elif self.autobackup: await AutoBackupFlow(offer=True).run() diff --git a/ports/stm32/boards/Passport/modules/tasks/restore_backup_task.py b/ports/stm32/boards/Passport/modules/tasks/restore_backup_task.py index f2a6afc10..7cded7282 100644 --- a/ports/stm32/boards/Passport/modules/tasks/restore_backup_task.py +++ b/ports/stm32/boards/Passport/modules/tasks/restore_backup_task.py @@ -107,11 +107,14 @@ async def restore_backup_task(on_done, decryption_password, backup_file_path): await on_done(Error.CORRUPT_BACKUP_FILE) return - # Set the secret into the Secure Element - pa.change(new_secret=raw) - - # Force the right chain and update XFP & XPUB - await pa.new_main_secret(raw, chain) + if settings.temporary_mode: + settings.set_volatile('temporary_seed', raw) + else: + # Set the secret into the Secure Element + pa.change(new_secret=raw) + + # Force the right chain and update XFP & XPUB + await pa.new_main_secret(raw, chain) # Finally, restore the settings for idx, k in enumerate(vals): From f5c9070beb4044eb52a93957e8e85edbc07cacc5 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Mon, 8 Apr 2024 12:34:38 -0400 Subject: [PATCH 069/126] SFT-1728: accounted for device settings --- .../boards/Passport/modules/ext_settings.py | 66 +++++++++---------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/ext_settings.py b/ports/stm32/boards/Passport/modules/ext_settings.py index a5c28938b..ad63cdc89 100644 --- a/ports/stm32/boards/Passport/modules/ext_settings.py +++ b/ports/stm32/boards/Passport/modules/ext_settings.py @@ -244,65 +244,63 @@ def changed(self): def set(self, kn, v): # print('set({}, {}'.format(kn, v)) - if self.temporary_mode: - self.temporary_settings[kn] = v - if kn not in DEVICE_SETTINGS: - return - - self.current[kn] = v - self.changed() + if not self.temporary_mode or kn in DEVICE_SETTINGS: + self.current[kn] = v + self.changed() + return + self.temporary_settings[kn] = v def get_from_current(self, kn, default): - if self.temporary_mode: - return self.temporary_settings.get(kn, default) - return self.current.get(kn, default) + if not self.temporary_mode or kn in DEVICE_SETTINGS: + return self.current.get(kn, default) + return self.temporary_settings.get(kn, default) def get_from_overrides(self, kn, default): - if self.temporary_mode: - return self.temporary_overrides.get(kn, default) - return self.overrides.get(kn, default) + if not self.temporary_mode or kn in DEVICE_SETTINGS: + return self.overrides.get(kn, default) + return self.temporary_overrides.get(kn, default) def set_volatile(self, kn, v): - if self.temporary_mode: - self.temporary_overrides[kn] = v + if not self.temporary_mode or kn in DEVICE_SETTINGS: + self.overrides[kn] = v return - self.overrides[kn] = v + self.temporary_overrides[kn] = v def in_current(self, kn): - if self.temporary_mode: - return kn in self.temporary_settings - return kn in self.current + if not self.temporary_mode or kn in DEVICE_SETTINGS: + return kn in self.current + return kn in self.temporary_settings def in_overrides(self, kn): - if self.temporary_mode: - return kn in self.temporary_overrides - return kn in self.overrides + if not self.temporary_mode or kn in DEVICE_SETTINGS: + return kn in self.overrides + return kn in self.temporary_overrides def clear_volatile(self, kn): - if self.temporary_mode: - if kn in self.temporary_overrides: - del self.temporary_overrides[kn] + if not self.temporary_mode or kn in DEVICE_SETTINGS: + if kn in self.overrides: + del self.overrides[kn] return - if kn in self.overrides: - del self.overrides[kn] + if kn in self.temporary_overrides: + del self.temporary_overrides[kn] def remove(self, kn): # print('remove(\'{}\') called!'.format(kn)) if self.in_current(kn): - if self.temporary_mode: - self.temporary_settings.pop(kn, None) + if not self.temporary_mode or kn in DEVICE_SETTINGS: + self.current.pop(kn, None) + self.changed() return - self.current.pop(kn, None) - self.changed() + self.temporary_settings.pop(kn, None) def remove_regex(self, pattern): import re pattern = re.compile(pattern) matches = [] - if self.temporary_mode: - matches = [k for k in self.temporary_settings if pattern.search(k)] - else: + if self.temporary_mode or kn in DEVICE_SETTINGS: matches = [k for k in self.current if pattern.search(k)] + else: + matches = [k for k in self.temporary_settings if pattern.search(k)] for k in matches: self.remove(k) From 5ede4139452530fea8e82dee08fbd2c6d1cce8f7 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Mon, 8 Apr 2024 22:46:43 -0400 Subject: [PATCH 070/126] SFT-1728: entering temporary seed after permanent setup --- ports/stm32/boards/Passport/manifest.py | 1 + .../boards/Passport/modules/flows/__init__.py | 1 + .../modules/flows/temporary_seed_flow.py | 28 +++++++++++++++++++ ports/stm32/boards/Passport/modules/menus.py | 9 ++++-- ports/stm32/boards/Passport/modules/utils.py | 14 ++++++---- 5 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py diff --git a/ports/stm32/boards/Passport/manifest.py b/ports/stm32/boards/Passport/manifest.py index 62cb6d2eb..d0f41a531 100644 --- a/ports/stm32/boards/Passport/manifest.py +++ b/ports/stm32/boards/Passport/manifest.py @@ -152,6 +152,7 @@ 'flows/sign_psbt_microsd_flow.py', 'flows/sign_psbt_qr_flow.py', 'flows/sign_text_file_flow.py', + 'flows/temporary_seed_flow.py', 'flows/terms_of_use_flow.py', 'flows/update_firmware_flow.py', 'flows/verify_address_flow.py', diff --git a/ports/stm32/boards/Passport/modules/flows/__init__.py b/ports/stm32/boards/Passport/modules/flows/__init__.py index 2b8782c3e..952fff863 100644 --- a/ports/stm32/boards/Passport/modules/flows/__init__.py +++ b/ports/stm32/boards/Passport/modules/flows/__init__.py @@ -88,6 +88,7 @@ # Top-level flows need to be declared after other flows that they use from .initial_seed_setup_flow import * +from .temporary_seed_flow import * from .envoy_setup_flow import * from .manual_setup_flow import * from .select_setup_mode_flow import * diff --git a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py new file mode 100644 index 000000000..8b21b570b --- /dev/null +++ b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py @@ -0,0 +1,28 @@ +# SPDX-FileCopyrightText: © 2024 Foundation Devices, Inc. +# SPDX-License-Identifier: GPL-3.0-or-later +# +# temporary_seed_flow.py - Start using a temporary seed + +from flows import Flow + + +class TemporarySeedFlow(Flow): + def __init__(self, clear=False): + initial_state = self.enter_seed if not clear else self.clear_seed + super().__init__(initial_state=initial_state, name='TemporarySeedFlow') + + async def enter_seed(self): + from flows import InitialSeedSetupFlow + result = await InitialSeedSetupFlow(temporary=True).run() + self.set_result(result) + + async def clear_seed(self): + from utils import spinner_task + from tasks import delay_task + from common import settings + from pages import SuccessPage + + await spinner_task('Clearing Temporary Seed', delay_task, args=[1000, False]) + await SuccessPage(text='Temporary seed cleared').show() + settings.exit_temporary_mode() + self.set_result(True) diff --git a/ports/stm32/boards/Passport/modules/menus.py b/ports/stm32/boards/Passport/modules/menus.py index ac8d63395..d5c6ddd00 100644 --- a/ports/stm32/boards/Passport/modules/menus.py +++ b/ports/stm32/boards/Passport/modules/menus.py @@ -131,8 +131,8 @@ def postmix_menu(): def plus_menu(): - from utils import is_passphrase_active - from flows import NewAccountFlow, ApplyPassphraseFlow + from utils import is_passphrase_active, has_temporary_seed, has_permanent_seed + from flows import NewAccountFlow, ApplyPassphraseFlow, TemporarySeedFlow return [ {'icon': 'ICON_ADD_ACCOUNT', 'label': 'New Account', 'flow': NewAccountFlow}, @@ -142,6 +142,11 @@ def plus_menu(): 'args': {'passphrase': ''}, 'statusbar': {'title': 'PASSPHRASE'}, 'is_visible': is_passphrase_active}, {'icon': 'ICON_PASSPHRASE', 'label': 'Change Passphrase', 'flow': ApplyPassphraseFlow, 'statusbar': {'title': 'PASSPHRASE'}, 'is_visible': is_passphrase_active}, + {'icon': 'ICON_PASSPHRASE', 'label': 'Enter Temporary Seed', 'flow': TemporarySeedFlow, + 'statusbar': {'title': 'TEMPORARY SEED'}, 'is_visible': lambda: not has_temporary_seed()}, + {'icon': 'ICON_PASSPHRASE', 'label': 'Clear Temporary Seed', 'flow': TemporarySeedFlow, + 'args': {'clear': True}, 'statusbar': {'title': 'TEMPORARY SEED'}, + 'is_visible': lambda: has_temporary_seed() and has_permanent_seed()}, ] diff --git a/ports/stm32/boards/Passport/modules/utils.py b/ports/stm32/boards/Passport/modules/utils.py index f79d7461e..84be6c26f 100644 --- a/ports/stm32/boards/Passport/modules/utils.py +++ b/ports/stm32/boards/Passport/modules/utils.py @@ -1154,17 +1154,21 @@ def set_list(lst, index, value): lst[index] = value -def has_seed(): - from common import pa, settings +def has_temporary_seed(): + if common.settings.temporary_mode: + return common.settings.get('temporary_seed', None) is not None + return False - if settings.temporary_mode: - return settings.get('temporary_seed', None) is not None +def has_permanent_seed(): # pa.is_secret_blank() function returns True before we are logged in, which is not right. if not is_logged_in(): return False + return not common.pa.is_secret_blank() - return not pa.is_secret_blank() + +def has_seed(): + return has_temporary_seed() or has_permanent_seed() def is_logged_in(): From c1f346a4a9790da1f91a1e306e8d9189167428c7 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Tue, 9 Apr 2024 17:28:56 -0400 Subject: [PATCH 071/126] SFT-1728: entering temporary mode from key manager, still needs UI work --- .../modules/flows/export_derived_key_flow.py | 1 + .../modules/flows/initial_seed_setup_flow.py | 35 +++++++++++++- .../modules/flows/temporary_seed_flow.py | 48 ++++++++++++++++--- ports/stm32/boards/Passport/modules/menus.py | 7 ++- 4 files changed, 81 insertions(+), 10 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/export_derived_key_flow.py b/ports/stm32/boards/Passport/modules/flows/export_derived_key_flow.py index a47ede1fc..64f6b431d 100644 --- a/ports/stm32/boards/Passport/modules/flows/export_derived_key_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/export_derived_key_flow.py @@ -23,6 +23,7 @@ async def generate_key(self): from flows import ViewSeedWordsFlow from public_constants import DIR_KEY_MNGR + # TODO: make this process a util function self.key_type = get_key_type_from_tn(self.key['tn']) if not self.key_type: diff --git a/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py b/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py index 6bcfcac07..adaa69396 100644 --- a/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py @@ -9,13 +9,18 @@ class InitialSeedSetupFlow(Flow): - def __init__(self, allow_backtrack=True, temporary=False): - super().__init__(initial_state=self.show_intro, name='InitialSeedSetupFlow') + def __init__(self, allow_backtrack=True, temporary=False, external_key=None): self.statusbar = {'title': 'CREATE SEED', 'icon': 'ICON_SEED'} self.allow_backtrack = allow_backtrack self.temporary = temporary + self.external_key = external_key + initial_state = self.show_intro if temporary: settings.enter_temporary_mode() + # TODO: go to "explain_temporary" first + if self.external_key: + initial_state = self.apply_external_key + super().__init__(initial_state=initial_state, name='InitialSeedSetupFlow') async def show_intro(self): from pages import InfoPage @@ -76,3 +81,29 @@ async def show_seed_setup_menu(self): return else: self.set_result(True) + + async def apply_external_key(self): + from utils import spinner_task + from tasks import save_seed_task + from common import ui + from pages import SuccessPage + + # Only allowed in temporary mode + if not self.temporary: + # TODO: add error messaging + print("setting external key in permanent mode") + self.set_result(False) + return + + (error,) = await spinner_task('Saving seed', save_seed_task, args=[self.external_key]) + + if error is not None: + options.exit_temporary_mode() + self.set_result(None) + return + + await SuccessPage(text='Temporary seed applied.').show() + + ui.full_cards_refresh() + await self.wait_to_die() + self.set_result(True) diff --git a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py index 8b21b570b..de3fef85e 100644 --- a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py @@ -7,8 +7,15 @@ class TemporarySeedFlow(Flow): - def __init__(self, clear=False): - initial_state = self.enter_seed if not clear else self.clear_seed + def __init__(self, context=None, clear=False): + self.key = context + print("context: {}".format(context)) + self.pk = None + initial_state = self.enter_seed + if self.key is not None: + initial_state = self.use_child_seed + elif clear: + initial_state = self.clear_seed super().__init__(initial_state=initial_state, name='TemporarySeedFlow') async def enter_seed(self): @@ -17,12 +24,41 @@ async def enter_seed(self): self.set_result(result) async def clear_seed(self): - from utils import spinner_task + from utils import spinner_task, start_task from tasks import delay_task - from common import settings + from common import settings, ui from pages import SuccessPage await spinner_task('Clearing Temporary Seed', delay_task, args=[1000, False]) - await SuccessPage(text='Temporary seed cleared').show() settings.exit_temporary_mode() - self.set_result(True) + await SuccessPage(text='Temporary seed cleared').show() + + # TODO: ui is still buggy when clearing a seed + ui.full_cards_refresh() + await self.wait_to_die() + self.set_result(False) + + async def use_child_seed(self): + from derived_key import get_key_type_from_tn + from utils import spinner_task + from pages import ErrorPage + from flows import InitialSeedSetupFlow + + self.key_type = get_key_type_from_tn(self.key['tn']) + + if not self.key_type: + await ErrorPage("Invalid key type number: {}".format(self.key['tn'])).show() + self.set_result(False) + return + + (vals, error) = await spinner_task(text='Generating key', + task=self.key_type['task'], + args=[self.key['index']]) + self.pk = vals['priv'] + if error is not None: + await ErrorPage(error).show() + self.set_result(False) + return + + result = await InitialSeedSetupFlow(temporary=True, external_key=self.pk).run() + self.set_result(result) diff --git a/ports/stm32/boards/Passport/modules/menus.py b/ports/stm32/boards/Passport/modules/menus.py index d5c6ddd00..b5c3e0937 100644 --- a/ports/stm32/boards/Passport/modules/menus.py +++ b/ports/stm32/boards/Passport/modules/menus.py @@ -179,16 +179,19 @@ def backup_menu(): def key_item_menu(): - from utils import toggle_key_hidden, is_key_hidden + from utils import toggle_key_hidden, is_key_hidden, has_temporary_seed from flows import ( ViewDerivedKeyDetailsFlow, RenameDerivedKeyFlow, - ExportDerivedKeyFlow) + ExportDerivedKeyFlow, + TemporarySeedFlow) return [ {'icon': 'ICON_ONE_KEY', 'label': 'View Details', 'flow': ViewDerivedKeyDetailsFlow}, {'icon': 'ICON_INFO', 'label': 'Rename', 'flow': RenameDerivedKeyFlow, 'auto_card_header': False}, {'icon': 'ICON_SCAN_QR', 'label': 'Export', 'flow': ExportDerivedKeyFlow}, + {'icon': 'ICON_SEED', 'label': 'Use as Temporary Seed', 'flow': TemporarySeedFlow, + 'is_visible': lambda: not has_temporary_seed()}, {'icon': 'ICON_ERASE', 'label': 'Hide Key', 'action': lambda item, context: toggle_key_hidden(item, context), From e5b79c0ea1bfc8142470e3ab23c3fd56ef0138f5 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Wed, 10 Apr 2024 13:41:51 -0400 Subject: [PATCH 072/126] SFT-1728: fixed simulator in temporary mode, improved temporary mode UI --- .../boards/Passport/modules/ext_settings.py | 8 ++-- .../modules/flows/initial_seed_setup_flow.py | 41 ++++++++++++++----- .../modules/flows/restore_seed_flow.py | 10 +++-- simulator/sim_modules/pincodes.py | 28 ++++++------- 4 files changed, 56 insertions(+), 31 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/ext_settings.py b/ports/stm32/boards/Passport/modules/ext_settings.py index ad63cdc89..9e4fcc942 100644 --- a/ports/stm32/boards/Passport/modules/ext_settings.py +++ b/ports/stm32/boards/Passport/modules/ext_settings.py @@ -242,9 +242,9 @@ def changed(self): if self.is_dirty < 2 and self.loop: call_later_ms(250, self.write_out()) - def set(self, kn, v): + def set(self, kn, v, permanent=False): # print('set({}, {}'.format(kn, v)) - if not self.temporary_mode or kn in DEVICE_SETTINGS: + if (not self.temporary_mode or kn in DEVICE_SETTINGS) or permanent: self.current[kn] = v self.changed() return @@ -396,8 +396,8 @@ def internal_save(self): self.do_save(erase_old_pos=True) self.do_save(erase_old_pos=False) - def save(self): - if self.temporary_mode: + def save(self, permanent=False): + if self.temporary_mode and not permanent: return self.internal_save() diff --git a/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py b/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py index adaa69396..1d861f69b 100644 --- a/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py @@ -17,9 +17,7 @@ def __init__(self, allow_backtrack=True, temporary=False, external_key=None): initial_state = self.show_intro if temporary: settings.enter_temporary_mode() - # TODO: go to "explain_temporary" first - if self.external_key: - initial_state = self.apply_external_key + initial_state = self.explain_temporary super().__init__(initial_state=initial_state, name='InitialSeedSetupFlow') async def show_intro(self): @@ -28,7 +26,7 @@ async def show_intro(self): import microns # Pass silently if seed already exists - if has_seed() and not self.temporary: + if has_seed(): self.set_result(True) return @@ -44,10 +42,34 @@ async def show_intro(self): if result: self.goto(self.show_seed_setup_menu) else: - if self.temporary: - settings.exit_temporary_mode() self.set_result(None) + async def explain_temporary(self): + from pages import InfoPage + import microns + + if self.allow_backtrack: + left_micron = microns.Back + else: + left_micron = None + + result = await InfoPage( + icon=lv.LARGE_ICON_SEED, + text='This temporary seed will not be saved, so be sure you have a backup, or create one during setup', + left_micron=left_micron, + right_micron=microns.Forward).show() + + if not result: + settings.exit_temporary_mode() + self.set_result(None) + return + + if self.external_key: + self.goto(self.apply_external_key) + return + + self.goto(self.show_seed_setup_menu) + async def show_seed_setup_menu(self): from pages import ChooserPage from flows import NewSeedFlow, RestoreSeedFlow, RestoreBackupFlow @@ -86,16 +108,15 @@ async def apply_external_key(self): from utils import spinner_task from tasks import save_seed_task from common import ui - from pages import SuccessPage + from pages import SuccessPage, ErrorPage # Only allowed in temporary mode if not self.temporary: - # TODO: add error messaging - print("setting external key in permanent mode") + await ErrorPage('Unable to use an external key in temporary mode').show() self.set_result(False) return - (error,) = await spinner_task('Saving seed', save_seed_task, args=[self.external_key]) + (error,) = await spinner_task('Applying seed', save_seed_task, args=[self.external_key]) if error is not None: options.exit_temporary_mode() diff --git a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py index 64824777e..5e333cdd1 100644 --- a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py @@ -166,13 +166,16 @@ async def invalid_seed(self): async def valid_seed(self): from flows import AutoBackupFlow, BackupFlow from utils import get_seed_from_words + from common import settings entropy = get_seed_from_words(self.mnemonic) - (error,) = await spinner_task('Saving seed', save_seed_task, args=[entropy]) + text = '{} seed'.format('Applying' if settings.temporary_mode else 'Saving') + (error,) = await spinner_task(text, save_seed_task, args=[entropy]) if error is None: import common - await SuccessPage(text='New seed imported and saved.').show() + text = 'New seed imported and {}'.format('applied' if settings.temporary_mode else 'saved') + await SuccessPage(text=text).show() if self.full_backup: await BackupFlow(initial_backup=True).run() elif self.autobackup: @@ -185,5 +188,6 @@ async def valid_seed(self): else: self.set_result(True) else: - await ErrorPage('Unable to save seed.').show() + text = 'Unable to {} seed'.format('apply' if settings.temporary_mode else 'save') + await ErrorPage(text).show() self.set_result(False) diff --git a/simulator/sim_modules/pincodes.py b/simulator/sim_modules/pincodes.py index 478181593..60f164953 100644 --- a/simulator/sim_modules/pincodes.py +++ b/simulator/sim_modules/pincodes.py @@ -139,8 +139,8 @@ def supply_chain_validation_words(challenge_str): return rv[0:4] # Only keep 4 words for supply chain validation def is_blank(self): - self.pin = common.settings.get('__pin__', '') - pin = common.settings.get('__pin__', '') + self.pin = common.settings.current.get('__pin__', '') + pin = common.settings.current.get('__pin__', '') return pin == '' def is_successful(self): @@ -162,14 +162,14 @@ def setup(self, pin, secondary=False): from common import settings # print('Setting up pin for login attempt') self.pin = pin - self.attempts_left = settings.get( + self.attempts_left = settings.current.get( '__attempts_left__', MAX_PIN_ATTEMPTS) return 0 def login(self): from common import settings - curr_pin = settings.get('__pin__', '') + curr_pin = settings.current.get('__pin__', '') self.is_logged_in = False # print('login(): self.pin={}, curr_pin={}'.format(self.pin, curr_pin)) @@ -181,14 +181,14 @@ def login(self): self.num_fails += 1 if self.attempts_left > MAX_PIN_ATTEMPTS: self.attempts_left = MAX_PIN_ATTEMPTS - settings.set('__attempts_left__', self.attempts_left) + settings.set('__attempts_left__', self.attempts_left, True) raise RuntimeError() else: # Reset to 21 when successfully logged in self.attempts_left = MAX_PIN_ATTEMPTS self.is_logged_in = True self.num_fails = 0 - settings.set('__attempts_left__', MAX_PIN_ATTEMPTS) + settings.set('__attempts_left__', MAX_PIN_ATTEMPTS, True) return self.is_logged_in @@ -198,7 +198,7 @@ def change(self, **kwargs): new_secret = kwargs.get('new_secret', None) if new_secret == None: - curr_pin = settings.get('__pin__', '').encode() + curr_pin = settings.current.get('__pin__', '').encode() old_pin = kwargs.get('old_pin', '').encode() new_pin = kwargs.get('new_pin', '').encode() @@ -209,12 +209,12 @@ def change(self, **kwargs): # print('new_pin={}, len={}'.format(new_pin, len(new_pin))) if isinstance(new_pin, (bytes, bytearray)) and is_all_zero(new_pin): # print('RECEIVED BLANK PIN!') - settings.set('__pin__', '') + settings.set('__pin__', '', True) else: - settings.set('__pin__', new_pin.decode()) + settings.set('__pin__', new_pin.decode(), True) # We have to save because the caller tries to reset before the normal save timeout expires - settings.save() + settings.save(True) else: # print('change(): {}'.format(new_secret)) @@ -224,13 +224,13 @@ def save_secret(self, buf): from common import settings str = b2a_hex(buf).decode('utf-8') # print('save_secret: {}'.format(str)) - settings.set('__secret__', str) - settings.save() + settings.set('__secret__', str, True) + settings.save(True) self.load_secret() def load_secret(self): from common import settings - str = settings.get('__secret__', ZERO_SECRET_STR) + str = settings.current.get('__secret__', ZERO_SECRET_STR) self.secret = a2b_hex(str.encode('utf-8')) def fetch(self): @@ -250,7 +250,7 @@ async def new_main_secret(self, raw_secret, chain=None): # TODO: Is this comment still accurate? # We shouldn't need to save this anymore since we are dynamically managing xfp and xpub - # settings.save() + # settings.save(True) # Set the key for the flash cache (cache was inaccessible prior to user logging in) flash_cache.set_key(new_secret=raw_secret) From d14e9a2f77e29ec548fb17c72fb0993d9cab505a Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Thu, 11 Apr 2024 20:50:47 -0400 Subject: [PATCH 073/126] SFT-1728: moved temporary functionality into temporary_seed_flow --- .../modules/flows/initial_seed_setup_flow.py | 79 ++----------------- .../modules/flows/restore_seed_flow.py | 58 ++++++++++++-- .../modules/flows/temporary_seed_flow.py | 64 +++++++++++---- ports/stm32/boards/Passport/modules/menus.py | 6 +- 4 files changed, 109 insertions(+), 98 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py b/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py index 1d861f69b..5c9838680 100644 --- a/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/initial_seed_setup_flow.py @@ -5,20 +5,13 @@ import lvgl as lv from flows import Flow -from common import settings class InitialSeedSetupFlow(Flow): - def __init__(self, allow_backtrack=True, temporary=False, external_key=None): + def __init__(self, allow_backtrack=True): + super().__init__(initial_state=self.show_intro, name='InitialSeedSetupFlow') self.statusbar = {'title': 'CREATE SEED', 'icon': 'ICON_SEED'} self.allow_backtrack = allow_backtrack - self.temporary = temporary - self.external_key = external_key - initial_state = self.show_intro - if temporary: - settings.enter_temporary_mode() - initial_state = self.explain_temporary - super().__init__(initial_state=initial_state, name='InitialSeedSetupFlow') async def show_intro(self): from pages import InfoPage @@ -44,50 +37,15 @@ async def show_intro(self): else: self.set_result(None) - async def explain_temporary(self): - from pages import InfoPage - import microns - - if self.allow_backtrack: - left_micron = microns.Back - else: - left_micron = None - - result = await InfoPage( - icon=lv.LARGE_ICON_SEED, - text='This temporary seed will not be saved, so be sure you have a backup, or create one during setup', - left_micron=left_micron, - right_micron=microns.Forward).show() - - if not result: - settings.exit_temporary_mode() - self.set_result(None) - return - - if self.external_key: - self.goto(self.apply_external_key) - return - - self.goto(self.show_seed_setup_menu) - async def show_seed_setup_menu(self): from pages import ChooserPage from flows import NewSeedFlow, RestoreSeedFlow, RestoreBackupFlow import microns - options = [] - - if not self.temporary: - options.append({'label': 'Create New Seed', - 'value': lambda: NewSeedFlow(full_backup=True)}) - - options.extend([{'label': 'Import Seed', 'value': lambda: RestoreSeedFlow(full_backup=True)}, - {'label': 'Restore Backup', 'value': lambda: RestoreBackupFlow(full_backup=True)}]) - - if not self.temporary: - options.append({'label': 'Temporary Seed', - 'value': lambda: InitialSeedSetupFlow(allow_backtrack=self.allow_backtrack, - temporary=True)}) + options = [{'label': 'Create New Seed', + 'value': lambda: NewSeedFlow(full_backup=True)}, + {'label': 'Import Seed', 'value': lambda: RestoreSeedFlow(full_backup=True)}, + {'label': 'Restore Backup', 'value': lambda: RestoreBackupFlow(full_backup=True)}] flow = await ChooserPage( text=None, @@ -103,28 +61,3 @@ async def show_seed_setup_menu(self): return else: self.set_result(True) - - async def apply_external_key(self): - from utils import spinner_task - from tasks import save_seed_task - from common import ui - from pages import SuccessPage, ErrorPage - - # Only allowed in temporary mode - if not self.temporary: - await ErrorPage('Unable to use an external key in temporary mode').show() - self.set_result(False) - return - - (error,) = await spinner_task('Applying seed', save_seed_task, args=[self.external_key]) - - if error is not None: - options.exit_temporary_mode() - self.set_result(None) - return - - await SuccessPage(text='Temporary seed applied.').show() - - ui.full_cards_refresh() - await self.wait_to_die() - self.set_result(True) diff --git a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py index 5e333cdd1..24e26b606 100644 --- a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py @@ -10,11 +10,12 @@ from utils import spinner_task, insufficient_randomness from tasks import save_seed_task from public_constants import SEED_LENGTHS +import lvgl as lv class RestoreSeedFlow(Flow): - def __init__(self, refresh_cards_when_done=False, autobackup=True, full_backup=False): - super().__init__(initial_state=self.choose_restore_method, name='RestoreSeedFlow') + def __init__(self, refresh_cards_when_done=False, autobackup=True, full_backup=False, + temporary=False): self.refresh_cards_when_done = refresh_cards_when_done self.seed_format = None self.seed_length = None @@ -24,6 +25,49 @@ def __init__(self, refresh_cards_when_done=False, autobackup=True, full_backup=F self.full_backup = full_backup self.autobackup = autobackup self.statusbar = {'title': 'IMPORT SEED', 'icon': 'ICON_SEED'} + self.temporary = temporary + + initial_state = self.choose_temporary + if self.temporary: + initial_state = self.explain_temporary + + super().__init__(initial_state=initial_state, name='RestoreSeedFlow') + + async def choose_temporary(self): + from pages import ChooserPage + + text = 'Would you like to make a permanent seed, or a temporary seed?' + options = [{'label': 'Permanent', 'value': True}, + {'label': 'Temporary', 'value': False}] + + permanent = await ChooserPage(text=text, + card_header={'title': 'Seed Type'}, + options=options).show() + if permanent is None: + self.set_result(False) + return + + if permanent: + self.goto(self.choose_restore_method) + else: + self.goto(self.explain_temporary) + + async def explain_temporary(self): + from pages import InfoPage + import microns + + result = await InfoPage( + icon=lv.LARGE_ICON_SEED, + text='This temporary seed will not be saved, so be sure you have a backup, or create one during setup', + left_micron=microns.Back, + right_micron=microns.Forward).show() + + if not result: + if not self.back(): + self.set_result(None) + return + + self.goto(self.choose_restore_method) async def choose_restore_method(self): from pages import ChooserPage @@ -37,7 +81,8 @@ async def choose_restore_method(self): choice = await ChooserPage(card_header={'title': 'Seed Format'}, options=options).show() if choice is None: - self.set_result(False) + if not self.back(): + self.set_result(False) return self.seed_format = choice @@ -166,15 +211,14 @@ async def invalid_seed(self): async def valid_seed(self): from flows import AutoBackupFlow, BackupFlow from utils import get_seed_from_words - from common import settings entropy = get_seed_from_words(self.mnemonic) - text = '{} seed'.format('Applying' if settings.temporary_mode else 'Saving') + text = '{} seed'.format('Applying' if self.temporary else 'Saving') (error,) = await spinner_task(text, save_seed_task, args=[entropy]) if error is None: import common - text = 'New seed imported and {}'.format('applied' if settings.temporary_mode else 'saved') + text = 'New seed imported and {}'.format('applied' if self.temporary else 'saved') await SuccessPage(text=text).show() if self.full_backup: await BackupFlow(initial_backup=True).run() @@ -188,6 +232,6 @@ async def valid_seed(self): else: self.set_result(True) else: - text = 'Unable to {} seed'.format('apply' if settings.temporary_mode else 'save') + text = 'Unable to {} seed'.format('apply' if self.temporary else 'save') await ErrorPage(text).show() self.set_result(False) diff --git a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py index de3fef85e..a211a76f2 100644 --- a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py @@ -4,45 +4,68 @@ # temporary_seed_flow.py - Start using a temporary seed from flows import Flow +from common import settings, ui class TemporarySeedFlow(Flow): def __init__(self, context=None, clear=False): self.key = context - print("context: {}".format(context)) - self.pk = None initial_state = self.enter_seed - if self.key is not None: - initial_state = self.use_child_seed - elif clear: + if clear: initial_state = self.clear_seed + elif self.key is not None: + initial_state = self.use_child_seed super().__init__(initial_state=initial_state, name='TemporarySeedFlow') + # Override set_result to safely exit temporary mode + def set_result(self, result, forget_state=True): + if not result: + settings.exit_temporary_mode() + super().set_result(result, forget_state) + async def enter_seed(self): - from flows import InitialSeedSetupFlow - result = await InitialSeedSetupFlow(temporary=True).run() + from flows import RestoreSeedFlow + + settings.enter_temporary_mode() + result = await RestoreSeedFlow(refresh_cards_when_done=True, + autobackup=False, + full_backup=True, + temporary=True).run() + print("temporary_settings: {}".format(settings.temporary_settings)) + print("current: {}".format(settings.current)) + print("temporary_mode: {}".format(settings.temporary_mode)) self.set_result(result) + print("temporary_mode: {}".format(settings.temporary_mode)) async def clear_seed(self): from utils import spinner_task, start_task from tasks import delay_task - from common import settings, ui from pages import SuccessPage await spinner_task('Clearing Temporary Seed', delay_task, args=[1000, False]) - settings.exit_temporary_mode() await SuccessPage(text='Temporary seed cleared').show() + settings.exit_temporary_mode() # TODO: ui is still buggy when clearing a seed + self.set_result(True) ui.full_cards_refresh() await self.wait_to_die() - self.set_result(False) async def use_child_seed(self): from derived_key import get_key_type_from_tn from utils import spinner_task - from pages import ErrorPage - from flows import InitialSeedSetupFlow + from tasks import save_seed_task + from pages import ErrorPage, SuccessPage, InfoPage + import microns + + # TODO: add page explaining child seed as temporary seed, give user change to back out + + result = await InfoPage('{} will be used as a temporary seed'.format(self.key['name']), + left_micron=microns.Back).show() + + if not result: + self.set_result(False) + return self.key_type = get_key_type_from_tn(self.key['tn']) @@ -54,11 +77,22 @@ async def use_child_seed(self): (vals, error) = await spinner_task(text='Generating key', task=self.key_type['task'], args=[self.key['index']]) - self.pk = vals['priv'] + pk = vals['priv'] if error is not None: await ErrorPage(error).show() self.set_result(False) return - result = await InitialSeedSetupFlow(temporary=True, external_key=self.pk).run() - self.set_result(result) + print('pk: {}'.format(pk)) + settings.enter_temporary_mode() + (error,) = await spinner_task('Applying seed', save_seed_task, args=[pk]) + + if error is not None: + self.set_result(None) + return + + await SuccessPage(text='Temporary seed applied.').show() + + self.set_result(True) + ui.full_cards_refresh() + await self.wait_to_die() diff --git a/ports/stm32/boards/Passport/modules/menus.py b/ports/stm32/boards/Passport/modules/menus.py index b5c3e0937..fed053ecb 100644 --- a/ports/stm32/boards/Passport/modules/menus.py +++ b/ports/stm32/boards/Passport/modules/menus.py @@ -142,9 +142,9 @@ def plus_menu(): 'args': {'passphrase': ''}, 'statusbar': {'title': 'PASSPHRASE'}, 'is_visible': is_passphrase_active}, {'icon': 'ICON_PASSPHRASE', 'label': 'Change Passphrase', 'flow': ApplyPassphraseFlow, 'statusbar': {'title': 'PASSPHRASE'}, 'is_visible': is_passphrase_active}, - {'icon': 'ICON_PASSPHRASE', 'label': 'Enter Temporary Seed', 'flow': TemporarySeedFlow, + {'icon': 'ICON_SEED', 'label': 'Temporary Seed', 'flow': TemporarySeedFlow, 'statusbar': {'title': 'TEMPORARY SEED'}, 'is_visible': lambda: not has_temporary_seed()}, - {'icon': 'ICON_PASSPHRASE', 'label': 'Clear Temporary Seed', 'flow': TemporarySeedFlow, + {'icon': 'ICON_SEED', 'label': 'Clear Temporary Seed', 'flow': TemporarySeedFlow, 'args': {'clear': True}, 'statusbar': {'title': 'TEMPORARY SEED'}, 'is_visible': lambda: has_temporary_seed() and has_permanent_seed()}, ] @@ -190,7 +190,7 @@ def key_item_menu(): {'icon': 'ICON_ONE_KEY', 'label': 'View Details', 'flow': ViewDerivedKeyDetailsFlow}, {'icon': 'ICON_INFO', 'label': 'Rename', 'flow': RenameDerivedKeyFlow, 'auto_card_header': False}, {'icon': 'ICON_SCAN_QR', 'label': 'Export', 'flow': ExportDerivedKeyFlow}, - {'icon': 'ICON_SEED', 'label': 'Use as Temporary Seed', 'flow': TemporarySeedFlow, + {'icon': 'ICON_SEED', 'label': 'Temporary Seed', 'flow': TemporarySeedFlow, 'is_visible': lambda: not has_temporary_seed()}, {'icon': 'ICON_ERASE', 'label': 'Hide Key', From ca6b8c1576984c482e916409937134bc7858abad Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Thu, 18 Apr 2024 16:52:43 -0400 Subject: [PATCH 074/126] SFT-1728: incorporated comments and ui improvements --- .../modules/flows/random_final_word_flow.py | 4 +++- .../Passport/modules/flows/restore_seed_flow.py | 14 +++++++++----- .../Passport/modules/flows/temporary_seed_flow.py | 2 +- .../boards/Passport/modules/pages/chooser_page.py | 5 +++-- ports/stm32/boards/Passport/modules/ui/ui.py | 2 +- 5 files changed, 17 insertions(+), 10 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/random_final_word_flow.py b/ports/stm32/boards/Passport/modules/flows/random_final_word_flow.py index 2fa6c5685..26b21ce03 100644 --- a/ports/stm32/boards/Passport/modules/flows/random_final_word_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/random_final_word_flow.py @@ -31,10 +31,12 @@ async def generate_word(self): import microns from utils import recolor from styles.colors import HIGHLIGHT_TEXT_HEX + from common import settings last_word = get_last_word(self.selected_words) styled_last_word = recolor(HIGHLIGHT_TEXT_HEX, last_word) - text = 'Your final word is\n{}.\n\nImport and save this seed?'.format(styled_last_word) + save_wording = ' and save' if not settings.temporary_mode else '' + text = 'Your final word is\n{}.\n\nImport{} this seed?'.format(styled_last_word, save_wording) result = await InfoPage(text=text, left_micron=microns.Retry).show() diff --git a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py index 24e26b606..13089dbec 100644 --- a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py @@ -11,6 +11,7 @@ from tasks import save_seed_task from public_constants import SEED_LENGTHS import lvgl as lv +from common import settings class RestoreSeedFlow(Flow): @@ -36,13 +37,14 @@ def __init__(self, refresh_cards_when_done=False, autobackup=True, full_backup=F async def choose_temporary(self): from pages import ChooserPage - text = 'Would you like to make a permanent seed, or a temporary seed?' + text = 'Import and save a seed, or import one temporarily?' options = [{'label': 'Permanent', 'value': True}, {'label': 'Temporary', 'value': False}] permanent = await ChooserPage(text=text, - card_header={'title': 'Seed Type'}, - options=options).show() + icon=lv.LARGE_ICON_QUESTION, + options=options, + icon_pad=-6).show() if permanent is None: self.set_result(False) return @@ -58,7 +60,7 @@ async def explain_temporary(self): result = await InfoPage( icon=lv.LARGE_ICON_SEED, - text='This temporary seed will not be saved, so be sure you have a backup, or create one during setup', + text='Temporary seeds are not saved to Passport. Ensure you have a robust backup in place.', left_micron=microns.Back, right_micron=microns.Forward).show() @@ -174,7 +176,9 @@ async def enter_seed_words(self): self.seed_words.append(last_word) if insufficient_randomness(self.seed_words): - text = "This seed contains 3 or more repeat words and may put funds at risk.\n\nSave seed and continue?" + save_wording = 'Save' if not settings.temporary_mode else 'Import' + text = "This seed contains 3 or more repeat words and may put funds at risk.\n\n{} seed and continue?" \ + .format(save_wording) result2 = await ErrorPage(text=text, left_micron=microns.Cancel).show() diff --git a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py index a211a76f2..eb7da215f 100644 --- a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py @@ -42,7 +42,7 @@ async def clear_seed(self): from tasks import delay_task from pages import SuccessPage - await spinner_task('Clearing Temporary Seed', delay_task, args=[1000, False]) + await spinner_task('Clearing temporary seed', delay_task, args=[1000, False]) await SuccessPage(text='Temporary seed cleared').show() settings.exit_temporary_mode() diff --git a/ports/stm32/boards/Passport/modules/pages/chooser_page.py b/ports/stm32/boards/Passport/modules/pages/chooser_page.py index 8be334cd5..175706223 100644 --- a/ports/stm32/boards/Passport/modules/pages/chooser_page.py +++ b/ports/stm32/boards/Passport/modules/pages/chooser_page.py @@ -23,7 +23,7 @@ def __init__( self, card_header=None, statusbar=None, options=[], initial_value=None, on_change=None, scroll_fix=False, icon=None, icon_color=CHOOSER_ICON, text=None, center=False, item_icon='ICON_SMALL_CHECKMARK', - left_micron=None, right_micron=None): + left_micron=None, right_micron=None, icon_pad=0): from views import ListItem, View import passport @@ -48,6 +48,7 @@ def __init__( self.text = text self.center = center self.item_icon = item_icon + self.icon_pad = icon_pad # If initial value is given, then select it, else select the first item if self.initial_value is not None: @@ -72,7 +73,7 @@ def __init__( with Stylize(self.icon_view) as default: if self.icon_color is not None: default.img_recolor(self.icon_color) - top = 20 if passport.IS_COLOR else 12 + top = (20 if passport.IS_COLOR else 12) + self.icon_pad default.pad(top=top, bottom=12) self.add_child(self.icon_view) diff --git a/ports/stm32/boards/Passport/modules/ui/ui.py b/ports/stm32/boards/Passport/modules/ui/ui.py index c097b6ced..e066b758b 100644 --- a/ports/stm32/boards/Passport/modules/ui/ui.py +++ b/ports/stm32/boards/Passport/modules/ui/ui.py @@ -255,7 +255,7 @@ def update_cards(self, # print('account[{}]={}'.format(account, i)) account_card = { - 'right_icon': 'ICON_BITCOIN', + 'right_icon': 'ICON_BITCOIN' if not common.settings.temporary_mode else 'ICON_SEED', 'header_color': LIGHT_GREY, 'header_fg_color': LIGHT_TEXT, 'statusbar': {'title': 'ACCOUNT', 'icon': 'ICON_FOLDER', 'fg_color': get_account_fg(account)}, From d08d6a6e68782b9ce1c5a197f9d05e35b259183a Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Mon, 20 May 2024 11:38:42 -0400 Subject: [PATCH 075/126] SFT-1728: improved temporary seed question --- .../boards/Passport/modules/flows/restore_seed_flow.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py index 13089dbec..c94d3472b 100644 --- a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py @@ -37,9 +37,9 @@ def __init__(self, refresh_cards_when_done=False, autobackup=True, full_backup=F async def choose_temporary(self): from pages import ChooserPage - text = 'Import and save a seed, or import one temporarily?' - options = [{'label': 'Permanent', 'value': True}, - {'label': 'Temporary', 'value': False}] + text = 'Save this seed, or import temporarily?' + options = [{'label': 'Save Seed', 'value': True}, + {'label': 'Temporary Seed', 'value': False}] permanent = await ChooserPage(text=text, icon=lv.LARGE_ICON_QUESTION, From 166c1096caa036a745ff64f3bf6def3eb5f9b591 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Fri, 21 Jun 2024 16:51:40 -0400 Subject: [PATCH 076/126] SFT-1728: removed spinner when clearing temporary seed --- .../boards/Passport/modules/flows/temporary_seed_flow.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py index eb7da215f..302792e96 100644 --- a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py @@ -42,9 +42,9 @@ async def clear_seed(self): from tasks import delay_task from pages import SuccessPage - await spinner_task('Clearing temporary seed', delay_task, args=[1000, False]) - await SuccessPage(text='Temporary seed cleared').show() settings.exit_temporary_mode() + # await spinner_task('Clearing temporary seed', delay_task, args=[1000, False]) + await SuccessPage(text='Temporary seed cleared').show() # TODO: ui is still buggy when clearing a seed self.set_result(True) From 41a577568feffbe6bc4ffa71fa8a3fb8965dca9e Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Thu, 4 Jul 2024 16:36:31 -0400 Subject: [PATCH 077/126] SFT-1728: removed old TODOs --- .../boards/Passport/modules/flows/export_derived_key_flow.py | 1 - .../stm32/boards/Passport/modules/flows/temporary_seed_flow.py | 3 --- 2 files changed, 4 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/export_derived_key_flow.py b/ports/stm32/boards/Passport/modules/flows/export_derived_key_flow.py index 64f6b431d..a47ede1fc 100644 --- a/ports/stm32/boards/Passport/modules/flows/export_derived_key_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/export_derived_key_flow.py @@ -23,7 +23,6 @@ async def generate_key(self): from flows import ViewSeedWordsFlow from public_constants import DIR_KEY_MNGR - # TODO: make this process a util function self.key_type = get_key_type_from_tn(self.key['tn']) if not self.key_type: diff --git a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py index 302792e96..57ff712c5 100644 --- a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py @@ -46,7 +46,6 @@ async def clear_seed(self): # await spinner_task('Clearing temporary seed', delay_task, args=[1000, False]) await SuccessPage(text='Temporary seed cleared').show() - # TODO: ui is still buggy when clearing a seed self.set_result(True) ui.full_cards_refresh() await self.wait_to_die() @@ -58,8 +57,6 @@ async def use_child_seed(self): from pages import ErrorPage, SuccessPage, InfoPage import microns - # TODO: add page explaining child seed as temporary seed, give user change to back out - result = await InfoPage('{} will be used as a temporary seed'.format(self.key['name']), left_micron=microns.Back).show() From f8e9a7da4e8100f0c6506f97330070cb7dde8b96 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Mon, 15 Jul 2024 13:09:05 -0400 Subject: [PATCH 078/126] SFT-1728: first pass of hourglass icons, removed prints --- .../Passport/images/color/ICON_HOURGLASS.c | 54 ++++++++++++++++++ .../ICON_HOURGLASS__CF_INDEXED_2_BIT.png | Bin 0 -> 1365 bytes ports/stm32/boards/Passport/images/images.h | 3 + .../Passport/images/mono/ICON_HOURGLASS.c | 51 +++++++++++++++++ .../images/mono/ICON_HOURGLASS_BACKGROUND.c | 51 +++++++++++++++++ ...HOURGLASS_BACKGROUND__CF_INDEXED_1_BIT.png | Bin 0 -> 3998 bytes .../mono/ICON_HOURGLASS__CF_INDEXED_1_BIT.png | Bin 0 -> 5944 bytes .../modules/flows/temporary_seed_flow.py | 5 -- ports/stm32/boards/Passport/modules/menus.py | 6 +- 9 files changed, 162 insertions(+), 8 deletions(-) create mode 100644 ports/stm32/boards/Passport/images/color/ICON_HOURGLASS.c create mode 100644 ports/stm32/boards/Passport/images/color/ICON_HOURGLASS__CF_INDEXED_2_BIT.png create mode 100644 ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS.c create mode 100644 ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c create mode 100644 ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND__CF_INDEXED_1_BIT.png create mode 100644 ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS__CF_INDEXED_1_BIT.png diff --git a/ports/stm32/boards/Passport/images/color/ICON_HOURGLASS.c b/ports/stm32/boards/Passport/images/color/ICON_HOURGLASS.c new file mode 100644 index 000000000..536133f08 --- /dev/null +++ b/ports/stm32/boards/Passport/images/color/ICON_HOURGLASS.c @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: © 2024 Foundation Devices, Inc. +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif +#ifndef LV_ATTRIBUTE_IMG_ICON_HOURGLASS +#define LV_ATTRIBUTE_IMG_ICON_HOURGLASS +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_ICON_HOURGLASS uint8_t ICON_HOURGLASS_map[] = { + 0x00, 0x00, 0x00, 0x00, /*Color of index 0*/ + 0xff, 0xff, 0xff, 0xff, /*Color of index 1*/ + 0x00, 0x00, 0x00, 0x00, /*Color of index 2*/ + 0x00, 0x00, 0x00, 0x00, /*Color of index 3*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x55, 0x55, 0x55, 0x50, + 0x01, 0x01, 0x55, 0x40, 0x40, + 0x01, 0x00, 0x14, 0x00, 0x40, + 0x00, 0x40, 0x00, 0x01, 0x00, + 0x00, 0x10, 0x14, 0x04, 0x00, + 0x00, 0x05, 0x14, 0x10, 0x00, + 0x00, 0x01, 0x01, 0x40, 0x00, + 0x00, 0x00, 0x41, 0x00, 0x00, + 0x00, 0x00, 0x41, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x40, 0x00, + 0x00, 0x04, 0x00, 0x10, 0x00, + 0x00, 0x10, 0x14, 0x05, 0x00, + 0x01, 0x40, 0x55, 0x01, 0x00, + 0x01, 0x01, 0x55, 0x40, 0x40, + 0x01, 0x05, 0x55, 0x50, 0x40, + 0x05, 0x55, 0x55, 0x55, 0x50, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t ICON_HOURGLASS = { + .header.cf = LV_IMG_CF_INDEXED_2BIT, + .header.always_zero = 0, + .header.reserved = 0, + .header.w = 20, + .header.h = 20, + .data_size = 116, + .data = ICON_HOURGLASS_map, +}; diff --git a/ports/stm32/boards/Passport/images/color/ICON_HOURGLASS__CF_INDEXED_2_BIT.png b/ports/stm32/boards/Passport/images/color/ICON_HOURGLASS__CF_INDEXED_2_BIT.png new file mode 100644 index 0000000000000000000000000000000000000000..67d9bc759ecb7efd85497c7d675e9d34561a01f3 GIT binary patch literal 1365 zcmV-b1*-aqP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+U=KHlH4E+MgLhvmVhLL#Bwm0s_Y=k&jmlGduH;g zrM`$U1_a3H!INBYeR{C38q>o=eUVS1PG+#lYinmDN&At34jlIY-a>a8EE) zf?keCtEYX1Uf*80)}ZZKAN-tQKOLPDmqVE=GY0)JNa*>{Ux9?OAA?-n9z)&u=-U;s zzujWX_3dOnhet&+WT`8xgn1W|xLQ9Vka;Jm=nXB2!j2$SL^!O8>ywa>r_a0k7#|b# zl;mp^-e>NJ8-Y8aTeGq`eRp6 zuzor~imaQ#58HLLMD}Dk8@|;SB^xl{BD^^`Rxq5bU z_u{p15uAZqaWr_6fB3`b zN<}Ii>>#3$p*mR*6>-!m6rn<>6Q(9nI5-4Gifa9=`f`NX zFOS&(000J1OjJeSf>r +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif +#ifndef LV_ATTRIBUTE_IMG_ICON_HOURGLASS +#define LV_ATTRIBUTE_IMG_ICON_HOURGLASS +#endif +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_ICON_HOURGLASS uint8_t ICON_HOURGLASS_map[] = { + 0x00, 0x00, 0x00, 0x00, /*Color of index 0*/ + 0xff, 0xff, 0xff, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + 0x3f, 0xff, 0x80, + 0x17, 0xfd, 0x00, + 0x13, 0xf9, 0x00, + 0x09, 0xf2, 0x00, + 0x04, 0x04, 0x00, + 0x02, 0x48, 0x00, + 0x01, 0x10, 0x00, + 0x00, 0xa0, 0x00, + 0x00, 0xa0, 0x00, + 0x01, 0x10, 0x00, + 0x02, 0x48, 0x00, + 0x04, 0xe4, 0x00, + 0x09, 0xf2, 0x00, + 0x13, 0xf9, 0x00, + 0x17, 0xfd, 0x00, + 0x3f, 0xff, 0x80, + 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t ICON_HOURGLASS = { + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .header.always_zero = 0, + .header.reserved = 0, + .header.w = 20, + .header.h = 20, + .data_size = 68, + .data = ICON_HOURGLASS_map, +}; diff --git a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c new file mode 100644 index 000000000..d6bf6ecb9 --- /dev/null +++ b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c @@ -0,0 +1,51 @@ +// SPDX-FileCopyrightText: © 2024 Foundation Devices, Inc. +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif +#ifndef LV_ATTRIBUTE_IMG_ICON_HOURGLASS_BACKGROUND +#define LV_ATTRIBUTE_IMG_ICON_HOURGLASS_BACKGROUND +#endif +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_ICON_HOURGLASS_BACKGROUND uint8_t ICON_HOURGLASS_BACKGROUND_map[] = { + 0x00, 0x00, 0x00, 0x00, /*Color of index 0*/ + 0xff, 0xff, 0xff, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, + 0x3f, 0xff, 0x80, + 0x7f, 0xff, 0xc0, + 0x7f, 0xff, 0xc0, + 0x3f, 0xff, 0x80, + 0x1f, 0xff, 0x00, + 0x0f, 0xfe, 0x00, + 0x07, 0xfc, 0x00, + 0x03, 0xf8, 0x00, + 0x01, 0xf0, 0x00, + 0x01, 0xf0, 0x00, + 0x03, 0xf8, 0x00, + 0x07, 0xfc, 0x00, + 0x0f, 0xfe, 0x00, + 0x1f, 0xff, 0x00, + 0x3f, 0xff, 0x80, + 0x7f, 0xff, 0xc0, + 0x7f, 0xff, 0xc0, + 0x3f, 0xff, 0x80, + 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t ICON_HOURGLASS_BACKGROUND = { + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .header.always_zero = 0, + .header.reserved = 0, + .header.w = 20, + .header.h = 20, + .data_size = 68, + .data = ICON_HOURGLASS_BACKGROUND_map, +}; diff --git a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND__CF_INDEXED_1_BIT.png b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND__CF_INDEXED_1_BIT.png new file mode 100644 index 0000000000000000000000000000000000000000..e501dd4f0a5c02e3384ec3d86a1ce932641671eb GIT binary patch literal 3998 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 Ef{xjiIsgCw literal 0 HcmV?d00001 diff --git a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS__CF_INDEXED_1_BIT.png b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS__CF_INDEXED_1_BIT.png new file mode 100644 index 0000000000000000000000000000000000000000..845ca7f5b3c0f4a821b24bbb6274e9fd9732f400 GIT binary patch literal 5944 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 aMgjoGoe*gOApijY0000 Date: Tue, 16 Jul 2024 15:03:55 -0400 Subject: [PATCH 079/126] SFT-1728: prevented erasing from temporary mode, fixed normal erasing bug --- ports/stm32/boards/Passport/modules/ext_settings.py | 6 +++--- ports/stm32/boards/Passport/modules/menus.py | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/ext_settings.py b/ports/stm32/boards/Passport/modules/ext_settings.py index 9e4fcc942..eae106440 100644 --- a/ports/stm32/boards/Passport/modules/ext_settings.py +++ b/ports/stm32/boards/Passport/modules/ext_settings.py @@ -297,10 +297,10 @@ def remove_regex(self, pattern): import re pattern = re.compile(pattern) matches = [] - if self.temporary_mode or kn in DEVICE_SETTINGS: - matches = [k for k in self.current if pattern.search(k)] - else: + if self.temporary_mode: matches = [k for k in self.temporary_settings if pattern.search(k)] + else: + matches = [k for k in self.current if pattern.search(k)] for k in matches: self.remove(k) diff --git a/ports/stm32/boards/Passport/modules/menus.py b/ports/stm32/boards/Passport/modules/menus.py index e6c49f357..411a98c76 100644 --- a/ports/stm32/boards/Passport/modules/menus.py +++ b/ports/stm32/boards/Passport/modules/menus.py @@ -391,6 +391,7 @@ def developer_pubkey_menu(): def advanced_menu(): from flows import ViewSeedWordsFlow, ErasePassportFlow, ScvFlow, ShowSecurityWordsSettingFlow + from utils import has_temporary_seed return [ {'icon': 'ICON_SETTINGS', 'label': 'Security Words', 'flow': ShowSecurityWordsSettingFlow}, @@ -400,7 +401,8 @@ def advanced_menu(): {'icon': 'ICON_ONE_KEY', 'label': 'Developer Pubkey', 'submenu': developer_pubkey_menu, 'statusbar': {'title': 'DEV. PUBKEY'}}, {'icon': 'ICON_MICROSD', 'label': 'microSD', 'submenu': microsd_menu}, - {'icon': 'ICON_ERASE', 'label': 'Erase Passport', 'flow': ErasePassportFlow}, + {'icon': 'ICON_ERASE', 'label': 'Erase Passport', 'flow': ErasePassportFlow, + 'is_visible': lambda: not has_temporary_seed()}, {'icon': 'ICON_SHIELD', 'label': 'Security Check', 'flow': ScvFlow, 'args': {'envoy': False, 'ask_to_skip': False}}, ] From 35102f0d9cc67b36d3afb96da9ba2fc37dc04c20 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Tue, 16 Jul 2024 15:31:33 -0400 Subject: [PATCH 080/126] SFT-1728: hid restore backup in temporary mode, fixed backup code for temporary seeds --- ports/stm32/boards/Passport/modules/menus.py | 9 ++++----- .../Passport/modules/tasks/get_backup_code_task.py | 8 ++++++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/menus.py b/ports/stm32/boards/Passport/modules/menus.py index 411a98c76..6883794e6 100644 --- a/ports/stm32/boards/Passport/modules/menus.py +++ b/ports/stm32/boards/Passport/modules/menus.py @@ -4,7 +4,7 @@ # menus.py - Menu configuration import lvgl as lv -from utils import has_seed +from utils import has_seed, has_temporary_seed # from pages import ColorPickerPage ######################################################################################## @@ -131,7 +131,7 @@ def postmix_menu(): def plus_menu(): - from utils import is_passphrase_active, has_temporary_seed, has_permanent_seed + from utils import is_passphrase_active, has_permanent_seed from flows import NewAccountFlow, ApplyPassphraseFlow, TemporarySeedFlow return [ @@ -171,7 +171,7 @@ def backup_menu(): return [ {'icon': 'ICON_BACKUP', 'label': 'Backup Now', 'flow': BackupFlow, 'is_visible': has_seed}, {'icon': 'ICON_RETRY', 'label': 'Restore', 'flow': RestoreBackupFlow, - 'args': {'refresh_cards_when_done': True}}, + 'args': {'refresh_cards_when_done': True}, 'is_visible': lambda: not has_temporary_seed()}, {'icon': 'ICON_CIRCLE_CHECK', 'label': 'Verify Backup', 'flow': VerifyBackupFlow}, {'icon': 'ICON_PIN', 'label': 'View Backup Code', 'flow': ViewBackupCodeFlow, 'statusbar': {'title': 'BACKUP', 'icon': 'ICON_PIN'}, 'is_visible': has_seed} @@ -179,7 +179,7 @@ def backup_menu(): def key_item_menu(): - from utils import toggle_key_hidden, is_key_hidden, has_temporary_seed + from utils import toggle_key_hidden, is_key_hidden from flows import ( ViewDerivedKeyDetailsFlow, @@ -391,7 +391,6 @@ def developer_pubkey_menu(): def advanced_menu(): from flows import ViewSeedWordsFlow, ErasePassportFlow, ScvFlow, ShowSecurityWordsSettingFlow - from utils import has_temporary_seed return [ {'icon': 'ICON_SETTINGS', 'label': 'Security Words', 'flow': ShowSecurityWordsSettingFlow}, diff --git a/ports/stm32/boards/Passport/modules/tasks/get_backup_code_task.py b/ports/stm32/boards/Passport/modules/tasks/get_backup_code_task.py index 7c584787d..17c350145 100644 --- a/ports/stm32/boards/Passport/modules/tasks/get_backup_code_task.py +++ b/ports/stm32/boards/Passport/modules/tasks/get_backup_code_task.py @@ -5,6 +5,7 @@ import trezorcrypto from micropython import const +from utils import has_temporary_seed # we make passwords with this number of words _NUM_DECIMAL_DIGITS_IN_BACKUP_CODE = const(20) @@ -16,11 +17,14 @@ def get_backup_code(): - from common import system, pa + from common import system, pa, settings device_hash = bytearray(32) system.get_device_hash(device_hash) - secret = pa.fetch() + if has_temporary_seed(): + secret = settings.get('temporary_seed') + else: + secret = pa.fetch() # print('secret: {}'.format(bytes_to_hex_str(secret))) hash = trezorcrypto.sha256() From b67eb9f56002ea25a125cf54789fe1332049baec Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Tue, 16 Jul 2024 16:07:38 -0400 Subject: [PATCH 081/126] SFT-1728: prevented saving nostr keys as temporary seed --- ports/stm32/boards/Passport/modules/menus.py | 23 ++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/menus.py b/ports/stm32/boards/Passport/modules/menus.py index 6883794e6..eaf3e1cfd 100644 --- a/ports/stm32/boards/Passport/modules/menus.py +++ b/ports/stm32/boards/Passport/modules/menus.py @@ -178,7 +178,7 @@ def backup_menu(): ] -def key_item_menu(): +def seed_item_menu(): from utils import toggle_key_hidden, is_key_hidden from flows import ( @@ -200,6 +200,25 @@ def key_item_menu(): ] +def key_item_menu(): + from utils import toggle_key_hidden, is_key_hidden + + from flows import ( + ViewDerivedKeyDetailsFlow, + RenameDerivedKeyFlow, + ExportDerivedKeyFlow) + return [ + {'icon': 'ICON_ONE_KEY', 'label': 'View Details', 'flow': ViewDerivedKeyDetailsFlow}, + {'icon': 'ICON_INFO', 'label': 'Rename', 'flow': RenameDerivedKeyFlow, 'auto_card_header': False}, + {'icon': 'ICON_SCAN_QR', 'label': 'Export', 'flow': ExportDerivedKeyFlow}, + {'icon': 'ICON_ERASE', + 'label': 'Hide Key', + 'action': lambda item, context: toggle_key_hidden(item, context), + 'is_toggle': True, + 'value': lambda context: is_key_hidden(context)}, + ] + + def new_key_menu(): from flows import NewDerivedKeyFlow from derived_key import key_types @@ -260,7 +279,7 @@ def key_manager_menu(): result.append({'icon': key_type['icon'], 'label': title, - 'submenu': key_item_menu, + 'submenu': seed_item_menu if key_type['words'] else key_item_menu, 'card_header': {'title': title, 'right_icon': key_type['icon']}, 'statusbar': {'title': 'KEY MANAGER'}, From 51f906b0150ae45076e8169c72730411be2f3b22 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Tue, 16 Jul 2024 17:15:19 -0400 Subject: [PATCH 082/126] SFT-1728: fixing wording and capitalization --- .../Passport/modules/flows/export_derived_key_flow.py | 2 +- .../boards/Passport/modules/flows/temporary_seed_flow.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/export_derived_key_flow.py b/ports/stm32/boards/Passport/modules/flows/export_derived_key_flow.py index a47ede1fc..af3d13042 100644 --- a/ports/stm32/boards/Passport/modules/flows/export_derived_key_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/export_derived_key_flow.py @@ -30,7 +30,7 @@ async def generate_key(self): self.set_result(False) return - (vals, error) = await spinner_task(text='Generating key', + (vals, error) = await spinner_task(text='Retrieving Key', task=self.key_type['task'], args=[self.key['index']]) self.pk = vals['priv'] diff --git a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py index d0359f95a..3ed73f720 100644 --- a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py @@ -40,7 +40,7 @@ async def clear_seed(self): settings.exit_temporary_mode() # await spinner_task('Clearing temporary seed', delay_task, args=[1000, False]) - await SuccessPage(text='Temporary seed cleared').show() + await SuccessPage(text='Temporary Seed Cleared').show() self.set_result(True) ui.full_cards_refresh() @@ -67,7 +67,7 @@ async def use_child_seed(self): self.set_result(False) return - (vals, error) = await spinner_task(text='Generating key', + (vals, error) = await spinner_task(text='Retrieving Key', task=self.key_type['task'], args=[self.key['index']]) pk = vals['priv'] @@ -77,13 +77,13 @@ async def use_child_seed(self): return settings.enter_temporary_mode() - (error,) = await spinner_task('Applying seed', save_seed_task, args=[pk]) + (error,) = await spinner_task('Applying Seed', save_seed_task, args=[pk]) if error is not None: self.set_result(None) return - await SuccessPage(text='Temporary seed applied.').show() + await SuccessPage(text='Temporary Seed Applied.').show() self.set_result(True) ui.full_cards_refresh() From c84445b6c45cf4108fefda7c95d338921d760da9 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Wed, 17 Jul 2024 14:04:04 -0400 Subject: [PATCH 083/126] SFT-1728: avoided scrollbar in key item menu --- ports/stm32/boards/Passport/modules/flows/menu_flow.py | 4 +++- ports/stm32/boards/Passport/modules/menus.py | 3 ++- ports/stm32/boards/Passport/modules/pages/menu_page.py | 7 +++++-- ports/stm32/boards/Passport/modules/views/menu_item.py | 9 +++++++-- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/menu_flow.py b/ports/stm32/boards/Passport/modules/flows/menu_flow.py index 0593aaa4e..ba08d8be4 100644 --- a/ports/stm32/boards/Passport/modules/flows/menu_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/menu_flow.py @@ -14,7 +14,7 @@ class MenuFlow(Flow): latest_menu = None def __init__(self, menu, initial_selected_index=0, is_top_level=None, context=None, - card_header=None, statusbar=None, dynamic=None): + card_header=None, statusbar=None, dynamic=None, item_vertical_padding=None): self.menu = menu super().__init__(initial_state=self.show_menu, name='MenuFlow') @@ -26,6 +26,7 @@ def __init__(self, menu, initial_selected_index=0, is_top_level=None, context=No self.statusbar = statusbar self.dynamic = dynamic MenuFlow.latest_menu = self + self.item_vertical_padding = item_vertical_padding async def show_menu(self): from common import ui @@ -52,6 +53,7 @@ async def show_menu(self): right_micron=microns.Checkmark, is_top_level=self.is_top_level, context=self.context, + item_vertical_padding=self.item_vertical_padding ).show() if result is None: if ui.is_top_level(): diff --git a/ports/stm32/boards/Passport/modules/menus.py b/ports/stm32/boards/Passport/modules/menus.py index eaf3e1cfd..9f9199dd4 100644 --- a/ports/stm32/boards/Passport/modules/menus.py +++ b/ports/stm32/boards/Passport/modules/menus.py @@ -283,7 +283,8 @@ def key_manager_menu(): 'card_header': {'title': title, 'right_icon': key_type['icon']}, 'statusbar': {'title': 'KEY MANAGER'}, - 'args': {'context': key, 'dynamic': key_type.get('menu', None)}, + 'args': {'context': key, 'dynamic': key_type.get('menu', None), + 'item_vertical_padding': 9}, 'auto_card_header': False}) result.append({'icon': 'ICON_ONE_KEY', 'label': 'New Key', 'submenu': new_key_menu}) diff --git a/ports/stm32/boards/Passport/modules/pages/menu_page.py b/ports/stm32/boards/Passport/modules/pages/menu_page.py index 767996a02..749040c17 100644 --- a/ports/stm32/boards/Passport/modules/pages/menu_page.py +++ b/ports/stm32/boards/Passport/modules/pages/menu_page.py @@ -23,7 +23,8 @@ def __init__(self, card_header=None, statusbar=None, left_micron=None, - right_micron=None): + right_micron=None, + item_vertical_padding=None): super().__init__(card_header=card_header, statusbar=statusbar, left_micron=left_micron, @@ -35,6 +36,7 @@ def __init__(self, self.focus_idx = focus_idx self.is_top_level = is_top_level self.context = context + self.item_vertical_padding = item_vertical_padding with Stylize(self) as default: default.flex_fill() @@ -67,7 +69,8 @@ def mount(self, lvgl_parent): is_toggle=item_desc.get('is_toggle'), value=item_desc.get('value'), desc=item_desc, - context=self.context) + context=self.context, + vertical_padding=self.item_vertical_padding) is_visible = item_desc.get('is_visible') if is_visible is None or (callable(is_visible) and is_visible()): self.visible_items.append(item) diff --git a/ports/stm32/boards/Passport/modules/views/menu_item.py b/ports/stm32/boards/Passport/modules/views/menu_item.py index cb75ccd5f..64e447585 100644 --- a/ports/stm32/boards/Passport/modules/views/menu_item.py +++ b/ports/stm32/boards/Passport/modules/views/menu_item.py @@ -14,7 +14,8 @@ class MenuItem(View): - def __init__(self, icon='', label='', is_toggle=False, value=False, desc=None, context=None): + def __init__(self, icon='', label='', is_toggle=False, value=False, desc=None, context=None, + vertical_padding=None): from views import Switch super().__init__(flex_flow=lv.FLEX_FLOW.ROW) @@ -24,13 +25,17 @@ def __init__(self, icon='', label='', is_toggle=False, value=False, desc=None, c self.value = value self.desc = desc self.context = context + self.vertical_padding = (vertical_padding if vertical_padding is not None else 10) # Default style with Stylize(self) as default: default.bg_transparent() default.text_color(NORMAL_TEXT) right_pad = 8 if self.is_toggle else 0 - default.pad(top=10, right=right_pad, bottom=10, left=10) + default.pad(top=self.vertical_padding, + right=right_pad, + bottom=self.vertical_padding, + left=10) default.flex_align(track=lv.FLEX_ALIGN.CENTER, cross=lv.FLEX_ALIGN.CENTER) default.img_recolor(NORMAL_TEXT) From f6f67e92c56394743d6736e01a971808606f7d8e Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Wed, 17 Jul 2024 16:22:45 -0400 Subject: [PATCH 084/126] SFT-1728: another iteration of hourglass icons --- .../Passport/images/color/ICON_HOURGLASS.c | 31 ++++++------- .../ICON_HOURGLASS__CF_INDEXED_2_BIT.png | Bin 1365 -> 4736 bytes .../Passport/images/mono/ICON_HOURGLASS.c | 39 +++++++++-------- .../images/mono/ICON_HOURGLASS_BACKGROUND.c | 41 ++++++++++-------- ...HOURGLASS_BACKGROUND__CF_INDEXED_1_BIT.png | Bin 3998 -> 197 bytes .../mono/ICON_HOURGLASS__CF_INDEXED_1_BIT.png | Bin 5944 -> 220 bytes 6 files changed, 58 insertions(+), 53 deletions(-) diff --git a/ports/stm32/boards/Passport/images/color/ICON_HOURGLASS.c b/ports/stm32/boards/Passport/images/color/ICON_HOURGLASS.c index 536133f08..c5d1722b5 100644 --- a/ports/stm32/boards/Passport/images/color/ICON_HOURGLASS.c +++ b/ports/stm32/boards/Passport/images/color/ICON_HOURGLASS.c @@ -11,33 +11,34 @@ #ifndef LV_ATTRIBUTE_MEM_ALIGN #define LV_ATTRIBUTE_MEM_ALIGN #endif + #ifndef LV_ATTRIBUTE_IMG_ICON_HOURGLASS #define LV_ATTRIBUTE_IMG_ICON_HOURGLASS #endif -const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_ICON_HOURGLASS uint8_t ICON_HOURGLASS_map[] = { +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_ICON_HOURGLASS uint8_t ICON_HOURGLASS_map[] = { 0x00, 0x00, 0x00, 0x00, /*Color of index 0*/ - 0xff, 0xff, 0xff, 0xff, /*Color of index 1*/ + 0xfe, 0xfe, 0xfe, 0xff, /*Color of index 1*/ 0x00, 0x00, 0x00, 0x00, /*Color of index 2*/ 0x00, 0x00, 0x00, 0x00, /*Color of index 3*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x55, 0x55, 0x55, 0x50, - 0x01, 0x01, 0x55, 0x40, 0x40, - 0x01, 0x00, 0x14, 0x00, 0x40, - 0x00, 0x40, 0x00, 0x01, 0x00, - 0x00, 0x10, 0x14, 0x04, 0x00, - 0x00, 0x05, 0x14, 0x10, 0x00, - 0x00, 0x01, 0x01, 0x40, 0x00, - 0x00, 0x00, 0x41, 0x00, 0x00, - 0x00, 0x00, 0x41, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x40, 0x00, - 0x00, 0x04, 0x00, 0x10, 0x00, - 0x00, 0x10, 0x14, 0x05, 0x00, - 0x01, 0x40, 0x55, 0x01, 0x00, - 0x01, 0x01, 0x55, 0x40, 0x40, + 0x01, 0x15, 0x55, 0x54, 0x40, + 0x01, 0x05, 0x55, 0x50, 0x40, + 0x01, 0x41, 0x55, 0x41, 0x40, + 0x00, 0x50, 0x00, 0x05, 0x00, + 0x00, 0x14, 0x14, 0x14, 0x00, + 0x00, 0x05, 0x00, 0x50, 0x00, + 0x00, 0x01, 0x41, 0x40, 0x00, + 0x00, 0x01, 0x41, 0x40, 0x00, + 0x00, 0x05, 0x00, 0x50, 0x00, + 0x00, 0x14, 0x14, 0x14, 0x00, + 0x00, 0x50, 0x55, 0x05, 0x00, + 0x01, 0x41, 0x55, 0x41, 0x40, 0x01, 0x05, 0x55, 0x50, 0x40, + 0x01, 0x15, 0x55, 0x54, 0x40, 0x05, 0x55, 0x55, 0x55, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/ports/stm32/boards/Passport/images/color/ICON_HOURGLASS__CF_INDEXED_2_BIT.png b/ports/stm32/boards/Passport/images/color/ICON_HOURGLASS__CF_INDEXED_2_BIT.png index 67d9bc759ecb7efd85497c7d675e9d34561a01f3..763d3acd459739d388aeeec1d604635517eafb2d 100644 GIT binary patch literal 4736 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 Hn3VY^%j5ka delta 1265 zcmVrM`$U1_a3H!INBYeR{C38q>o=eUVS1PG+#lYinmDN&At34jlIY-a>a8EE)f?keC ztEYX1Uf*80)}ZZKAN-tQKOLPDmqVE=GY0)JNa*>{Ux9?OAA?-n9z%cK_~_devA^A7 z%k}MKK8Hs|GGwVMtb}F3E`DSlX3?uht#!sK%GG09_eA7ekhuBMEP=Ch(= zxUw_ff#HUf%X%$C47Yz`Y)cBnV~q`PdTeonG%CcPGEk7zsnVc*^^F=rOuVz9XKpM@ z=VDyA$>fx#&|s4yT^uYM?*ZtN@Er@Sb<0|>kz?gfFlB->!YehH zemX#ku(l!?lzpO@u?HwqG-e}09vy%xBF8@A4FpJ)JA&jSg7e7Od9ICjjFv4cXP#=D zO#q>iv9}zADqy9gs2@v?8mcOqR5h!q2Q6B&EX>4Tx0C=2zkv&MmKp2MK zrb$B!1+& z;_(~jqRRr$jGCGBJaLp*EOxNc!K`Fz#8bpERnsY7$a<`D-r}rPYOHln{=#rxUs>il ztsx|_h$To6p`eB`DzFi!T_?ptn$8nG{z2C-kxL<08H^kYs6vD6`oaI;cWQ(9nI5-4Gifa9= z`f`NXFOS&(000J1OjJeSf>r +// SPDX-FileCopyrightText: © 2022 Foundation Devices, Inc. // SPDX-License-Identifier: GPL-3.0-or-later // @@ -7,35 +7,36 @@ #else #include "lvgl/lvgl.h" #endif - #ifndef LV_ATTRIBUTE_MEM_ALIGN #define LV_ATTRIBUTE_MEM_ALIGN #endif + #ifndef LV_ATTRIBUTE_IMG_ICON_HOURGLASS #define LV_ATTRIBUTE_IMG_ICON_HOURGLASS #endif + const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_ICON_HOURGLASS uint8_t ICON_HOURGLASS_map[] = { 0x00, 0x00, 0x00, 0x00, /*Color of index 0*/ - 0xff, 0xff, 0xff, 0xff, /*Color of index 1*/ + 0xfe, 0xfe, 0xfe, 0xff, /*Color of index 1*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0xff, 0x80, - 0x17, 0xfd, 0x00, - 0x13, 0xf9, 0x00, - 0x09, 0xf2, 0x00, - 0x04, 0x04, 0x00, - 0x02, 0x48, 0x00, - 0x01, 0x10, 0x00, - 0x00, 0xa0, 0x00, - 0x00, 0xa0, 0x00, - 0x01, 0x10, 0x00, - 0x02, 0x48, 0x00, - 0x04, 0xe4, 0x00, - 0x09, 0xf2, 0x00, - 0x13, 0xf9, 0x00, - 0x17, 0xfd, 0x00, - 0x3f, 0xff, 0x80, + 0x3f, 0xff, 0xc0, + 0x17, 0xfe, 0x80, + 0x13, 0xfc, 0x80, + 0x19, 0xf9, 0x80, + 0x0c, 0x03, 0x00, + 0x06, 0x66, 0x00, + 0x03, 0x0c, 0x00, + 0x01, 0x98, 0x00, + 0x01, 0x98, 0x00, + 0x03, 0x0c, 0x00, + 0x06, 0x66, 0x00, + 0x0c, 0xf3, 0x00, + 0x19, 0xf9, 0x80, + 0x13, 0xfc, 0x80, + 0x17, 0xfe, 0x80, + 0x3f, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; diff --git a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c index d6bf6ecb9..cf159820f 100644 --- a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c +++ b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c @@ -8,35 +8,38 @@ #include "lvgl/lvgl.h" #endif + #ifndef LV_ATTRIBUTE_MEM_ALIGN #define LV_ATTRIBUTE_MEM_ALIGN #endif + #ifndef LV_ATTRIBUTE_IMG_ICON_HOURGLASS_BACKGROUND #define LV_ATTRIBUTE_IMG_ICON_HOURGLASS_BACKGROUND #endif + const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_ICON_HOURGLASS_BACKGROUND uint8_t ICON_HOURGLASS_BACKGROUND_map[] = { 0x00, 0x00, 0x00, 0x00, /*Color of index 0*/ - 0xff, 0xff, 0xff, 0xff, /*Color of index 1*/ + 0xfe, 0xfe, 0xfe, 0xff, /*Color of index 1*/ 0x00, 0x00, 0x00, - 0x3f, 0xff, 0x80, - 0x7f, 0xff, 0xc0, - 0x7f, 0xff, 0xc0, - 0x3f, 0xff, 0x80, - 0x1f, 0xff, 0x00, - 0x0f, 0xfe, 0x00, - 0x07, 0xfc, 0x00, - 0x03, 0xf8, 0x00, - 0x01, 0xf0, 0x00, - 0x01, 0xf0, 0x00, - 0x03, 0xf8, 0x00, - 0x07, 0xfc, 0x00, - 0x0f, 0xfe, 0x00, - 0x1f, 0xff, 0x00, - 0x3f, 0xff, 0x80, - 0x7f, 0xff, 0xc0, - 0x7f, 0xff, 0xc0, - 0x3f, 0xff, 0x80, + 0x3f, 0xff, 0xc0, + 0x7f, 0xff, 0xe0, + 0x7f, 0xff, 0xe0, + 0x3f, 0xff, 0xc0, + 0x3f, 0xff, 0xc0, + 0x1f, 0xff, 0x80, + 0x0f, 0xff, 0x00, + 0x07, 0xfe, 0x00, + 0x03, 0xfc, 0x00, + 0x03, 0xfc, 0x00, + 0x07, 0xfe, 0x00, + 0x0f, 0xff, 0x00, + 0x1f, 0xff, 0x80, + 0x3f, 0xff, 0xc0, + 0x3f, 0xff, 0xc0, + 0x7f, 0xff, 0xe0, + 0x7f, 0xff, 0xe0, + 0x3f, 0xff, 0xc0, 0x00, 0x00, 0x00, }; diff --git a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND__CF_INDEXED_1_BIT.png b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND__CF_INDEXED_1_BIT.png index e501dd4f0a5c02e3384ec3d86a1ce932641671eb..c3e7aaca68019863ea22190160b9de26f9ad2ec3 100644 GIT binary patch delta 181 zcmbOyf0S{8L_G%^0|P^Erz|g!;w(0r%1aer? z9eo`c7&i8E|4C#8@}oRm978G?pI-6iVo>044b1*u{m$5636Ix&ffDy#J~w%#f==mCjY#dG3h*xXKl$^fc+!!}|d;F>R c{k(g)tge|z%&dQPA7~AOr>mdKI;Vst0N*$|Y5)KL delta 3991 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 BN{#>k literal 5944 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 aMgjoGoe*gOApijY0000 Date: Wed, 17 Jul 2024 20:38:32 -0400 Subject: [PATCH 085/126] SFT-1728: improved mono background for hourglass icon --- .../images/mono/ICON_HOURGLASS_BACKGROUND.c | 45 +++++++++--------- ...HOURGLASS_BACKGROUND__CF_INDEXED_1_BIT.png | Bin 197 -> 198 bytes 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c index cf159820f..24af5db38 100644 --- a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c +++ b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c @@ -8,7 +8,6 @@ #include "lvgl/lvgl.h" #endif - #ifndef LV_ATTRIBUTE_MEM_ALIGN #define LV_ATTRIBUTE_MEM_ALIGN #endif @@ -22,24 +21,26 @@ const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_ICON_HOURGLASS_BACKGROUND uint8_t 0xfe, 0xfe, 0xfe, 0xff, /*Color of index 1*/ 0x00, 0x00, 0x00, - 0x3f, 0xff, 0xc0, - 0x7f, 0xff, 0xe0, - 0x7f, 0xff, 0xe0, - 0x3f, 0xff, 0xc0, - 0x3f, 0xff, 0xc0, - 0x1f, 0xff, 0x80, - 0x0f, 0xff, 0x00, - 0x07, 0xfe, 0x00, - 0x03, 0xfc, 0x00, - 0x03, 0xfc, 0x00, - 0x07, 0xfe, 0x00, - 0x0f, 0xff, 0x00, - 0x1f, 0xff, 0x80, - 0x3f, 0xff, 0xc0, - 0x3f, 0xff, 0xc0, - 0x7f, 0xff, 0xe0, - 0x7f, 0xff, 0xe0, - 0x3f, 0xff, 0xc0, + 0x00, 0x00, 0x00, + 0x1f, 0xff, 0xe0, + 0x3f, 0xff, 0xf0, + 0x3f, 0xff, 0xf0, + 0x1f, 0xff, 0xe0, + 0x1f, 0xff, 0xe0, + 0x0f, 0xff, 0xc0, + 0x07, 0xff, 0x80, + 0x03, 0xff, 0x00, + 0x01, 0xfe, 0x00, + 0x01, 0xfe, 0x00, + 0x03, 0xff, 0x00, + 0x07, 0xff, 0x80, + 0x0f, 0xff, 0xc0, + 0x1f, 0xff, 0xe0, + 0x1f, 0xff, 0xe0, + 0x3f, 0xff, 0xf0, + 0x3f, 0xff, 0xf0, + 0x1f, 0xff, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; @@ -47,8 +48,8 @@ const lv_img_dsc_t ICON_HOURGLASS_BACKGROUND = { .header.cf = LV_IMG_CF_INDEXED_1BIT, .header.always_zero = 0, .header.reserved = 0, - .header.w = 20, - .header.h = 20, - .data_size = 68, + .header.w = 22, + .header.h = 22, + .data_size = 74, .data = ICON_HOURGLASS_BACKGROUND_map, }; diff --git a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND__CF_INDEXED_1_BIT.png b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND__CF_INDEXED_1_BIT.png index c3e7aaca68019863ea22190160b9de26f9ad2ec3..e8483790652d744ba77c5982a7fe19acfcb11342 100644 GIT binary patch delta 154 zcmX@gc#Ki8Gr-TCmrII^fq{Y7)59eQNQ;3m2OE$)vL(lQqM~U*w5N+>NCo5DE8biT z3LLJ1+5fBG8FMT$@v3aPFxS-5!+uf5(#pW76pgKBH@enUs0D0T`(fse<#Vg%O=~s% z(be^V``kkJA8gYv%)9@vS0S3w(&j{I_t#JV-ixj0`Z(7_qH~Y*0-$vap00i_>zopr E0A4~nSO5S3 delta 153 zcmX@cc$87GGr-TCmrII^fq{Y7)59eQNQ;0l2OE&=?Ud!6sA!rW<>}%WQo;E2iZ>U7 z0*7m0_W$a4#s*7xyygp(xcBn8$tx9fiZ6BT3(9VY^I0_Wz`^Y@=85O6RQ Date: Wed, 17 Jul 2024 22:08:11 -0400 Subject: [PATCH 086/126] SFT-1728: manually edited hourglass background code to increase its visual buffer --- .../Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c index 24af5db38..332122dd4 100644 --- a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c +++ b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c @@ -22,24 +22,24 @@ const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_ICON_HOURGLASS_BACKGROUND uint8_t 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0xff, 0xe0, 0x3f, 0xff, 0xf0, 0x3f, 0xff, 0xf0, + 0x3f, 0xff, 0xf0, + 0x1f, 0xff, 0xe0, 0x1f, 0xff, 0xe0, 0x1f, 0xff, 0xe0, 0x0f, 0xff, 0xc0, 0x07, 0xff, 0x80, 0x03, 0xff, 0x00, - 0x01, 0xfe, 0x00, - 0x01, 0xfe, 0x00, 0x03, 0xff, 0x00, 0x07, 0xff, 0x80, 0x0f, 0xff, 0xc0, 0x1f, 0xff, 0xe0, 0x1f, 0xff, 0xe0, + 0x1f, 0xff, 0xe0, + 0x3f, 0xff, 0xf0, 0x3f, 0xff, 0xf0, 0x3f, 0xff, 0xf0, - 0x1f, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; From 8c769841f70ef4b7861acf1ba82af704992daf46 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Mon, 22 Jul 2024 00:44:12 -0400 Subject: [PATCH 087/126] SFT-1728: finalized hourglass mono background --- .../images/mono/ICON_HOURGLASS_BACKGROUND.c | 3 +-- ...N_HOURGLASS_BACKGROUND__CF_INDEXED_1_BIT.png | Bin 198 -> 192 bytes 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c index 332122dd4..d30d9339a 100644 --- a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c +++ b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND.c @@ -7,7 +7,6 @@ #else #include "lvgl/lvgl.h" #endif - #ifndef LV_ATTRIBUTE_MEM_ALIGN #define LV_ATTRIBUTE_MEM_ALIGN #endif @@ -18,7 +17,7 @@ const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_IMG_ICON_HOURGLASS_BACKGROUND uint8_t ICON_HOURGLASS_BACKGROUND_map[] = { 0x00, 0x00, 0x00, 0x00, /*Color of index 0*/ - 0xfe, 0xfe, 0xfe, 0xff, /*Color of index 1*/ + 0xff, 0xff, 0xff, 0xff, /*Color of index 1*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND__CF_INDEXED_1_BIT.png b/ports/stm32/boards/Passport/images/mono/ICON_HOURGLASS_BACKGROUND__CF_INDEXED_1_BIT.png index e8483790652d744ba77c5982a7fe19acfcb11342..dcffdda85ec154ca9d88ff7b1043e701f8f346eb 100644 GIT binary patch delta 113 zcmV-%0FM900l)!}R%%sAL_t(|0qvBr3IHGo1-1YG=jsK4gbi5+g11!54Hrrj4W5_U zp$BjWXCWut#4I1TIc5eBJO!}kkQr35MZQY6;uEXEXr*~k zT4X4-HrR@;cqUM=t?-$+r=5D9mW4I=ii+?H&Y?&62A9yD-{FlQTLC#d$aRbL^8W7@ ZuL|* Date: Mon, 22 Jul 2024 01:10:43 -0400 Subject: [PATCH 088/126] SFT-1728: ensured initial seeds can be temporary, removed print --- .../boards/Passport/modules/flows/restore_seed_flow.py | 9 ++++++--- .../Passport/modules/pages/predictive_text_input_page.py | 1 - 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py index c94d3472b..6f65e8400 100644 --- a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py @@ -49,10 +49,12 @@ async def choose_temporary(self): self.set_result(False) return - if permanent: - self.goto(self.choose_restore_method) - else: + self.temporary = not permanent + if self.temporary: + settings.enter_temporary_mode() self.goto(self.explain_temporary) + else: + self.goto(self.choose_restore_method) async def explain_temporary(self): from pages import InfoPage @@ -66,6 +68,7 @@ async def explain_temporary(self): if not result: if not self.back(): + settings.exit_temporary_mode() self.set_result(None) return diff --git a/ports/stm32/boards/Passport/modules/pages/predictive_text_input_page.py b/ports/stm32/boards/Passport/modules/pages/predictive_text_input_page.py index 84c1c4e67..c50dbb8a6 100644 --- a/ports/stm32/boards/Passport/modules/pages/predictive_text_input_page.py +++ b/ports/stm32/boards/Passport/modules/pages/predictive_text_input_page.py @@ -97,7 +97,6 @@ def update_predictions(self): # print('Lookup words for {}'.format(prefix)) set_list(self.prefixes, self.word_idx, prefix) self.predictions = get_words_matching_prefix(prefix, max=10, word_list=self.word_list) - print("len(predictions): {}".format(len(self.predictions))) elif self.word_idx == self.total_words - 1: self.predictions = [RANDOM_WORD_STRING] else: From 77bfe63ad570ac96b1406861f6308ef6ab854cb9 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Tue, 23 Jul 2024 13:00:10 -0400 Subject: [PATCH 089/126] SFT-1728: fixed import header and added cancel button to initial backup prompt --- .../boards/Passport/modules/flows/backup_flow.py | 5 +++-- .../Passport/modules/flows/restore_seed_flow.py | 16 +++++++++------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/backup_flow.py b/ports/stm32/boards/Passport/modules/flows/backup_flow.py index b038b65df..42cd03c54 100644 --- a/ports/stm32/boards/Passport/modules/flows/backup_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/backup_flow.py @@ -13,7 +13,7 @@ class BackupFlow(Flow): - def __init__(self, initial_backup=False): + def __init__(self, initial_backup=False, left_micron=microns.Back): from common import settings super().__init__(initial_state=self.show_intro, name='BackupFlow') self.backup_quiz_passed = settings.get('backup_quiz', False) @@ -21,6 +21,7 @@ def __init__(self, initial_backup=False): self.statusbar = {'title': 'BACKUP', 'icon': 'ICON_BACKUP'} self.initial_backup = initial_backup + self.first_left_micron = left_micron async def show_intro(self): from pages import InfoPage @@ -42,7 +43,7 @@ async def show_intro(self): result = await InfoPage( icon=lv.LARGE_ICON_BACKUP, text=msgs, - left_micron=microns.Back, + left_micron=self.first_left_micron, right_micron=microns.Forward).show() if result: diff --git a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py index 6f65e8400..6a84b9565 100644 --- a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py @@ -17,6 +17,12 @@ class RestoreSeedFlow(Flow): def __init__(self, refresh_cards_when_done=False, autobackup=True, full_backup=False, temporary=False): + initial_state = self.choose_temporary + if temporary: + initial_state = self.explain_temporary + + super().__init__(initial_state=initial_state, name='RestoreSeedFlow') + self.refresh_cards_when_done = refresh_cards_when_done self.seed_format = None self.seed_length = None @@ -25,14 +31,10 @@ def __init__(self, refresh_cards_when_done=False, autobackup=True, full_backup=F self.seed_words = [] self.full_backup = full_backup self.autobackup = autobackup - self.statusbar = {'title': 'IMPORT SEED', 'icon': 'ICON_SEED'} self.temporary = temporary - initial_state = self.choose_temporary - if self.temporary: - initial_state = self.explain_temporary - - super().__init__(initial_state=initial_state, name='RestoreSeedFlow') + # This must be after super().__init__, so it isn't set to null + self.statusbar = {'title': 'IMPORT SEED', 'icon': 'ICON_SEED'} async def choose_temporary(self): from pages import ChooserPage @@ -228,7 +230,7 @@ async def valid_seed(self): text = 'New seed imported and {}'.format('applied' if self.temporary else 'saved') await SuccessPage(text=text).show() if self.full_backup: - await BackupFlow(initial_backup=True).run() + await BackupFlow(initial_backup=True, left_micron=microns.Cancel).run() elif self.autobackup: await AutoBackupFlow(offer=True).run() From 81ef57d9a58a1113b9176f51189f45e65938d146 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Tue, 23 Jul 2024 13:41:11 -0400 Subject: [PATCH 090/126] SFT-1728: removed unnecessary changes, fixed some copy, used hourglass indicator for active temporary seeds --- .../Passport/modules/flows/temporary_seed_flow.py | 2 +- .../Passport/modules/tasks/restore_backup_task.py | 13 +++++-------- ports/stm32/boards/Passport/modules/ui/ui.py | 2 +- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py index 3ed73f720..643c6ca7d 100644 --- a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py @@ -83,7 +83,7 @@ async def use_child_seed(self): self.set_result(None) return - await SuccessPage(text='Temporary Seed Applied.').show() + await SuccessPage(text='Temporary Seed Applied').show() self.set_result(True) ui.full_cards_refresh() diff --git a/ports/stm32/boards/Passport/modules/tasks/restore_backup_task.py b/ports/stm32/boards/Passport/modules/tasks/restore_backup_task.py index 7cded7282..f2a6afc10 100644 --- a/ports/stm32/boards/Passport/modules/tasks/restore_backup_task.py +++ b/ports/stm32/boards/Passport/modules/tasks/restore_backup_task.py @@ -107,14 +107,11 @@ async def restore_backup_task(on_done, decryption_password, backup_file_path): await on_done(Error.CORRUPT_BACKUP_FILE) return - if settings.temporary_mode: - settings.set_volatile('temporary_seed', raw) - else: - # Set the secret into the Secure Element - pa.change(new_secret=raw) - - # Force the right chain and update XFP & XPUB - await pa.new_main_secret(raw, chain) + # Set the secret into the Secure Element + pa.change(new_secret=raw) + + # Force the right chain and update XFP & XPUB + await pa.new_main_secret(raw, chain) # Finally, restore the settings for idx, k in enumerate(vals): diff --git a/ports/stm32/boards/Passport/modules/ui/ui.py b/ports/stm32/boards/Passport/modules/ui/ui.py index e066b758b..ed566121f 100644 --- a/ports/stm32/boards/Passport/modules/ui/ui.py +++ b/ports/stm32/boards/Passport/modules/ui/ui.py @@ -255,7 +255,7 @@ def update_cards(self, # print('account[{}]={}'.format(account, i)) account_card = { - 'right_icon': 'ICON_BITCOIN' if not common.settings.temporary_mode else 'ICON_SEED', + 'right_icon': 'ICON_BITCOIN' if not common.settings.temporary_mode else 'ICON_HOURGLASS', 'header_color': LIGHT_GREY, 'header_fg_color': LIGHT_TEXT, 'statusbar': {'title': 'ACCOUNT', 'icon': 'ICON_FOLDER', 'fg_color': get_account_fg(account)}, From ed8702a09a6f83170a43ac43e2fb85ce11325331 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Fri, 26 Jul 2024 12:45:04 -0400 Subject: [PATCH 091/126] SFT-1728: fixed account index out of bounds when switching keys --- .../Passport/modules/flows/temporary_seed_flow.py | 9 ++++----- ports/stm32/boards/Passport/modules/ui/ui.py | 10 ++++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py index 643c6ca7d..dea360732 100644 --- a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py @@ -41,10 +41,7 @@ async def clear_seed(self): settings.exit_temporary_mode() # await spinner_task('Clearing temporary seed', delay_task, args=[1000, False]) await SuccessPage(text='Temporary Seed Cleared').show() - - self.set_result(True) - ui.full_cards_refresh() - await self.wait_to_die() + await self.finalize() async def use_child_seed(self): from derived_key import get_key_type_from_tn @@ -84,7 +81,9 @@ async def use_child_seed(self): return await SuccessPage(text='Temporary Seed Applied').show() + await self.finalize() + async def finalize(self): self.set_result(True) - ui.full_cards_refresh() + ui.full_cards_refresh(go_to_account_0=True) await self.wait_to_die() diff --git a/ports/stm32/boards/Passport/modules/ui/ui.py b/ports/stm32/boards/Passport/modules/ui/ui.py index ed566121f..68f045d4d 100644 --- a/ports/stm32/boards/Passport/modules/ui/ui.py +++ b/ports/stm32/boards/Passport/modules/ui/ui.py @@ -98,8 +98,10 @@ def set_screen_bg_color(self, bg_color): self.active_screen.set_bg_color(bg_color) def set_cards(self, card_descs, active_idx=0): - assert(active_idx >= 0) - assert(active_idx < len(card_descs)) + # An index out of bounds could occur when the account/passphrase/seed changes + # and the active_idx isnt updated + if active_idx < 0 or active_idx > len(card_descs): + active_idx = 1 # print('set_cards: len={}'.format(len(card_descs))) self.card_descs = card_descs @@ -336,13 +338,13 @@ def on_nav_right(self): self.next_card() # Full refresh - def full_cards_refresh(self): + def full_cards_refresh(self, go_to_account_0=False): from utils import start_task self.update_cards() async def restart_main_task(): - self.start_card_task(card_idx=self.active_card_idx) + self.start_card_task(card_idx=(1 if go_to_account_0 else self.active_card_idx)) start_task(restart_main_task()) From a1706a9cff7ec7dd929677d3d899e990cd3af78e Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Mon, 29 Jul 2024 13:28:40 -0400 Subject: [PATCH 092/126] SFT-1943: tested and fixed restoring backups with 6 word encryption --- .../stm32/boards/Passport/modules/flows/restore_backup_flow.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ports/stm32/boards/Passport/modules/flows/restore_backup_flow.py b/ports/stm32/boards/Passport/modules/flows/restore_backup_flow.py index 2750b31bf..1eaf583d2 100644 --- a/ports/stm32/boards/Passport/modules/flows/restore_backup_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/restore_backup_flow.py @@ -103,10 +103,11 @@ async def enter_backup_password(self): initial_prefixes=self.backup_password_prefixes).show() (backup_password_words, self.backup_password_prefixes, _) = result - if result is None: + if backup_password_words is None: cancel = await QuestionPage(text='Cancel password entry? ' + 'All progress will be lost.').show() if cancel: + self.backup_password_prefixes = [] self.back() else: self.backup_password_words = backup_password_words From a88b85559013cb7d55d3b908f4180564687454ae Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Tue, 30 Jul 2024 10:15:01 -0400 Subject: [PATCH 093/126] SFT-1728: temporary seeds entered from plus menu return to plus menu --- .../boards/Passport/modules/flows/temporary_seed_flow.py | 6 +++--- ports/stm32/boards/Passport/modules/ui/ui.py | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py index dea360732..43f547162 100644 --- a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py @@ -81,9 +81,9 @@ async def use_child_seed(self): return await SuccessPage(text='Temporary Seed Applied').show() - await self.finalize() + await self.finalize(child=True) - async def finalize(self): + async def finalize(self, child=False): self.set_result(True) - ui.full_cards_refresh(go_to_account_0=True) + ui.full_cards_refresh(go_to_account_0=child, go_to_plus_menu=(not child)) await self.wait_to_die() diff --git a/ports/stm32/boards/Passport/modules/ui/ui.py b/ports/stm32/boards/Passport/modules/ui/ui.py index 68f045d4d..f071a4953 100644 --- a/ports/stm32/boards/Passport/modules/ui/ui.py +++ b/ports/stm32/boards/Passport/modules/ui/ui.py @@ -338,13 +338,14 @@ def on_nav_right(self): self.next_card() # Full refresh - def full_cards_refresh(self, go_to_account_0=False): + def full_cards_refresh(self, go_to_account_0=False, go_to_plus_menu=False): from utils import start_task - self.update_cards() + self.update_cards(is_init=go_to_account_0, + stay_on_last_card=go_to_plus_menu) async def restart_main_task(): - self.start_card_task(card_idx=(1 if go_to_account_0 else self.active_card_idx)) + self.start_card_task(card_idx=self.active_card_idx) start_task(restart_main_task()) From b3d5329bedd2aca82757b4761f6bf26fa459bd85 Mon Sep 17 00:00:00 2001 From: mjg-foundation <114431859+mjg-foundation@users.noreply.github.com> Date: Tue, 30 Jul 2024 18:27:48 -0400 Subject: [PATCH 094/126] Update version.txt to v2.3.2b7 --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index 86806dec3..8612accf0 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -2.3.2b1 +2.3.2b7 From ddf97824fe0138be26eec852b8ec1f966a3d8e51 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Wed, 31 Jul 2024 23:57:41 -0400 Subject: [PATCH 095/126] SFT-3903: fixed passphrase and temporary seed interactions --- .../modules/flows/apply_passphrase_flow.py | 2 +- .../Passport/modules/flows/backup_flow.py | 5 ++--- .../modules/flows/new_derived_key_flow.py | 4 ++-- .../modules/flows/seed_warning_flow.py | 4 ++-- .../modules/flows/view_seed_words_flow.py | 5 +++-- .../Passport/modules/multisig_wallet.py | 2 +- ports/stm32/boards/Passport/modules/stash.py | 19 ++++++++++++++----- .../modules/tasks/apply_passphrase_task.py | 10 +++++----- ports/stm32/boards/Passport/modules/ui/ui.py | 10 +++++++--- ports/stm32/boards/Passport/modules/utils.py | 2 +- 10 files changed, 38 insertions(+), 25 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/apply_passphrase_flow.py b/ports/stm32/boards/Passport/modules/flows/apply_passphrase_flow.py index 56b0a32b0..8897edb30 100644 --- a/ports/stm32/boards/Passport/modules/flows/apply_passphrase_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/apply_passphrase_flow.py @@ -12,7 +12,7 @@ def __init__(self, passphrase=None): # Caller wants to set this passphrase self.attempted = False - self.prev_passphrase = stash.bip39_passphrase + self.prev_passphrase = stash.get_passphrase() self.passphrase = passphrase self.msg = 'Apply' if self.passphrase is not None: diff --git a/ports/stm32/boards/Passport/modules/flows/backup_flow.py b/ports/stm32/boards/Passport/modules/flows/backup_flow.py index 42cd03c54..537322bbc 100644 --- a/ports/stm32/boards/Passport/modules/flows/backup_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/backup_flow.py @@ -25,8 +25,7 @@ def __init__(self, initial_backup=False, left_micron=microns.Back): async def show_intro(self): from pages import InfoPage - from utils import recolor - import stash + from utils import recolor, is_passphrase_active if self.backup_quiz_passed: msgs = ['Passport is about to create an updated microSD backup.', @@ -37,7 +36,7 @@ async def show_intro(self): recolor(HIGHLIGHT_TEXT_HEX, 'REQUIRED')), 'We recommend writing down the Backup Code on the included security card.', 'We consider this safe since physical access to the microSD card is required to access the backup.'] - if stash.bip39_passphrase != '': + if is_passphrase_active(): msgs.append('The current passphrase applied to Passport will not be saved as part of this backup.') result = await InfoPage( diff --git a/ports/stm32/boards/Passport/modules/flows/new_derived_key_flow.py b/ports/stm32/boards/Passport/modules/flows/new_derived_key_flow.py index 869ef70db..796beed44 100644 --- a/ports/stm32/boards/Passport/modules/flows/new_derived_key_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/new_derived_key_flow.py @@ -47,10 +47,10 @@ async def key_limit_warning(self): async def passphrase_warning(self): from pages import LongTextPage + from utils import is_passphrase_active import microns - import stash - if len(stash.bip39_passphrase) > 0: + if is_passphrase_active(): text = '''\ \n\nThis new key will be linked to your active passphrase. \ It will only be displayed when this same passphrase is applied. Continue?''' diff --git a/ports/stm32/boards/Passport/modules/flows/seed_warning_flow.py b/ports/stm32/boards/Passport/modules/flows/seed_warning_flow.py index 9754c2ea0..b6e9477b2 100644 --- a/ports/stm32/boards/Passport/modules/flows/seed_warning_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/seed_warning_flow.py @@ -49,9 +49,9 @@ async def show_intro(self): import lvgl as lv import microns from pages import InfoPage - import stash + from utils import is_passphrase_active - if self.mention_passphrase and stash.bip39_passphrase: + if self.mention_passphrase and is_passphrase_active(): text = 'Passport is about to {} and passphrase'.format(self.action_text) else: text = 'Passport is about to {}'.format(self.action_text) diff --git a/ports/stm32/boards/Passport/modules/flows/view_seed_words_flow.py b/ports/stm32/boards/Passport/modules/flows/view_seed_words_flow.py index d99de814f..847ac3d93 100644 --- a/ports/stm32/boards/Passport/modules/flows/view_seed_words_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/view_seed_words_flow.py @@ -230,8 +230,9 @@ async def qr_button(self): async def show_passphrase(self): import stash + from utils import is_passphrase_active from pages import InfoPage - if stash.bip39_passphrase != '' and not self.external_key: - await InfoPage(text='Passphrase: {}'.format(stash.bip39_passphrase)).show() + if is_passphrase_active() and not self.external_key: + await InfoPage(text='Passphrase: {}'.format(stash.get_passphrase())).show() self.set_result(True) diff --git a/ports/stm32/boards/Passport/modules/multisig_wallet.py b/ports/stm32/boards/Passport/modules/multisig_wallet.py index 0dd325782..240bf1215 100644 --- a/ports/stm32/boards/Passport/modules/multisig_wallet.py +++ b/ports/stm32/boards/Passport/modules/multisig_wallet.py @@ -726,7 +726,7 @@ def from_file(cls, config, name=None): unique_id = b2a_hex(unique_id).decode('utf-8') # TODO: why does this band-aid "multisig object is not iterable" error? - passphrase = stash.bip39_passphrase + passphrase = stash.get_passphrase() if passphrase != '': await spinner_task('Checking Multisig Config', apply_passphrase_task, args=[passphrase]) diff --git a/ports/stm32/boards/Passport/modules/stash.py b/ports/stm32/boards/Passport/modules/stash.py index 480b199b4..541bcff48 100644 --- a/ports/stm32/boards/Passport/modules/stash.py +++ b/ports/stm32/boards/Passport/modules/stash.py @@ -121,9 +121,18 @@ def decode(secret, _bip39pw=''): return 'master', ms, hd -# optional global value: user-supplied passphrase to salt BIP39 seed process -bip39_passphrase = '' -bip39_hash = '' +def set_passphrase(passphrase=''): + from common import settings + settings.set_volatile('bip39_passphrase', passphrase) + + +def clear_passphrase(): + set_passphrase('') + + +def get_passphrase(): + from common import settings + return settings.get('bip39_passphrase', '') class SensitiveValues: @@ -154,7 +163,7 @@ def __init__(self, secret=None, for_backup=False): self.spots = [] # backup during volatile bip39 encryption: do not use passphrase - self._bip39pw = '' if for_backup else str(bip39_passphrase) + self._bip39pw = '' if for_backup else str(get_passphrase()) # print('self._bip39pw={}'.format(self._bip39pw)) def __enter__(self): @@ -228,7 +237,7 @@ def capture_xpub(self, save=False): # Always store these volatile - Takes less than 1 second to recreate, and it will change whenever # a passphrase is entered, so no need to waste flash cycles on storing it. - if bip39_passphrase == '': + if get_passphrase() == '': settings.set_volatile('root_xfp', xfp) if save: settings.set('xfp', xfp) diff --git a/ports/stm32/boards/Passport/modules/tasks/apply_passphrase_task.py b/ports/stm32/boards/Passport/modules/tasks/apply_passphrase_task.py index c9b67d382..077296df8 100644 --- a/ports/stm32/boards/Passport/modules/tasks/apply_passphrase_task.py +++ b/ports/stm32/boards/Passport/modules/tasks/apply_passphrase_task.py @@ -12,22 +12,22 @@ from errors import Error import stash import foundation -from utils import bytes_to_hex_str +from utils import bytes_to_hex_str, is_passphrase_active import common async def apply_passphrase_task(on_done, passphrase): - if stash.bip39_passphrase == '': + if not is_passphrase_active(): # This checks if the root_xfp has been generated and saved, # If not, it's saved to settings for future use common.settings.get('root_xfp') - stash.bip39_passphrase = passphrase + stash.set_passphrase(passphrase) # Create a hash from the passphrase - if len(stash.bip39_passphrase) > 0: + if len(passphrase) > 0: digest = bytearray(32) - foundation.sha256(stash.bip39_passphrase, digest) + foundation.sha256(passphrase, digest) digest_hex = bytes_to_hex_str(digest) stash.bip39_hash = digest_hex[:8] # Take first 8 characters (32-bits) # print('stash.bip39_hash={}'.format(stash.bip39_hash)) diff --git a/ports/stm32/boards/Passport/modules/ui/ui.py b/ports/stm32/boards/Passport/modules/ui/ui.py index f071a4953..66877b0f6 100644 --- a/ports/stm32/boards/Passport/modules/ui/ui.py +++ b/ports/stm32/boards/Passport/modules/ui/ui.py @@ -202,7 +202,11 @@ def update_cards(self, is_init=False, stay_on_last_card=False): from flows import MenuFlow - from utils import get_accounts_by_xfp, has_seed, is_extension_enabled, escape_text + from utils import (get_accounts_by_xfp, + has_seed, + is_extension_enabled, + escape_text, + is_passphrase_active) from menus import account_menu, plus_menu from extensions.extensions import supported_extensions from constants import DEFAULT_ACCOUNT_ENTRY @@ -268,7 +272,7 @@ def update_cards(self, 'args': {'menu': account_menu, 'is_top_level': True}, 'account': account } - if len(stash.bip39_passphrase) > 0: + if is_passphrase_active(): account_card['icon'] = 'ICON_PASSPHRASE' card_descs.append(account_card) @@ -277,7 +281,7 @@ def update_cards(self, for extension in supported_extensions: if is_extension_enabled(extension['name']): - if len(stash.bip39_passphrase) > 0: + if is_passphrase_active(): extension['card']['icon'] = 'ICON_PASSPHRASE' else: extension['card']['icon'] = None diff --git a/ports/stm32/boards/Passport/modules/utils.py b/ports/stm32/boards/Passport/modules/utils.py index 84be6c26f..ecb1528a1 100644 --- a/ports/stm32/boards/Passport/modules/utils.py +++ b/ports/stm32/boards/Passport/modules/utils.py @@ -1295,7 +1295,7 @@ def are_hidden_keys_showing(): def is_passphrase_active(): import stash - return stash.bip39_passphrase != '' + return stash.get_passphrase() != '' MSG_CHARSET = range(32, 127) From f1c96952d25b7e585f182f3e96c9fd342670afa5 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Thu, 1 Aug 2024 00:22:54 -0400 Subject: [PATCH 096/126] SFT-3902: fixed runaway temporary mode --- .../boards/Passport/modules/flows/restore_seed_flow.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py index 9ebfcf1ac..808dbe44b 100644 --- a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py @@ -71,7 +71,6 @@ async def explain_temporary(self): if not result: if not self.back(): - settings.exit_temporary_mode() self.set_result(None) return @@ -247,3 +246,8 @@ async def valid_seed(self): text = 'Unable to {} seed'.format('apply' if self.temporary else 'save') await ErrorPage(text).show() self.set_result(False) + + def set_result(self, result, forget_state=True): + if not result: + settings.exit_temporary_mode() + super().set_result(result, forget_state) From 86ac3db3d65bc5f39ec881768424e647f72b8212 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Thu, 1 Aug 2024 15:55:47 -0400 Subject: [PATCH 097/126] SFT-3903: added extra check before clearing a temporary seed --- .../Passport/modules/flows/temporary_seed_flow.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py index 43f547162..9342f4564 100644 --- a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py @@ -36,10 +36,16 @@ async def enter_seed(self): async def clear_seed(self): from utils import spinner_task, start_task from tasks import delay_task - from pages import SuccessPage + from pages import SuccessPage, QuestionPage + + result = await QuestionPage(text='Clear Temporary Seed?').show() + + if not result: + # Setting result to false exits temporary mode + self.set_result(True) + return settings.exit_temporary_mode() - # await spinner_task('Clearing temporary seed', delay_task, args=[1000, False]) await SuccessPage(text='Temporary Seed Cleared').show() await self.finalize() From fe114cfd7dd043c653ca4b9bf47e1f582a4e48ab Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Fri, 2 Aug 2024 14:44:05 -0400 Subject: [PATCH 098/126] SFT-3903: added mention of clearing passphrases with the temporary seed --- .../boards/Passport/modules/flows/temporary_seed_flow.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py index 9342f4564..bd6258899 100644 --- a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py @@ -34,11 +34,16 @@ async def enter_seed(self): self.set_result(result) async def clear_seed(self): - from utils import spinner_task, start_task + from utils import spinner_task, start_task, is_passphrase_active from tasks import delay_task from pages import SuccessPage, QuestionPage - result = await QuestionPage(text='Clear Temporary Seed?').show() + if is_passphrase_active(): + text = 'Clear temporary seed? The passphrase applied to it will also be removed.' + else: + text = 'Clear Temporary Seed?' + + result = await QuestionPage(text=text).show() if not result: # Setting result to false exits temporary mode From a703c9bac4287b624f7a0417bd392bdadbc3d3a6 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Mon, 5 Aug 2024 12:28:22 -0400 Subject: [PATCH 099/126] SFT-3903: fixed capitalization, implemented Ken's copy suggestion --- .../Passport/modules/flows/temporary_seed_flow.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py index bd6258899..724f51b08 100644 --- a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py @@ -41,7 +41,7 @@ async def clear_seed(self): if is_passphrase_active(): text = 'Clear temporary seed? The passphrase applied to it will also be removed.' else: - text = 'Clear Temporary Seed?' + text = 'Clear temporary seed?' result = await QuestionPage(text=text).show() @@ -51,7 +51,7 @@ async def clear_seed(self): return settings.exit_temporary_mode() - await SuccessPage(text='Temporary Seed Cleared').show() + await SuccessPage(text='Temporary seed cleared').show() await self.finalize() async def use_child_seed(self): @@ -75,7 +75,7 @@ async def use_child_seed(self): self.set_result(False) return - (vals, error) = await spinner_task(text='Retrieving Key', + (vals, error) = await spinner_task(text='Retrieving key', task=self.key_type['task'], args=[self.key['index']]) pk = vals['priv'] @@ -85,13 +85,13 @@ async def use_child_seed(self): return settings.enter_temporary_mode() - (error,) = await spinner_task('Applying Seed', save_seed_task, args=[pk]) + (error,) = await spinner_task('Applying seed', save_seed_task, args=[pk]) if error is not None: self.set_result(None) return - await SuccessPage(text='Temporary Seed Applied').show() + await SuccessPage(text='Temporary seed applied').show() await self.finalize(child=True) async def finalize(self, child=False): From 8dc6dae4e1e626d9baae0d81d467b9e128d9e74a Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Tue, 6 Aug 2024 15:01:00 -0400 Subject: [PATCH 100/126] SFT-3903: Ken's copy suggestion --- .../stm32/boards/Passport/modules/flows/temporary_seed_flow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py index 724f51b08..5be509660 100644 --- a/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/temporary_seed_flow.py @@ -39,7 +39,7 @@ async def clear_seed(self): from pages import SuccessPage, QuestionPage if is_passphrase_active(): - text = 'Clear temporary seed? The passphrase applied to it will also be removed.' + text = 'Clear temporary seed? The current passphrase will be removed.' else: text = 'Clear temporary seed?' From 3cc4308205f54804a3a8e0623f055518a9cb1919 Mon Sep 17 00:00:00 2001 From: mjg-foundation <114431859+mjg-foundation@users.noreply.github.com> Date: Wed, 7 Aug 2024 12:46:30 -0400 Subject: [PATCH 101/126] Update version.txt to v2.3.2b8 --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index 8612accf0..ace3adf68 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -2.3.2b7 +2.3.2b8 From 57bc334b3ecbde03e0e9c8faa41cb890526444ed Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Mon, 12 Aug 2024 06:44:06 -0400 Subject: [PATCH 102/126] SFT-3975: added security word toggle to device settings --- ports/stm32/boards/Passport/modules/public_constants.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ports/stm32/boards/Passport/modules/public_constants.py b/ports/stm32/boards/Passport/modules/public_constants.py index 15dd7e4d0..3423e16e2 100644 --- a/ports/stm32/boards/Passport/modules/public_constants.py +++ b/ports/stm32/boards/Passport/modules/public_constants.py @@ -127,7 +127,8 @@ DEVICE_SETTINGS = [ 'screen_brightness', 'shutdown_timeout', - 'device_name' + 'device_name', + 'security_words' ] # Size of a pin prefix: From 2452834ce9e9663bca0d34e604105869b942c7a5 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Mon, 12 Aug 2024 07:15:57 -0400 Subject: [PATCH 103/126] SFT-3975: added pin_prefix_hash to device settings --- ports/stm32/boards/Passport/modules/public_constants.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ports/stm32/boards/Passport/modules/public_constants.py b/ports/stm32/boards/Passport/modules/public_constants.py index 3423e16e2..65b1cbc01 100644 --- a/ports/stm32/boards/Passport/modules/public_constants.py +++ b/ports/stm32/boards/Passport/modules/public_constants.py @@ -128,7 +128,8 @@ 'screen_brightness', 'shutdown_timeout', 'device_name', - 'security_words' + 'security_words', + 'pin_prefix_hash' ] # Size of a pin prefix: From cafc0e9396969a3bd10832a5a882cc1fb6b35bc1 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Mon, 12 Aug 2024 13:51:05 -0400 Subject: [PATCH 104/126] SFT-3980: first pass at clarifying psbt fingerprint mismatch message --- ports/stm32/boards/Passport/modules/psbt.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/psbt.py b/ports/stm32/boards/Passport/modules/psbt.py index 00e373057..93b2d6fa5 100644 --- a/ports/stm32/boards/Passport/modules/psbt.py +++ b/ports/stm32/boards/Passport/modules/psbt.py @@ -268,6 +268,7 @@ def parse_subpaths(self, my_xfp): # update in place self.subpaths[pk] = here + print("subpath my_xfp: {}".format(xfp2str(my_xfp))) if here[0] == my_xfp or here[0] == swab32(my_xfp): num_ours += 1 else: @@ -299,6 +300,7 @@ def parse_subpaths(self, my_xfp): # update in place self.tap_subpaths[pk] = (here, tap_hashes) + print("tap subpath my_xfp: {}".format(xfp2str(my_xfp))) if here[0] == my_xfp or here[0] == swab32(my_xfp): num_ours += 1 else: @@ -935,6 +937,7 @@ def __init__(self): from common import settings self.my_xfp = settings.get('xfp', 0) + print("psbtObject xfp: {}".format(xfp2str(self.my_xfp))) # details that we discover as we go self.inputs = None @@ -1467,9 +1470,9 @@ def consider_keys(self): others.discard(self.my_xfp) msg = ', '.join(xfp2str(i) for i in others) - raise FatalPSBTIssue('None of the keys involved in this transaction ' - 'belong to this Passport (need %s, found %s).' - % (xfp2str(self.my_xfp), msg)) + raise FatalPSBTIssue('This transaction can be signed by these wallets: %s,' + 'but this passport has fingerprint %s.' + % (msg, xfp2str(self.my_xfp))) @classmethod def read_psbt(cls, fd): From 563076741f479f305e635a49a7a7d4903b3f4dac Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Tue, 13 Aug 2024 21:11:27 -0400 Subject: [PATCH 105/126] SFT-3980: fixed wrong wallet signature message --- ports/stm32/boards/Passport/modules/psbt.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/psbt.py b/ports/stm32/boards/Passport/modules/psbt.py index 93b2d6fa5..49361de8a 100644 --- a/ports/stm32/boards/Passport/modules/psbt.py +++ b/ports/stm32/boards/Passport/modules/psbt.py @@ -1470,9 +1470,9 @@ def consider_keys(self): others.discard(self.my_xfp) msg = ', '.join(xfp2str(i) for i in others) - raise FatalPSBTIssue('This transaction can be signed by these wallets: %s,' - 'but this passport has fingerprint %s.' - % (msg, xfp2str(self.my_xfp))) + raise FatalPSBTIssue('Transaction must be signed with wallet%s %s. ' + 'Currently active wallet is %s.' + % ('' if len(others) == 1 else 's', msg, xfp2str(self.my_xfp))) @classmethod def read_psbt(cls, fd): From 2a5543a47363d73d2c14fd53a22ad08916a09a99 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Tue, 13 Aug 2024 21:13:53 -0400 Subject: [PATCH 106/126] SFT-3980: removed debug --- ports/stm32/boards/Passport/modules/psbt.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/psbt.py b/ports/stm32/boards/Passport/modules/psbt.py index 49361de8a..23527e342 100644 --- a/ports/stm32/boards/Passport/modules/psbt.py +++ b/ports/stm32/boards/Passport/modules/psbt.py @@ -268,7 +268,6 @@ def parse_subpaths(self, my_xfp): # update in place self.subpaths[pk] = here - print("subpath my_xfp: {}".format(xfp2str(my_xfp))) if here[0] == my_xfp or here[0] == swab32(my_xfp): num_ours += 1 else: @@ -300,7 +299,6 @@ def parse_subpaths(self, my_xfp): # update in place self.tap_subpaths[pk] = (here, tap_hashes) - print("tap subpath my_xfp: {}".format(xfp2str(my_xfp))) if here[0] == my_xfp or here[0] == swab32(my_xfp): num_ours += 1 else: @@ -937,7 +935,6 @@ def __init__(self): from common import settings self.my_xfp = settings.get('xfp', 0) - print("psbtObject xfp: {}".format(xfp2str(self.my_xfp))) # details that we discover as we go self.inputs = None From a0418f1458095bd251479241e44d450dd106ab1a Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Tue, 13 Aug 2024 21:31:24 -0400 Subject: [PATCH 107/126] SFT-3982: removed backup prompts for temp seed entries, unless the final word is randomly generated --- .../boards/Passport/modules/flows/restore_seed_flow.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py index 808dbe44b..f34be56c2 100644 --- a/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/restore_seed_flow.py @@ -33,6 +33,7 @@ def __init__(self, refresh_cards_when_done=False, autobackup=True, full_backup=F self.full_backup = full_backup self.autobackup = autobackup self.temporary = temporary + self.get_last_word = False # This must be after super().__init__, so it isn't set to null self.statusbar = {'title': 'IMPORT SEED', 'icon': 'ICON_SEED'} @@ -158,7 +159,7 @@ async def enter_seed_words(self): initial_prefixes=self.prefixes, start_index=self.index).show() - seed_words, self.prefixes, get_last_word = result + seed_words, self.prefixes, self.get_last_word = result if seed_words is None: cancel = await QuestionPage( @@ -172,7 +173,7 @@ async def enter_seed_words(self): return self.seed_words = seed_words - if get_last_word: + if self.get_last_word: last_word = await RandomFinalWordFlow(self.seed_words).run() @@ -231,7 +232,9 @@ async def valid_seed(self): text = 'New seed imported and {}'.format('applied' if self.temporary else 'saved') await SuccessPage(text=text).show() - if self.full_backup: + + # Only prompt for a backup in temporary mode if the last word was generated + if (self.get_last_word if self.temporary else self.full_backup): await BackupFlow(initial_backup=True, left_micron=microns.Cancel).run() elif self.autobackup: await AutoBackupFlow(offer=True).run() From fe5bb64beb1aee9b817b9381338d4571dabd5b22 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Wed, 14 Aug 2024 09:40:44 -0400 Subject: [PATCH 108/126] SFT-3980: fixed ordering of XFPs mentioned, reverting messaging to previous copy for later revision --- ports/stm32/boards/Passport/modules/psbt.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/psbt.py b/ports/stm32/boards/Passport/modules/psbt.py index 23527e342..812debcff 100644 --- a/ports/stm32/boards/Passport/modules/psbt.py +++ b/ports/stm32/boards/Passport/modules/psbt.py @@ -1467,9 +1467,9 @@ def consider_keys(self): others.discard(self.my_xfp) msg = ', '.join(xfp2str(i) for i in others) - raise FatalPSBTIssue('Transaction must be signed with wallet%s %s. ' - 'Currently active wallet is %s.' - % ('' if len(others) == 1 else 's', msg, xfp2str(self.my_xfp))) + raise FatalPSBTIssue('None of the keys involved in this transaction ' + 'belong to this Passport (need %s, found %s).' + % (msg, xfp2str(self.my_xfp))) @classmethod def read_psbt(cls, fd): From b0d15a020ca250cfbf1389685d80500634b34d34 Mon Sep 17 00:00:00 2001 From: mjg-foundation <114431859+mjg-foundation@users.noreply.github.com> Date: Wed, 14 Aug 2024 11:12:15 -0400 Subject: [PATCH 109/126] Update version.txt to 2.3.2b9 --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index ace3adf68..8bab31c0a 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -2.3.2b8 +2.3.2b9 From cc82a1e1f77fef98beaa805271b7a95b067f9501 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Fri, 16 Aug 2024 14:54:15 -0400 Subject: [PATCH 110/126] SFT-3982: only run autobackup if quiz was passed --- .../stm32/boards/Passport/modules/flows/auto_backup_flow.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ports/stm32/boards/Passport/modules/flows/auto_backup_flow.py b/ports/stm32/boards/Passport/modules/flows/auto_backup_flow.py index 9fbc7487e..954580f4f 100644 --- a/ports/stm32/boards/Passport/modules/flows/auto_backup_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/auto_backup_flow.py @@ -10,14 +10,20 @@ class AutoBackupFlow(Flow): def __init__(self, reason='System information changed.', offer=False): + from common import settings super().__init__(initial_state=self.check_for_microsd, name='AutoBackupFlow') self.reason = reason self.offer = offer + self.backup_quiz_passed = settings.get('backup_quiz', False) async def check_for_microsd(self): from files import CardSlot, CardMissingError from pages import QuestionPage + if not self.backup_quiz_passed: + self.set_result(False) + return + try: with CardSlot() as card: # If the card is inserted, we can try to run the backup flow From bf6d4b0e3f3a65c051284288bbec8f000e9a1a17 Mon Sep 17 00:00:00 2001 From: mjg-foundation <114431859+mjg-foundation@users.noreply.github.com> Date: Mon, 19 Aug 2024 08:34:07 -0400 Subject: [PATCH 111/126] Update version.txt to beta 10 --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index 8bab31c0a..27840fcf1 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -2.3.2b9 +2.3.2bA From 61635e3322b851262407bc3d306efe3bba61b63d Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Tue, 20 Aug 2024 17:20:44 -0400 Subject: [PATCH 112/126] SFT-4094: formalized multisig policy default, clarified warning language around "required" multisig for signing --- .../boards/Passport/modules/multisig_wallet.py | 18 +++--------------- .../pages/multisig_policy_setting_page.py | 4 ++-- ports/stm32/boards/Passport/modules/psbt.py | 2 +- .../Passport/modules/public_constants.py | 5 ++++- .../boards/Passport/modules/wallets/theya.py | 3 --- 5 files changed, 10 insertions(+), 22 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/multisig_wallet.py b/ports/stm32/boards/Passport/modules/multisig_wallet.py index 240bf1215..2dd88d0b0 100644 --- a/ports/stm32/boards/Passport/modules/multisig_wallet.py +++ b/ports/stm32/boards/Passport/modules/multisig_wallet.py @@ -18,7 +18,7 @@ from ubinascii import hexlify as b2a_hex from utils import xfp2str, str2xfp, cleanup_deriv_path, keypath_to_str, str_to_keypath from public_constants import (AF_P2SH, AF_P2WSH_P2SH, AF_P2WSH, AFC_SCRIPT, - TRUST_OFFER, TRUST_PSBT, TRUST_VERIFY, MAX_SIGNERS) + TRUST_DEFAULT, TRUST_PSBT, TRUST_VERIFY, MAX_SIGNERS) from constants import MAX_MULTISIG_NAME_LEN from exceptions import FatalPSBTIssue from opcodes import OP_CHECKMULTISIG @@ -159,13 +159,7 @@ def chain(self): @classmethod def get_trust_policy(cls): from common import settings - - which = settings.get('multisig_policy', None) - - if which is None: - which = TRUST_VERIFY if cls.exists() else TRUST_OFFER - - return which + return settings.get('multisig_policy', TRUST_DEFAULT) def serialize(self): # return a JSON-able object @@ -329,12 +323,6 @@ def get_all(cls): # return them all, as a generator return cls.iter_wallets() - @classmethod - def exists(cls): - # are there any wallets defined? - from common import settings - return bool(settings.get('multisig', False)) - @classmethod def get_count(cls): from common import settings @@ -878,7 +866,7 @@ def import_from_psbt(cls, M, N, xpubs_list): if trust_mode == TRUST_VERIFY: # already checked for existing import and wasn't found, so fail - raise FatalPSBTIssue("XPUBs in PSBT do not match any existing wallet") + raise FatalPSBTIssue("XPUBs in PSBT do not match any existing wallet as required by multisig policy") # build up an in-memory version of the wallet. # - capture address format based on path used for my leg (if standards compliant) diff --git a/ports/stm32/boards/Passport/modules/pages/multisig_policy_setting_page.py b/ports/stm32/boards/Passport/modules/pages/multisig_policy_setting_page.py index beaeecc4e..02100c81e 100644 --- a/ports/stm32/boards/Passport/modules/pages/multisig_policy_setting_page.py +++ b/ports/stm32/boards/Passport/modules/pages/multisig_policy_setting_page.py @@ -4,7 +4,7 @@ # multisig_policy_setting_page.py - Set the multisig policy from pages import SettingPage -from public_constants import TRUST_OFFER, TRUST_VERIFY, TRUST_PSBT +from public_constants import TRUST_OFFER, TRUST_VERIFY, TRUST_PSBT, TRUST_DEFAULT # Chooser for trust policy ch = ['Ask to Import', 'Require Existing', 'Skip Verification'] @@ -24,4 +24,4 @@ def __init__(self, card_header=None, statusbar=None): statusbar=statusbar, setting_name='multisig_policy', options=self.OPTIONS, - default_value=self.OPTIONS[1].get('value')) + default_value=TRUST_DEFAULT) diff --git a/ports/stm32/boards/Passport/modules/psbt.py b/ports/stm32/boards/Passport/modules/psbt.py index 812debcff..6105339fd 100644 --- a/ports/stm32/boards/Passport/modules/psbt.py +++ b/ports/stm32/boards/Passport/modules/psbt.py @@ -1173,13 +1173,13 @@ async def handle_xpubs(self): # print('proposed={}, need_approval={}'.format(proposed, need_approval)) # Gen1.2 We don't do the UI part of this here, but in SignPsbtCommonFlow + if need_approval: self.multisig_import_needs_approval = True # # do a complex UX sequence, which lets them save new wallet # ch = await proposed.confirm_import() # if ch != 'y': # raise FatalPSBTIssue("Refused to import new wallet") - self.active_multisig = proposed else: # Validate good match here. The xpubs must be exactly right, but diff --git a/ports/stm32/boards/Passport/modules/public_constants.py b/ports/stm32/boards/Passport/modules/public_constants.py index 65b1cbc01..612080b2f 100644 --- a/ports/stm32/boards/Passport/modules/public_constants.py +++ b/ports/stm32/boards/Passport/modules/public_constants.py @@ -112,6 +112,7 @@ TRUST_VERIFY = const(0) TRUST_OFFER = const(1) TRUST_PSBT = const(2) +TRUST_DEFAULT = TRUST_OFFER # Default Directories DIR_BACKUPS = 'backups' @@ -129,7 +130,9 @@ 'shutdown_timeout', 'device_name', 'security_words', - 'pin_prefix_hash' + 'pin_prefix_hash', + # 'multisig_policy', # TODO: do we want these to be globally accessible? + # 'multisig', ] # Size of a pin prefix: diff --git a/ports/stm32/boards/Passport/modules/wallets/theya.py b/ports/stm32/boards/Passport/modules/wallets/theya.py index 343a9af2c..91a698dfc 100644 --- a/ports/stm32/boards/Passport/modules/wallets/theya.py +++ b/ports/stm32/boards/Passport/modules/wallets/theya.py @@ -22,7 +22,4 @@ 'filename_pattern_multisig': '{xfp}-theya-multisig.json'} ], 'export_fw_version': True, - # 'skip_address_validation': True, - # 'skip_multisig_import': True, - # 'force_multisig_policy': True } From 03c3737d725b48bcb2d36abbb9c9e33e8f1defb4 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Tue, 20 Aug 2024 19:25:37 -0400 Subject: [PATCH 113/126] SFT-4094: updated multisig policy constant names for readability --- .../Passport/modules/flows/connect_wallet_flow.py | 8 ++++---- .../stm32/boards/Passport/modules/multisig_wallet.py | 8 ++++---- .../modules/pages/multisig_policy_setting_page.py | 12 ++++++------ .../boards/Passport/modules/public_constants.py | 8 ++++---- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/connect_wallet_flow.py b/ports/stm32/boards/Passport/modules/flows/connect_wallet_flow.py index a32676442..c619fad83 100644 --- a/ports/stm32/boards/Passport/modules/flows/connect_wallet_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/connect_wallet_flow.py @@ -25,7 +25,7 @@ get_deriv_path_from_addr_type_and_acct, get_addr_type_from_deriv, derive_address) -from public_constants import TRUST_PSBT +from public_constants import MUSIG_SKIP from wallets.constants import EXPORT_MODE_MICROSD, EXPORT_MODE_QR from wallets.sw_wallets import supported_software_wallets from utils import random_hex, spinner_task @@ -306,7 +306,7 @@ async def export_by_qr(self): self.set_result(False) return else: - common.settings.set('multisig_policy', TRUST_PSBT) + common.settings.set('multisig_policy', MUSIG_SKIP) self.goto(self.complete) else: self.goto(self.complete) @@ -375,7 +375,7 @@ async def export_by_microsd(self): self.set_result(False) return else: - common.settings.set('multisig_policy', TRUST_PSBT) + common.settings.set('multisig_policy', MUSIG_SKIP) self.goto(self.complete) else: self.goto(self.complete) @@ -475,7 +475,7 @@ async def do_multisig_config_import(self): self.set_result(False) return else: - common.settings.set('multisig_policy', TRUST_PSBT) + common.settings.set('multisig_policy', MUSIG_SKIP) self.goto(self.complete) else: self.goto(self.complete) diff --git a/ports/stm32/boards/Passport/modules/multisig_wallet.py b/ports/stm32/boards/Passport/modules/multisig_wallet.py index 2dd88d0b0..2d1f610b1 100644 --- a/ports/stm32/boards/Passport/modules/multisig_wallet.py +++ b/ports/stm32/boards/Passport/modules/multisig_wallet.py @@ -18,7 +18,7 @@ from ubinascii import hexlify as b2a_hex from utils import xfp2str, str2xfp, cleanup_deriv_path, keypath_to_str, str_to_keypath from public_constants import (AF_P2SH, AF_P2WSH_P2SH, AF_P2WSH, AFC_SCRIPT, - TRUST_DEFAULT, TRUST_PSBT, TRUST_VERIFY, MAX_SIGNERS) + MUSIG_DEFAULT, MUSIG_SKIP, MUSIG_REQUIRE, MAX_SIGNERS) from constants import MAX_MULTISIG_NAME_LEN from exceptions import FatalPSBTIssue from opcodes import OP_CHECKMULTISIG @@ -159,7 +159,7 @@ def chain(self): @classmethod def get_trust_policy(cls): from common import settings - return settings.get('multisig_policy', TRUST_DEFAULT) + return settings.get('multisig_policy', MUSIG_DEFAULT) def serialize(self): # return a JSON-able object @@ -864,7 +864,7 @@ def import_from_psbt(cls, M, N, xpubs_list): trust_mode = cls.get_trust_policy() # print('import_from_psbt(): trust_mode = {}'.format(trust_mode)) - if trust_mode == TRUST_VERIFY: + if trust_mode == MUSIG_REQUIRE: # already checked for existing import and wasn't found, so fail raise FatalPSBTIssue("XPUBs in PSBT do not match any existing wallet as required by multisig policy") @@ -895,7 +895,7 @@ def import_from_psbt(cls, M, N, xpubs_list): # may just keep just in-memory version, no approval required, if we are # trusting PSBT's today, otherwise caller will need to handle UX w.r.t new wallet - return ms, (trust_mode != TRUST_PSBT) + return ms, (trust_mode != MUSIG_SKIP) def validate_psbt_xpubs(self, xpubs_list): # The xpubs provided in PSBT must be exactly right, compared to our record. diff --git a/ports/stm32/boards/Passport/modules/pages/multisig_policy_setting_page.py b/ports/stm32/boards/Passport/modules/pages/multisig_policy_setting_page.py index 02100c81e..37059e624 100644 --- a/ports/stm32/boards/Passport/modules/pages/multisig_policy_setting_page.py +++ b/ports/stm32/boards/Passport/modules/pages/multisig_policy_setting_page.py @@ -4,18 +4,18 @@ # multisig_policy_setting_page.py - Set the multisig policy from pages import SettingPage -from public_constants import TRUST_OFFER, TRUST_VERIFY, TRUST_PSBT, TRUST_DEFAULT +from public_constants import MUSIG_ASK, MUSIG_REQUIRE, MUSIG_SKIP, MUSIG_DEFAULT # Chooser for trust policy ch = ['Ask to Import', 'Require Existing', 'Skip Verification'] -values = [TRUST_OFFER, TRUST_VERIFY, TRUST_PSBT] +values = [MUSIG_ASK, MUSIG_REQUIRE, MUSIG_SKIP] class MultisigPolicySettingPage(SettingPage): OPTIONS = [ - {'label': 'Ask to Import', 'value': TRUST_OFFER}, - {'label': 'Require Existing', 'value': TRUST_VERIFY}, - {'label': 'Skip Verification', 'value': TRUST_PSBT}, + {'label': 'Ask to Import', 'value': MUSIG_ASK}, + {'label': 'Require Existing', 'value': MUSIG_REQUIRE}, + {'label': 'Skip Verification', 'value': MUSIG_SKIP}, ] def __init__(self, card_header=None, statusbar=None): @@ -24,4 +24,4 @@ def __init__(self, card_header=None, statusbar=None): statusbar=statusbar, setting_name='multisig_policy', options=self.OPTIONS, - default_value=TRUST_DEFAULT) + default_value=MUSIG_DEFAULT) diff --git a/ports/stm32/boards/Passport/modules/public_constants.py b/ports/stm32/boards/Passport/modules/public_constants.py index 612080b2f..0efa6162a 100644 --- a/ports/stm32/boards/Passport/modules/public_constants.py +++ b/ports/stm32/boards/Passport/modules/public_constants.py @@ -109,10 +109,10 @@ MAX_SIGNERS = const(15) # PSBT Xpub trust policies -TRUST_VERIFY = const(0) -TRUST_OFFER = const(1) -TRUST_PSBT = const(2) -TRUST_DEFAULT = TRUST_OFFER +MUSIG_REQUIRE = const(0) +MUSIG_ASK = const(1) +MUSIG_SKIP = const(2) +MUSIG_DEFAULT = MUSIG_ASK # Default Directories DIR_BACKUPS = 'backups' From 989add3498ff23129eefe43a6f6738c94cddedaf Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Wed, 21 Aug 2024 18:44:39 -0400 Subject: [PATCH 114/126] SFT-4103: fixed missing keys messaging when signing a taproot transaction --- ports/stm32/boards/Passport/modules/psbt.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/psbt.py b/ports/stm32/boards/Passport/modules/psbt.py index 812debcff..d79e4f08a 100644 --- a/ports/stm32/boards/Passport/modules/psbt.py +++ b/ports/stm32/boards/Passport/modules/psbt.py @@ -1325,6 +1325,7 @@ def hard_bits(p): if not out.is_change: continue # it's a change output, okay if a p2sh change; we're looking at paths + # TODO: account for tap_subpaths for path in out.subpaths.values(): if path[0] != my_xfp: continue # possible in p2sh case @@ -1459,10 +1460,12 @@ def consider_keys(self): # collect a list of XFP's given in file that aren't ours others = set() for inp in self.inputs: - if not inp.subpaths: - continue - for path in inp.subpaths.values(): - others.add(path[0]) + if len(inp.subpaths) > 0: + for path in inp.subpaths.values(): + others.add(path[0]) + if len(inp.tap_subpaths) > 0: + for (path, tap_hashes) in inp.tap_subpaths.values(): + others.add(path[0]) others.discard(self.my_xfp) msg = ', '.join(xfp2str(i) for i in others) From d3fe8e68f9b3d44caa1c36c7bdf2fac5de9f343d Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Wed, 21 Aug 2024 18:49:37 -0400 Subject: [PATCH 115/126] SFT-4103: removed TODO --- ports/stm32/boards/Passport/modules/psbt.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ports/stm32/boards/Passport/modules/psbt.py b/ports/stm32/boards/Passport/modules/psbt.py index d79e4f08a..b937369d8 100644 --- a/ports/stm32/boards/Passport/modules/psbt.py +++ b/ports/stm32/boards/Passport/modules/psbt.py @@ -1325,7 +1325,6 @@ def hard_bits(p): if not out.is_change: continue # it's a change output, okay if a p2sh change; we're looking at paths - # TODO: account for tap_subpaths for path in out.subpaths.values(): if path[0] != my_xfp: continue # possible in p2sh case From eb91120e063bf3392f03623f3baa4ac334ae81a5 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Wed, 21 Aug 2024 19:08:52 -0400 Subject: [PATCH 116/126] SFT-4094: made a central function to get multisig policy, temporary key multisigs default to skip --- ports/stm32/boards/Passport/modules/multisig_wallet.py | 4 ++-- .../Passport/modules/pages/multisig_policy_setting_page.py | 3 ++- ports/stm32/boards/Passport/modules/public_constants.py | 1 + ports/stm32/boards/Passport/modules/utils.py | 7 ++++++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/multisig_wallet.py b/ports/stm32/boards/Passport/modules/multisig_wallet.py index 2d1f610b1..bd58ad3ce 100644 --- a/ports/stm32/boards/Passport/modules/multisig_wallet.py +++ b/ports/stm32/boards/Passport/modules/multisig_wallet.py @@ -158,8 +158,8 @@ def chain(self): @classmethod def get_trust_policy(cls): - from common import settings - return settings.get('multisig_policy', MUSIG_DEFAULT) + from utils import get_multisig_policy + return get_multisig_policy() def serialize(self): # return a JSON-able object diff --git a/ports/stm32/boards/Passport/modules/pages/multisig_policy_setting_page.py b/ports/stm32/boards/Passport/modules/pages/multisig_policy_setting_page.py index 37059e624..92e2b1248 100644 --- a/ports/stm32/boards/Passport/modules/pages/multisig_policy_setting_page.py +++ b/ports/stm32/boards/Passport/modules/pages/multisig_policy_setting_page.py @@ -5,6 +5,7 @@ from pages import SettingPage from public_constants import MUSIG_ASK, MUSIG_REQUIRE, MUSIG_SKIP, MUSIG_DEFAULT +from utils import get_multisig_policy # Chooser for trust policy ch = ['Ask to Import', 'Require Existing', 'Skip Verification'] @@ -24,4 +25,4 @@ def __init__(self, card_header=None, statusbar=None): statusbar=statusbar, setting_name='multisig_policy', options=self.OPTIONS, - default_value=MUSIG_DEFAULT) + default_value=get_multisig_policy()) diff --git a/ports/stm32/boards/Passport/modules/public_constants.py b/ports/stm32/boards/Passport/modules/public_constants.py index 0efa6162a..63cf591f9 100644 --- a/ports/stm32/boards/Passport/modules/public_constants.py +++ b/ports/stm32/boards/Passport/modules/public_constants.py @@ -113,6 +113,7 @@ MUSIG_ASK = const(1) MUSIG_SKIP = const(2) MUSIG_DEFAULT = MUSIG_ASK +MUSIG_TEMP_DEFAULT = MUSIG_SKIP # Default Directories DIR_BACKUPS = 'backups' diff --git a/ports/stm32/boards/Passport/modules/utils.py b/ports/stm32/boards/Passport/modules/utils.py index ecb1528a1..259626a62 100644 --- a/ports/stm32/boards/Passport/modules/utils.py +++ b/ports/stm32/boards/Passport/modules/utils.py @@ -12,7 +12,7 @@ import lvgl as lv from constants import NUM_BACKUP_CODE_SECTIONS, NUM_DIGITS_PER_BACKUP_CODE_SECTION -from public_constants import DIR_BACKUPS +from public_constants import DIR_BACKUPS, MUSIG_DEFAULT, MUSIG_TEMP_DEFAULT from files import CardSlot from styles.colors import DEFAULT_LARGE_ICON_COLOR import ustruct @@ -1569,4 +1569,9 @@ def insufficient_randomness(seed_words): return False +def get_multisig_policy(): + default = MUSIG_TEMP_DEFAULT if has_temporary_seed() else MUSIG_DEFAULT + return common.settings.get('multisig_policy', default) + + # EOF From 51fbca281e8af0e0162a83cc6f8cef108796c53d Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Thu, 22 Aug 2024 12:16:49 -0400 Subject: [PATCH 117/126] SFT-4094: removed commented code --- ports/stm32/boards/Passport/modules/public_constants.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/public_constants.py b/ports/stm32/boards/Passport/modules/public_constants.py index 63cf591f9..0d10ca809 100644 --- a/ports/stm32/boards/Passport/modules/public_constants.py +++ b/ports/stm32/boards/Passport/modules/public_constants.py @@ -132,8 +132,6 @@ 'device_name', 'security_words', 'pin_prefix_hash', - # 'multisig_policy', # TODO: do we want these to be globally accessible? - # 'multisig', ] # Size of a pin prefix: From a29c776cc011d93207490aecab5301033bd15b20 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Thu, 22 Aug 2024 12:46:17 -0400 Subject: [PATCH 118/126] SFT-4095: added update settings and other universal settings to device settings --- ports/stm32/boards/Passport/modules/public_constants.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ports/stm32/boards/Passport/modules/public_constants.py b/ports/stm32/boards/Passport/modules/public_constants.py index 65b1cbc01..3bfc79e3f 100644 --- a/ports/stm32/boards/Passport/modules/public_constants.py +++ b/ports/stm32/boards/Passport/modules/public_constants.py @@ -129,7 +129,13 @@ 'shutdown_timeout', 'device_name', 'security_words', - 'pin_prefix_hash' + 'pin_prefix_hash', + 'update', + 'firmware_title', + 'validated_ok', + 'terms_ok', + 'last_qr_size_idx', + 'last_qr_brightness', ] # Size of a pin prefix: From aaee1284ad1fd79a8bff336843c915b38801741c Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Thu, 22 Aug 2024 18:56:34 -0400 Subject: [PATCH 119/126] SFT-4094: improved multisig import flow --- .../flows/import_multisig_wallet_flow.py | 53 ++++++++++++------- .../modules/flows/sign_psbt_common_flow.py | 6 ++- .../Passport/modules/multisig_wallet.py | 9 +--- .../boards/Passport/modules/pages/page.py | 4 +- 4 files changed, 42 insertions(+), 30 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/flows/import_multisig_wallet_flow.py b/ports/stm32/boards/Passport/modules/flows/import_multisig_wallet_flow.py index 32899264a..8797966a3 100644 --- a/ports/stm32/boards/Passport/modules/flows/import_multisig_wallet_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/import_multisig_wallet_flow.py @@ -16,35 +16,50 @@ async def show_overview(self): msg, is_dup = self.ms.format_overview() if is_dup: - await ErrorPage(text="Duplicate wallet will not be imported.").show() + await ErrorPage(text="Duplicate multisig wallet will not be imported.").show() self.set_result(False) return - else: - result = await LongTextPage(card_header={'title': 'Confirm Import'}, text=msg, centered=True).show() - if not result: - self.set_result(False) - return + + await LongTextPage(card_header={'title': 'Confirm Import'}, text=msg, centered=True, left_micron=None).show() self.goto(self.show_details) async def show_details(self): - from errors import Error - from utils import spinner_task, escape_text - from tasks import save_multisig_wallet_task + from utils import escape_text msg = self.ms.format_details() result = await LongTextPage(card_header={'title': escape_text(self.ms.name)}, text=msg, centered=True).show() + if not result: self.back() + return + + self.goto(self.confirm_import) + + async def confirm_import(self): + from pages import QuestionPage + + result = await QuestionPage('Create new multisig wallet?').show() + + if not result: + self.set_result(False) + return + + self.goto(self.import_wallet) + + async def import_wallet(self): + from utils import spinner_task + from errors import Error + from tasks import save_multisig_wallet_task + + # User confirmed the import, so save + (error,) = await spinner_task('Saving multisig', save_multisig_wallet_task, args=[self.ms]) + if not error: + self.set_result(True) + elif error is Error.USER_SETTINGS_FULL: + await ErrorPage(text='Out of space in user settings. Too many multisig configs?').show() + self.set_result(False) else: - # User confirmed the import, so save - (error,) = await spinner_task('Saving multisig', save_multisig_wallet_task, args=[self.ms]) - if not error: - self.set_result(True) - elif error is Error.USER_SETTINGS_FULL: - await ErrorPage(text='Out of space in user settings. Too many multisig configs?').show() - self.set_result(False) - else: - await ErrorPage(text='Unable to save multisig config.').show() - self.set_result(False) + await ErrorPage(text='Unable to save multisig config.').show() + self.set_result(False) diff --git a/ports/stm32/boards/Passport/modules/flows/sign_psbt_common_flow.py b/ports/stm32/boards/Passport/modules/flows/sign_psbt_common_flow.py index dc74b1835..1624de0be 100644 --- a/ports/stm32/boards/Passport/modules/flows/sign_psbt_common_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/sign_psbt_common_flow.py @@ -49,8 +49,10 @@ async def check_multisig_import(self): if self.psbt.multisig_import_needs_approval: result = await ImportMultisigWalletFlow(self.psbt.active_multisig).run() if not result: - await ErrorPage( - 'The transaction can still be signed, but this multisig config will not be saved.').show() + text = 'The transaction can still be signed, but this multisig config will not be saved.' + result2 = await ErrorPage(text=text, left_micron=microns.Back).show() + if not result2: + return self.goto(self.show_transaction_details) diff --git a/ports/stm32/boards/Passport/modules/multisig_wallet.py b/ports/stm32/boards/Passport/modules/multisig_wallet.py index bd58ad3ce..b50f9d41f 100644 --- a/ports/stm32/boards/Passport/modules/multisig_wallet.py +++ b/ports/stm32/boards/Passport/modules/multisig_wallet.py @@ -976,18 +976,13 @@ def format_overview(self, importing=True): wallet first. Differences: '''.format(recolor(COPPER_HEX, 'WARNING:')) + ', '.join(diff_items) is_dup = True elif importing and num_dups: - msg = 'Duplicate wallet. All details are the same as an existing wallet, so it will not be added.' + msg = 'Duplicate wallet. All details are the same as an existing wallet, so it will not be added.\n\n' is_dup = True - elif not importing: - msg = '' else: - msg = 'Create new multisig wallet?' + msg = '' derivs, dsum = self.get_deriv_paths() - if importing: - msg += '\n\n' - msg += '''{name_title} {name} diff --git a/ports/stm32/boards/Passport/modules/pages/page.py b/ports/stm32/boards/Passport/modules/pages/page.py index 452276dfc..b7aca95c8 100644 --- a/ports/stm32/boards/Passport/modules/pages/page.py +++ b/ports/stm32/boards/Passport/modules/pages/page.py @@ -60,12 +60,12 @@ def detach(self): def left_action(self, is_pressed): # print('Page.right_action()') - if not is_pressed: + if not is_pressed and self.left_micron: self.set_result(False) def right_action(self, is_pressed): # print('Page.right_action()') - if not is_pressed: + if not is_pressed and self.right_micron: self.set_result(True) # By default, returns None so caller can differentiate between user pressing a button to back out From 45895e229c819b67a15cf0b52b08d8423d77dc40 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Fri, 23 Aug 2024 13:40:33 -0400 Subject: [PATCH 120/126] SFT-4094: changed prompt from create to import --- .../Passport/modules/flows/import_multisig_wallet_flow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/boards/Passport/modules/flows/import_multisig_wallet_flow.py b/ports/stm32/boards/Passport/modules/flows/import_multisig_wallet_flow.py index 8797966a3..cbce14c55 100644 --- a/ports/stm32/boards/Passport/modules/flows/import_multisig_wallet_flow.py +++ b/ports/stm32/boards/Passport/modules/flows/import_multisig_wallet_flow.py @@ -40,7 +40,7 @@ async def show_details(self): async def confirm_import(self): from pages import QuestionPage - result = await QuestionPage('Create new multisig wallet?').show() + result = await QuestionPage('Import new multisig wallet?').show() if not result: self.set_result(False) From 56169ac5ef07472fb546f0296641737489b62d74 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Fri, 23 Aug 2024 13:47:35 -0400 Subject: [PATCH 121/126] SFT-4094: removed excess diff --- ports/stm32/boards/Passport/modules/psbt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/boards/Passport/modules/psbt.py b/ports/stm32/boards/Passport/modules/psbt.py index 6105339fd..812debcff 100644 --- a/ports/stm32/boards/Passport/modules/psbt.py +++ b/ports/stm32/boards/Passport/modules/psbt.py @@ -1173,13 +1173,13 @@ async def handle_xpubs(self): # print('proposed={}, need_approval={}'.format(proposed, need_approval)) # Gen1.2 We don't do the UI part of this here, but in SignPsbtCommonFlow - if need_approval: self.multisig_import_needs_approval = True # # do a complex UX sequence, which lets them save new wallet # ch = await proposed.confirm_import() # if ch != 'y': # raise FatalPSBTIssue("Refused to import new wallet") + self.active_multisig = proposed else: # Validate good match here. The xpubs must be exactly right, but From aca781ad5af6627c4543a1717b2d084418b996e9 Mon Sep 17 00:00:00 2001 From: mjg-foundation <114431859+mjg-foundation@users.noreply.github.com> Date: Fri, 23 Aug 2024 15:34:21 -0400 Subject: [PATCH 122/126] Update version.txt to 2.3.2bB (11) --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index 27840fcf1..b8519ed50 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -2.3.2bA +2.3.2bB From 86739471523c36058877bb0b2a778c668b900d8b Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Mon, 26 Aug 2024 10:09:04 -0400 Subject: [PATCH 123/126] SFT-4088: erased multisig policy on device reset --- ports/stm32/boards/Passport/modules/tasks/erase_passport_task.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/stm32/boards/Passport/modules/tasks/erase_passport_task.py b/ports/stm32/boards/Passport/modules/tasks/erase_passport_task.py index 5cc5ad222..6b2c5c372 100644 --- a/ports/stm32/boards/Passport/modules/tasks/erase_passport_task.py +++ b/ports/stm32/boards/Passport/modules/tasks/erase_passport_task.py @@ -26,6 +26,7 @@ async def erase_passport_task(on_done, full_reset): settings.remove('xpub') settings.remove('words') settings.remove('multisig') + settings.remove('multisig_policy') settings.remove('accounts') settings.remove('backup_quiz') settings.remove('enable_passphrase') From 7fafe71328ede39ed76b6a5616e48b3240473db4 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Mon, 26 Aug 2024 15:03:49 -0400 Subject: [PATCH 124/126] SFT-4120: standardized menu icons and messages --- ports/stm32/boards/Passport/modules/menus.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/menus.py b/ports/stm32/boards/Passport/modules/menus.py index 9f9199dd4..3cac82a6d 100644 --- a/ports/stm32/boards/Passport/modules/menus.py +++ b/ports/stm32/boards/Passport/modules/menus.py @@ -35,8 +35,8 @@ def manage_account_menu(): from pages import AccountDetailsPage return [ - {'icon': 'ICON_FOLDER', 'label': 'Account Details', 'page': AccountDetailsPage}, - {'icon': 'ICON_INFO', 'label': 'Rename Account', 'flow': RenameAccountFlow}, + {'icon': 'ICON_FOLDER', 'label': 'View Details', 'page': AccountDetailsPage}, + {'icon': 'ICON_SIGN', 'label': 'Rename Account', 'flow': RenameAccountFlow}, {'icon': 'ICON_CONNECT', 'label': 'Connect Wallet', 'flow': ConnectWalletFlow, 'statusbar': {'title': 'CONNECT'}}, {'icon': 'ICON_CANCEL', 'label': 'Delete Account', 'flow': DeleteAccountFlow}, @@ -160,7 +160,7 @@ def device_menu(): {'icon': 'ICON_COUNTDOWN', 'label': 'Auto-Shutdown', 'page': AutoShutdownSettingPage}, {'icon': 'ICON_PIN', 'label': 'Change PIN', 'flow': ChangePINFlow, 'is_visible': is_logged_in}, {'icon': 'ICON_BATTERY', 'label': 'Battery', 'page': BatteryPage}, - {'icon': 'ICON_SIGN', 'label': 'Device Name', 'flow': RenameDeviceFlow}, + {'icon': 'ICON_SIGN', 'label': 'Rename Device', 'flow': RenameDeviceFlow}, {'icon': 'ICON_INFO', 'label': 'About', 'flow': AboutFlow}, ] @@ -188,7 +188,7 @@ def seed_item_menu(): TemporarySeedFlow) return [ {'icon': 'ICON_ONE_KEY', 'label': 'View Details', 'flow': ViewDerivedKeyDetailsFlow}, - {'icon': 'ICON_INFO', 'label': 'Rename', 'flow': RenameDerivedKeyFlow, 'auto_card_header': False}, + {'icon': 'ICON_SIGN', 'label': 'Rename', 'flow': RenameDerivedKeyFlow, 'auto_card_header': False}, {'icon': 'ICON_SCAN_QR', 'label': 'Export', 'flow': ExportDerivedKeyFlow}, {'icon': 'ICON_HOURGLASS', 'label': 'Temporary Seed', 'flow': TemporarySeedFlow, 'is_visible': lambda: not has_temporary_seed()}, @@ -209,7 +209,7 @@ def key_item_menu(): ExportDerivedKeyFlow) return [ {'icon': 'ICON_ONE_KEY', 'label': 'View Details', 'flow': ViewDerivedKeyDetailsFlow}, - {'icon': 'ICON_INFO', 'label': 'Rename', 'flow': RenameDerivedKeyFlow, 'auto_card_header': False}, + {'icon': 'ICON_SIGN', 'label': 'Rename', 'flow': RenameDerivedKeyFlow, 'auto_card_header': False}, {'icon': 'ICON_SCAN_QR', 'label': 'Export', 'flow': ExportDerivedKeyFlow}, {'icon': 'ICON_ERASE', 'label': 'Hide Key', @@ -354,8 +354,8 @@ def multisig_item_menu(): 'statusbar': {'title': 'EXPORT'}}, {'icon': 'ICON_MICROSD', 'label': 'Export via microSD', 'flow': ExportMultisigMicrosdFlow, 'statusbar': {'title': 'EXPORT'}}, - {'icon': 'ICON_TWO_KEYS', 'label': 'Rename', 'flow': RenameMultisigFlow, 'exit_on_success': True}, - {'icon': 'ICON_TWO_KEYS', 'label': 'Delete', 'flow': DeleteMultisigFlow, 'exit_on_success': True}, + {'icon': 'ICON_SIGN', 'label': 'Rename', 'flow': RenameMultisigFlow, 'exit_on_success': True}, + {'icon': 'ICON_CANCEL', 'label': 'Delete', 'flow': DeleteMultisigFlow, 'exit_on_success': True}, ] From e655676421bcf8478305608a8bd45bc73be479b4 Mon Sep 17 00:00:00 2001 From: Matt Gleason Date: Tue, 27 Aug 2024 13:58:31 -0400 Subject: [PATCH 125/126] SFT-4120: changed account options to "Rename" and "Delete" --- ports/stm32/boards/Passport/modules/menus.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/stm32/boards/Passport/modules/menus.py b/ports/stm32/boards/Passport/modules/menus.py index 3cac82a6d..07f5ed6c0 100644 --- a/ports/stm32/boards/Passport/modules/menus.py +++ b/ports/stm32/boards/Passport/modules/menus.py @@ -36,10 +36,10 @@ def manage_account_menu(): return [ {'icon': 'ICON_FOLDER', 'label': 'View Details', 'page': AccountDetailsPage}, - {'icon': 'ICON_SIGN', 'label': 'Rename Account', 'flow': RenameAccountFlow}, + {'icon': 'ICON_SIGN', 'label': 'Rename', 'flow': RenameAccountFlow}, {'icon': 'ICON_CONNECT', 'label': 'Connect Wallet', 'flow': ConnectWalletFlow, 'statusbar': {'title': 'CONNECT'}}, - {'icon': 'ICON_CANCEL', 'label': 'Delete Account', 'flow': DeleteAccountFlow}, + {'icon': 'ICON_CANCEL', 'label': 'Delete', 'flow': DeleteAccountFlow}, ] From 9bdb07f24d6bd1675465fd7332404cce901cc2e5 Mon Sep 17 00:00:00 2001 From: mjg-foundation <114431859+mjg-foundation@users.noreply.github.com> Date: Tue, 27 Aug 2024 17:35:13 -0400 Subject: [PATCH 126/126] Update version.txt to 2.3.2bC (12) --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index b8519ed50..56be9a7ff 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -2.3.2bB +2.3.2bC