diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c12d973f..c9cfae88 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -43,9 +43,12 @@ jobs: tests/requirements.txt - name: Install dependencies run: | - sudo apt-get install -y --no-install-recommends libzbar0 - pip install -r requirements.txt -r tests/requirements.txt - pip install . + sudo apt-get install libzbar0 + python -m pip install --upgrade pip + pip install -r requirements.txt -r tests/requirements.txt -r l10n/requirements-l10n.txt + pip install -e . + - name: Compile translations + run: python setup.py compile_catalog - name: Test with pytest run: | mkdir -p artifacts diff --git a/README.md b/README.md index 171bd671..4e0ef878 100644 --- a/README.md +++ b/README.md @@ -112,23 +112,23 @@ Instructions to build a SeedSigner OS image (using precisely the same process th ## Downloading the Software -Download the current Version (0.8.6) software image that is compatible with your Raspberry Pi Hardware. The Pi Zero 1.3 is the most common and recommended board. +Download the current Version (0.8.7) software image that is compatible with your Raspberry Pi Hardware. The Pi Zero 1.3 is the most common and recommended board. | Board | Download Image Link/Name | | --------------------- | --------------------------------- | -|**[Raspberry Pi Zero 1.3](https://www.raspberrypi.com/products/raspberry-pi-zero/)** |[`seedsigner_os.0.8.6.pi0.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.8.6/seedsigner_os.0.8.6.pi0.img) | -|[Raspberry Pi Zero W](https://www.raspberrypi.com/products/raspberry-pi-zero-w/) |[`seedsigner_os.0.8.6.pi0.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.8.6/seedsigner_os.0.8.6.pi0.img) | -|[Raspberry Pi Zero 2 W](https://www.raspberrypi.com/products/raspberry-pi-zero-2-w/) |[`seedsigner_os.0.8.6.pi02w.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.8.6/seedsigner_os.0.8.6.pi02w.img) | -|[Raspberry Pi 1 Model B/B+](https://www.raspberrypi.com/products/raspberry-pi-1-model-b-plus/) |[`seedsigner_os.0.8.6.pi0.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.8.6/seedsigner_os.0.8.6.pi0.img) | -|[Raspberry Pi 2 Model B](https://www.raspberrypi.com/products/raspberry-pi-2-model-b/) |[`seedsigner_os.0.8.6.pi2.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.8.6/seedsigner_os.0.8.6.pi2.img) | -|[Raspberry Pi 3 Model B](https://www.raspberrypi.com/products/raspberry-pi-3-model-b/) |[`seedsigner_os.0.8.6.pi02w.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.8.6/seedsigner_os.0.8.6.pi02w.img) | -|[Raspberry Pi 4 Model B](https://www.raspberrypi.com/products/raspberry-pi-4-model-b/) |[`seedsigner_os.0.8.6.pi4.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.8.6/seedsigner_os.0.8.6.pi4.img) | -|[Raspberry Pi 400](https://www.raspberrypi.com/products/raspberry-pi-400-unit/) |[`seedsigner_os.0.8.6.pi4.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.8.6/seedsigner_os.0.8.6.pi4.img) | +|**[Raspberry Pi Zero 1.3](https://www.raspberrypi.com/products/raspberry-pi-zero/)** |[`seedsigner_os.0.8.7.pi0.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.8.7/seedsigner_os.0.8.7.pi0.img) | +|[Raspberry Pi Zero W](https://www.raspberrypi.com/products/raspberry-pi-zero-w/) |[`seedsigner_os.0.8.7.pi0.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.8.7/seedsigner_os.0.8.7.pi0.img) | +|[Raspberry Pi Zero 2 W](https://www.raspberrypi.com/products/raspberry-pi-zero-2-w/) |[`seedsigner_os.0.8.7.pi02w.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.8.7/seedsigner_os.0.8.7.pi02w.img) | +|[Raspberry Pi 1 Model B/B+](https://www.raspberrypi.com/products/raspberry-pi-1-model-b-plus/) |[`seedsigner_os.0.8.7.pi0.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.8.7/seedsigner_os.0.8.7.pi0.img) | +|[Raspberry Pi 2 Model B](https://www.raspberrypi.com/products/raspberry-pi-2-model-b/) |[`seedsigner_os.0.8.7.pi2.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.8.7/seedsigner_os.0.8.7.pi2.img) | +|[Raspberry Pi 3 Model B](https://www.raspberrypi.com/products/raspberry-pi-3-model-b/) |[`seedsigner_os.0.8.7.pi02w.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.8.7/seedsigner_os.0.8.7.pi02w.img) | +|[Raspberry Pi 4 Model B](https://www.raspberrypi.com/products/raspberry-pi-4-model-b/) |[`seedsigner_os.0.8.7.pi4.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.8.7/seedsigner_os.0.8.7.pi4.img) | +|[Raspberry Pi 400](https://www.raspberrypi.com/products/raspberry-pi-400-unit/) |[`seedsigner_os.0.8.7.pi4.img`](https://github.com/SeedSigner/seedsigner/releases/download/0.8.7/seedsigner_os.0.8.7.pi4.img) | Note: If you have physically removed the WiFi component from your board, you will still use the image file of the original (un-modified) hardware. (Our files are compiled/based on the *processor* architecture). Although it is better to spend a few minutes upfront to determine which specific Pi hardware/model you have, if you are still unsure which hardware you have, you can try using the pi0.img file. Making an incorrect choice here will not ruin your board, because this is software, not firmware. **Also download** these 2 signature verification files to the same folder -[The Plaintext manifest file](https://github.com/SeedSigner/seedsigner/releases/download/0.8.6/seedsigner.0.8.6.sha256.txt) -[The Signature of the manifest file](https://github.com/SeedSigner/seedsigner/releases/download/0.8.6/seedsigner.0.8.6.sha256.txt.sig) +[The Plaintext manifest file](https://github.com/SeedSigner/seedsigner/releases/download/0.8.7/seedsigner.0.8.7.sha256.txt) +[The Signature of the manifest file](https://github.com/SeedSigner/seedsigner/releases/download/0.8.7/seedsigner.0.8.7.sha256.txt.sig) Users familiar with older versions of the SeedSigner software might be surprised with how fast their software downloads now are, because since version 0.6.0 the software image files are now 100x smaller! Each image file is now under 42 Megabytes so your downloads and verifications will be very quick now (and might even seem *too* quick)! @@ -160,7 +160,7 @@ The result should confirm that 1 key was *either* imported or updated. *Ignore* Next, you will run the *verify* command on the signature (.sig) file. (*Verify* must be run from inside the same folder that you downloaded the files into earlier.) ``` -gpg --verify seedsigner.0.8.6.sha256.txt.sig +gpg --verify seedsigner.0.8.7.sha256.txt.sig ``` When the verify command completes successfully, it should display output like this: @@ -230,22 +230,22 @@ Now that you have confirmed that you do have the real SeedSigner Project's Publi **On Linux or OSX:** Run this command ``` -shasum -a 256 --ignore-missing --check seedsigner.0.8.6.sha256.txt +shasum -a 256 --ignore-missing --check seedsigner.0.8.7.sha256.txt ``` Note: macOS versions earlier than v11 (Big Sur) do not support the `--ignore-missing` flag. You can omit it and disregard any missing file warnings. **On Windows (inside Powershell):** Run this command ``` -CertUtil -hashfile seedsigner_os.0.8.6.Insert_Your_Pi_Models_binary_here_For_Example_pi02w.img SHA256 +CertUtil -hashfile seedsigner_os.0.8.7.Insert_Your_Pi_Models_binary_here_For_Example_pi02w.img SHA256 ``` On Windows, you must then manually compare the resulting file hash value to the corresponding hash value shown inside the .SHA256 cleartext file.
Wait up to 30 seconds for the command to complete, and it should display: ``` -seedsigner_os.0.8.6.[Your_Pi_Model_For_Example:pi02w].img: OK +seedsigner_os.0.8.7.[Your_Pi_Model_For_Example:pi02w].img: OK ``` -**If you receive the "OK" message** for your **seedsigner_os.0.8.6.[Your_Pi_Model_For_Example:pi02w].img file**, as shown above, then your verification is fully complete! +**If you receive the "OK" message** for your **seedsigner_os.0.8.7.[Your_Pi_Model_For_Example:pi02w].img file**, as shown above, then your verification is fully complete! **All of your downloaded files have now been confirmed as both authentic and unaltered!** You can proceed to create/write your MicroSD cardπŸ˜„πŸ˜„ !! If your file result shows "FAILED", then you must stop here immediately. Do not continue. Contact us for assistance at the Telegram group address above. diff --git a/l10n/requirements-l10n.txt b/l10n/requirements-l10n.txt index b83c7a02..d7821516 100644 --- a/l10n/requirements-l10n.txt +++ b/l10n/requirements-l10n.txt @@ -1 +1,2 @@ Babel==2.16.0 +setuptools>=82.0.0 diff --git a/src/seedsigner/resources/seedsigner-translations b/src/seedsigner/resources/seedsigner-translations index 6b911be2..708961a4 160000 --- a/src/seedsigner/resources/seedsigner-translations +++ b/src/seedsigner/resources/seedsigner-translations @@ -1 +1 @@ -Subproject commit 6b911be2da88cd186d86ff5b7641fb048ebc3e9c +Subproject commit 708961a4163b1bd43eb15c4a26713649c63f6ccd diff --git a/src/seedsigner/views/seed_views.py b/src/seedsigner/views/seed_views.py index e938da49..65830d37 100644 --- a/src/seedsigner/views/seed_views.py +++ b/src/seedsigner/views/seed_views.py @@ -226,11 +226,13 @@ class SeedMnemonicEntryView(View): ) if ret == RET_CODE__BACK_BUTTON: - if self.cur_word_index > 0: - return Destination(BackStackView) - else: + # This handles two possible scenarios: + # 1. Backing out of the first word cancels the mnemonic entry process; + # return to whichever `View` routed us here initially. + # 2. Backing out of the current word returns to the previous word. + if self.cur_word_index == 0: self.controller.storage.discard_pending_mnemonic() - return Destination(MainMenuView) + return Destination(BackStackView) # ret will be our new mnemonic word self.controller.storage.update_pending_mnemonic(ret, self.cur_word_index) @@ -339,9 +341,6 @@ class SeedFinalizeView(View): elif button_data[selected_menu_num] == self.PASSPHRASE: return Destination(SeedAddPassphraseView) - elif selected_menu_num == RET_CODE__BACK_BUTTON: - return Destination(BackStackView) - class SeedAddPassphraseView(View): diff --git a/src/seedsigner/views/tools_views.py b/src/seedsigner/views/tools_views.py index b2c6b9fb..798cf5ba 100644 --- a/src/seedsigner/views/tools_views.py +++ b/src/seedsigner/views/tools_views.py @@ -1,6 +1,5 @@ import hashlib import logging -import os import time from gettext import gettext as _ @@ -148,9 +147,12 @@ class ToolsImageEntropyMnemonicLengthView(View): # Build in some hardware-level uniqueness via CPU unique Serial num try: - stream = os.popen("cat /proc/cpuinfo | grep Serial") - output = stream.read() - serial_num = output.split(":")[-1].strip().encode('utf-8') + serial_num = b'' + with open("/proc/cpuinfo", "r") as f: + for line in f: + if "Serial" in line: + serial_num = line.split(":")[-1].strip().encode('utf-8') + break serial_hash = hashlib.sha256(serial_num) hash_bytes = serial_hash.digest() except Exception as e: diff --git a/tests/test_flows_seed.py b/tests/test_flows_seed.py index e5f2c346..578f0094 100644 --- a/tests/test_flows_seed.py +++ b/tests/test_flows_seed.py @@ -495,6 +495,79 @@ class TestSeedFlows(FlowTest): +class TestSeedEntryBackFlows(FlowTest): + """ + Tests for every BACK exit scenario from SeedMnemonicEntryView and related views. + + A naive BackStackView swap can leave resume_main_flow dangling, causing + auto-redirects on stale flow state. These tests verify that BACK navigation + returns to the correct parent view AND that no flow state leaks. + """ + + def test_back_from_seed_entry_first_word(self): + """ + Pressing BACK on the first word of mnemonic entry should return to + the View that initiated the mnemonic entry process. + """ + for seed_type in [seed_views.LoadSeedView.TYPE_12WORD, seed_views.LoadSeedView.TYPE_24WORD]: + self.run_sequence([ + FlowStep(MainMenuView, button_data_selection=MainMenuView.SEEDS), + FlowStep(seed_views.SeedsMenuView, is_redirect=True), # No seeds loaded; auto-redirects to LoadSeedView + FlowStep(seed_views.LoadSeedView, button_data_selection=seed_type), + FlowStep(seed_views.SeedMnemonicEntryView, screen_return_value=RET_CODE__BACK_BUTTON), + FlowStep(seed_views.LoadSeedView), # Should land here, NOT MainMenuView + ]) + BaseTest.reset_controller() + + + def test_back_from_seed_entry_mid_word(self): + """ + Pressing BACK from a middle word (eg. word #2) should return to the + previous SeedMnemonicEntryView (eg. word #1) via the back stack. + """ + self.run_sequence([ + FlowStep(MainMenuView, button_data_selection=MainMenuView.SEEDS), + FlowStep(seed_views.SeedsMenuView, is_redirect=True), + FlowStep(seed_views.LoadSeedView, button_data_selection=seed_views.LoadSeedView.TYPE_12WORD), + FlowStep(seed_views.SeedMnemonicEntryView, screen_return_value="abandon"), # word #1 + FlowStep(seed_views.SeedMnemonicEntryView, screen_return_value=RET_CODE__BACK_BUTTON), # BACK from word #2 + FlowStep(seed_views.SeedMnemonicEntryView), # Returns to word #1 + ]) + + # Verify we're back on word #1: word at index 0 should still be set + # from the previous entry, while word at index 1 should be unset. + assert self.controller.storage.get_pending_mnemonic_word(0) == "abandon" + assert self.controller.storage.get_pending_mnemonic_word(1) is None + + + def test_back_from_seed_entry_via_seed_select(self): + """ + Backing out of mnemonic entry during an active flow must preserve + `resume_main_flow` so the user remains within that flow. + """ + from seedsigner.controller import Controller + from seedsigner.models.settings import SettingsConstants + + self.settings.set_value(SettingsConstants.SETTING__MESSAGE_SIGNING, SettingsConstants.OPTION__ENABLED) + + def load_signmessage_into_decoder(view): + view.decoder.add_data("signmessage m/84h/0h/0h/0/0 ascii:test message") + + self.run_sequence([ + FlowStep(MainMenuView, button_data_selection=MainMenuView.SCAN), + FlowStep(scan_views.ScanView, before_run=load_signmessage_into_decoder), + FlowStep(seed_views.SeedSignMessageStartView, is_redirect=True), + FlowStep(seed_views.SeedSelectSeedView, button_data_selection=seed_views.SeedSelectSeedView.TYPE_12WORD), + FlowStep(seed_views.SeedMnemonicEntryView, screen_return_value=RET_CODE__BACK_BUTTON), # BACK on first word + FlowStep(seed_views.SeedSelectSeedView), # Should return here, in the sign message flow + ]) + + # Verify resume_main_flow is still set β€” user is still in the sign message flow + assert self.controller.resume_main_flow == Controller.FLOW__SIGN_MESSAGE + + + + class TestMessageSigningFlows(FlowTest): MAINNET_DERIVATION_PATH = "m/84h/0h/0h/0/0" TESTNET_DERIVATION_PATH = "m/84h/1h/0h/0/0"