Fix and update device docs, datum sequence, and images (#748)
What changed, and why it matters
This commit is a routine maintenance patch: it moves the 'Check SD Card' feature from the Tools menu into the Device Tests menu, updates user documentation and screenshots, refreshes simulator button sequences, and adds unit tests. There is no security-relevant code change and no indication of a vulnerability being fixed.
No security action required; treat as normal documentation and UI refactor review.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff shows a pure refactor/reorganization. The sd_check() method and its menu entry are relocated from src/krux/pages/tools.py to src/krux/pages/device_tests.py with no functional changes to the SD-card detection, filesystem statistics, or file-manager invocation logic. Documentation and simulator sequences are updated to reflect the new menu location. Tests are moved/added accordingly. No cryptographic, input-validation, or privilege logic is modified.
Changed components
src/krux/pages/tools.pysrc/krux/pages/device_tests.pydocs/getting-started/features/tools.en.mdsimulator/sequences/tools-check-sd.txtsimulator/sequences/tools-datum-tool.txttests/pages/test_tools.pytests/pages/test_device_tests.pyInspect captured patch +169 / −129
diff --git a/docs/getting-started/features/encryption/encryption.en.md b/docs/getting-started/features/encryption/encryption.en.md
index 6c41bcf..d293453 100644
--- a/docs/getting-started/features/encryption/encryption.en.md
+++ b/docs/getting-started/features/encryption/encryption.en.md
@@ -48,11 +48,11 @@ Modes ECB, CBC, and GCM use an Initialization Vector (IV), where IV is better te
## Key Stretching (PBKDF2 Iterations)
-When you enter the encryption key, it is not directly used to encrypt your data. In order to protect against brute force attacks, the user supplied key is derived multiple times -- stretched to 256 bits via `pbkdf2_hmac_sha256`. PBKDF2 (Password-Based Key Derivation Function) Iterations refer to the number of derivations that will be performed over your key -- as the `password` -- `salted` with an ID, prior to encrypting/decrypting your secret. Users may set a preferred `PBKDF2 Iterations` value in `Encryption Settings`, then Krux will propose a slightly different value -- within a 10% delta, whenever encrypting.
+When you enter the encryption key, it is not directly used to encrypt your data. In order to protect against brute force attacks, the user supplied key is derived multiple times - stretched to 256 bits via `pbkdf2_hmac_sha256`. PBKDF2 (Password-Based Key Derivation Function) Iterations refer to the number of derivations that will be performed over your key - as the `password` - `salted` with an ID, prior to encrypting/decrypting your secret. Users may set a preferred `PBKDF2 Iterations` value in `Encryption Settings`, then Krux will propose a slightly different value - within a 10% delta, whenever encrypting.
## KEF Encryption Format
-When Krux encrypts a secret, the result is a `KEF Envelope` -- which is a series of bytes. Each envelope is constructed similarly, containing fixed-length and variable-length fields representing: a custom `ID` for the envelope, a `Version`, number of PBKDF2 `Iterations`, and a `Cipher PayLoad`, so that any devices or software supporting KEF may recognize the envelope and know how to decrypt it -- given the correct `key`. These fields, within each KEF envelope are:
+When Krux encrypts a secret, the result is a `KEF Envelope` - which is a series of bytes. Each envelope is constructed similarly, containing fixed-length and variable-length fields representing: a custom `ID` for the envelope, a `Version`, number of PBKDF2 `Iterations`, and a `Cipher PayLoad`, so that any devices or software supporting KEF may recognize the envelope and know how to decrypt it - given the correct `key`. These fields, within each KEF envelope are:
| ID length (1) | ID (2) | Version (3) | Key Derivations (4) | Cipher PayLoad (5, 6, and 7) |
| :---: | :---: | :---: | :---: | :---: |
@@ -69,7 +69,7 @@ When Krux encrypts a secret, the result is a `KEF Envelope` -- which is a series
* **(7)** Authentication/validation data (3, 4, or 16 bytes).
### Version Details
-While all KEF envelopes share the above format, each version differs -- offering choices to the user, as trade-offs that may better fit a particular use-case. For technical details, see: [KEF specifications](kef-specifications.md)
+While all KEF envelopes share the above format, each version differs - offering choices to the user, as trade-offs that may better fit a particular use-case. For technical details, see: [KEF specifications](kef-specifications.md)
| Version | Name | Mode | IV | Compressed | Intended Use Case |
|---------|------------|------|----|------------|----------------------------------------|
diff --git a/docs/getting-started/features/encryption/kef-specifications.en.md b/docs/getting-started/features/encryption/kef-specifications.en.md
index d68d77c..f06b030 100644
--- a/docs/getting-started/features/encryption/kef-specifications.en.md
+++ b/docs/getting-started/features/encryption/kef-specifications.en.md
@@ -1,17 +1,17 @@
-# KEF Encryption Format -- Technical Specification
+# KEF Encryption Format - Technical Specification
-...`The K stands for "KEF"` --anon
+...`The K stands for "KEF"` -anon
## Motivation
In the autumn of 2023, during the lead-up to **krux release 23.09.0**, contributors proposed a method of encrypting bip39 mnemonics that could be stored in SPI-flash, on sdcard, and/or exported to QR. Regarding the encrypted-mnemonic QR format: the layout proposed was interesting as an extensible, lite-weight, self-describing envelope that has been appreciated by users ever since.
-...`"Wen passphrases, output descriptors, PSBTs, and notes?"` --plebs
+...`"Wen passphrases, output descriptors, PSBTs, and notes?"` -plebs
This specification, and its accompanying implementation and test-suite are the result of months of exploration into improvements meant to better define, test, and extend the original encryption format that we'll refer to as KEF. It proposes ten new versions, extending its usefulness to more than mnemonics, targeting variable-length strings up to moderately sized PSBTs, flexibility to choose among four AES modes of operation, with-or-without compression, and versions optimized to result in a smaller envelope.
-Above all, this specification aims to be supported by as many projects as would consider adopting it, so that users are not "locked" into a particular project when recovering their secrets. Corrections and refinement to, and scrutiny of this specification are appreciated. Proposals for more `versions` are welcome, provided they offer "value" to the user and fit within the scope of this system. Once released, because it cannot be known how many KEF envelopes may exist in-the-wild, changes to any particular version must remain backwards compatible for decryption. Adopting implementations are free to support any KEF versions they wish to support, for decryption-only or for both encryption and decryption -- with the expectation that claims-of-support made are clear and precise about what is supported.
+Above all, this specification aims to be supported by as many projects as would consider adopting it, so that users are not "locked" into a particular project when recovering their secrets. Corrections and refinement to, and scrutiny of this specification are appreciated. Proposals for more `versions` are welcome, provided they offer "value" to the user and fit within the scope of this system. Once released, because it cannot be known how many KEF envelopes may exist in-the-wild, changes to any particular version must remain backwards compatible for decryption. Adopting implementations are free to support any KEF versions they wish to support, for decryption-only or for both encryption and decryption - with the expectation that claims-of-support made are clear and precise about what is supported.
## Overview
@@ -56,21 +56,21 @@ Authentication has three forms:
## Generalizations Regarding Implementation
-It is expected that any implementation can decrypt a KEF envelope that was created by itself on the same device. Implementations are asked to make their "best-effort" to be capable of decrypting KEF envelopes for versions they support which were created by other implementations or on other devices -- but this will not always be possible. Decrypting large KEF envelopes on severely constrained devices, or ones created with flawed implementations is unrealistic. Therefore, in such cases it is the responsibility of the user to find an implementation and device capable of decrypting their KEF envelope, or to have a non-KEF form of recovery.
+It is expected that any implementation can decrypt a KEF envelope that was created by itself on the same device. Implementations are asked to make their "best-effort" to be capable of decrypting KEF envelopes for versions they support which were created by other implementations or on other devices - but this will not always be possible. Decrypting large KEF envelopes on severely constrained devices, or ones created with flawed implementations is unrealistic. Therefore, in such cases it is the responsibility of the user to find an implementation and device capable of decrypting their KEF envelope, or to have a non-KEF form of recovery.
-* Be strict while encrypting. Be tolerant -- and non-specific about errors, when decrypting.
+* Be strict while encrypting. Be tolerant - and non-specific about errors, when decrypting.
-* At its base, **a KEF envelope is a format of bytes -- so are all of its inputs**. Remember this when converting strings gathered for the `key` and `id`. Consider being strict about offering a reasonably minimal set of characters, common and available on other devices and/or international keyboards when encrypting -- then encode unicode codepoints (if not ascii) directly to their utf-8 representations without normalization. For decryption, more characters could be offered when gathering the `key`, and multiple normalization strategies may be tried, so that secrets may be recovered. Consider some capability of displaying both `key` and `id` as bytes, and gathering the `key` as bytes either directly or via hex/base64 conversion if necessary, to enable recovery. Do NOT assume that a user originally used a particular implementation to encrypt a KEF envelope.
+* At its base, **a KEF envelope is a format of bytes - so are all of its inputs**. Remember this when converting strings gathered for the `key` and `id`. Consider being strict about offering a reasonably minimal set of characters, common and available on other devices and/or international keyboards when encrypting - then encode unicode codepoints (if not ascii) directly to their utf-8 representations without normalization. For decryption, more characters could be offered when gathering the `key`, and multiple normalization strategies may be tried, so that secrets may be recovered. Consider some capability of displaying both `key` and `id` as bytes, and gathering the `key` as bytes either directly or via hex/base64 conversion if necessary, to enable recovery. Do NOT assume that a user originally used a particular implementation to encrypt a KEF envelope.
* **On the importance of a STRONG user-supplied `key`** This cannot be stressed enough to each user of KEF. While KEF allows for key-stretching via `id` and `iterations`, and offers modes that require a random `IV` / Nonce, **KEF offers no expectation of security for a weak user-supplied `key`**. Consider making this point clear to users before encrypting and/or offer an indication of `key` strength once gathered. If a KEF envelope has been created with a "weak" `key` and stored accessible to others, user should assume that their secret has been leaked. Consider encouraging users to make sane choices about the characters they use in their `key`, aware that non-ascii characters offered by one implementation may not be easy to enter on another, or that a recognizable glyph may not exist on other devices for them to verify their `key` when decrypting.
* **On security** Not all KEF `versions` offer the same security guarantees, so implementors MUST take care to protect against "unsafe" usage. As already mentioned: be strict and fail to encrypt when "unsafe"; be tolerant and vague while decrypting. Support for decrypt-only on a particular version is perfectly valid should an implementation choose to "nudge" users towards a more-secure version where it supports full encrypt/decrypt functionality.
- * **On mode ECB**: Repeated blocks would leak patterns within ciphertext. Therefore, be strict -- refuse to encrypt using mode ECB whenever duplicate blocks are detected. Consider a compressed version which may resolve this.
+ * **On mode ECB**: Repeated blocks would leak patterns within ciphertext. Therefore, be strict - refuse to encrypt using mode ECB whenever duplicate blocks are detected. Consider a compressed version which may resolve this.
* **On block modes with NUL padding** Problems to unpad can arise decrypting where valid NUL bytes are confused with removable padding.
- * If `auth` is appended to plaintext before padding AND the `auth` bytes end in 0x00: be strict -- refuse to encrypt. Consider a version with safe padding.
- * If `auth` is appended to ciphertext after padding/encryption AND the `plaintext` bytes end in 0x00: be strict -- refuse to encrypt. Consider a version with safe padding.
+ * If `auth` is appended to plaintext before padding AND the `auth` bytes end in 0x00: be strict - refuse to encrypt. Consider a version with safe padding.
+ * If `auth` is appended to ciphertext after padding/encryption AND the `plaintext` bytes end in 0x00: be strict - refuse to encrypt. Consider a version with safe padding.
* Do not assume that other implementations adhere to the above. Be tolerant and make reasonable efforts to successfully recover secrets when decrypting. Offering a warning to users AFTER successful decryption in this case may be appropriate.
* **On modes that require IV or Nonce** Take precautions to ensure that this value is random and not reused. ie: Natural entropy captured from camera sensor (user validated and/or analyzed to ensure sensor is working / high entropy).
@@ -82,7 +82,7 @@ It is expected that any implementation can decrypt a KEF envelope that was creat
4. if parsing succeeds without errors, it is likely to be a KEF envelope and a decryption user-interface should be offered to the user.
While the user likely knows, the process instance of a KEF implementation will learn definitively, only AFTER a successful decryption, that a bytestring was indeed a KEF envelope. If at any point along this process, an implementation finds that `version` is unknown/disabled, or if parsing fails, the expected action is NOT TO RAISE SPECIFIC ERRORS regarding this inspection. Rather, the appropriate action is to assume it was not a KEF envelope and to treat the data under another context: ie: "Unknown". Similarly, as mentioned above, being vague about errors during decryption implies that "Failed!" may be a sufficient response for any error, instead of leaking to a potential attacker specific details about the failure.
-* **On Iterations** Consider that users may want to decrypt KEF envelopes on various resource-constrained devices. There is a minimum 10,000 iterations imposed in any KEF envelope (a value of 1 would be 10,000 pbkdf2_hmac iterations), and the maximum could be as high as 100,000,000 (a value of 10,000), but depending on the device used, 500,000 might be too high. Also, since the user-supplied `key` is stretched by this value, consider offering a range to users -- then adding a small `delta` as extra bits of entropy to derive different AES-256 keys that would otherwise be the same in the event the user re-uses the same `key`, `id` and `iterations` when creating many KEF envelopes.
+* **On Iterations** Consider that users may want to decrypt KEF envelopes on various resource-constrained devices. There is a minimum 10,000 iterations imposed in any KEF envelope (a value of 1 would be 10,000 pbkdf2_hmac iterations), and the maximum could be as high as 100,000,000 (a value of 10,000), but depending on the device used, 500,000 might be too high. Also, since the user-supplied `key` is stretched by this value, consider offering a range to users - then adding a small `delta` as extra bits of entropy to derive different AES-256 keys that would otherwise be the same in the event the user re-uses the same `key`, `id` and `iterations` when creating many KEF envelopes.
* **On truncated Authentication** At first glance it may be concerning that `auth` bytes for many versions have been truncated and are trivially "weak". Note that KEF's use-case for authentication is to validate that the user has correctly entered their decryption `key`. In the worse case, "false-authenticated" success will occur at a rate of 1:16M (or 1:4B for others) if using an incorrect decryption `key`; similar if an attacker has modified the KEF envelope. In these "false-authenticated" success cases, data will result from decryption, but that data will NOT be the original secret or plaintext; it will be of no value.
@@ -237,7 +237,7 @@ k: pbkdf2_hmac_sha256(K, id, i)
* **Authentication**: First 4 bytes of `SHA256(plaintext)`, hidden
* **cpl layout**: `[ciphertext]` (auth embedded after compression, before padding/encryption)
* **Use Case**: Mid-sized variable length plaintext
-* **Security Note**: like others, when encrypting: fail "unsafe" if duplicate blocks -- unlikely with compression
+* **Security Note**: like others, when encrypting: fail "unsafe" if duplicate blocks - unlikely with compression
---
@@ -446,13 +446,13 @@ k: pbkdf2_hmac_sha256(K, id, i)
Using examples from, and as an introduction to the reference [KEF implementation](https://github.com/selfcustody/krux/blob/develop/src/krux/kef.py), we'll quickly cover some basic concepts that may be helpful in getting started with your own KEF implementation.
### Version Configuration
-From the version details and summary table: note that all KEF versions can be defined as having a set of parameters which define that version's KEF rules. For ease-of-maintenance -- and also for extending later, it may be useful to store these in a central configuration. Within our sample reference, these are defined by constants `kef.VERSIONS`, `kef.MODE_NUMBERS` and `kef.MODE_IVS`.
+From the version details and summary table: note that all KEF versions can be defined as having a set of parameters which define that version's KEF rules. For ease-of-maintenance - and also for extending later, it may be useful to store these in a central configuration. Within our sample reference, these are defined by constants `kef.VERSIONS`, `kef.MODE_NUMBERS` and `kef.MODE_IVS`.
### Choosing a Version
As soon as you have data to hide, KEF offers choices for which version to use. That choice may be made by the user, or by the implementation, based on what is being hidden, compatibility with others, and how it may be stored/transported. The sample reference uses a function named `kef.suggest_versions()` to make a choice based on user's preferred mode-of-operation, the plaintext being hidden, then optimizes for a smaller KEF envelope.
### Encryption, Decryption, and Authentication
-Once you know what you need to hide and how you want to hide it, you'll need something to perform the encryption. You'll start by stretching the user-supplied `key`, salted with `id` for a number of `iterations` to **derive** the 256-bit AES key. Next you'll need to **encrypt** the plaintext (possibly with a random `IV` / Nonce) according to the chosen KEF version, so that the result is a cipher-payload `cpl`. To reverse this process, you'll need something to **decrypt** and **authenticate** the cipher-payload `cpl` -- again according to the rules of the particular KEF version. The sample reference uses a class named `kef.Cipher` for stretching the `key`, encrypting plaintext to `cpl`, and decrypting / authenticating `cpl` back into plaintext.
+Once you know what you need to hide and how you want to hide it, you'll need something to perform the encryption. You'll start by stretching the user-supplied `key`, salted with `id` for a number of `iterations` to **derive** the 256-bit AES key. Next you'll need to **encrypt** the plaintext (possibly with a random `IV` / Nonce) according to the chosen KEF version, so that the result is a cipher-payload `cpl`. To reverse this process, you'll need something to **decrypt** and **authenticate** the cipher-payload `cpl` - again according to the rules of the particular KEF version. The sample reference uses a class named `kef.Cipher` for stretching the `key`, encrypting plaintext to `cpl`, and decrypting / authenticating `cpl` back into plaintext.
### Padding and Unpadding
Depending on the mode-of-operation of your version, you may need to **pad** the plaintext. If so, there will also be a need to **unpad** during the decryption process. The sample reference uses functions named `kef.pad()` and `kef.unpad()`, which are called from inside the `kef.Cipher` object when encrypting and decrypting.
diff --git a/docs/getting-started/features/tools.en.md b/docs/getting-started/features/tools.en.md
index dfda41b..ca8236a 100644
--- a/docs/getting-started/features/tools.en.md
+++ b/docs/getting-started/features/tools.en.md
@@ -3,16 +3,6 @@ Here are some useful tools that are available as soon as Krux starts! These are
<img src="../../../img/maixpy_amigo/tools-options-300.png" class="amigo">
<img src="../../../img/maixpy_m5stickv/tools-options-250.png" class="m5stickv">
-### Check SD Card
-<img src="../../../img/maixpy_m5stickv/check-sd-card-250.png" align="right" class="m5stickv">
-<img src="../../../img/maixpy_amigo/check-sd-card-300.png" align="right" class="amigo">
-
-Verify whether your device detects and reads the SD card, and browse its contents. Files can be deleted individually. If there are more files than can fit on a single screen, swipe up :material-gesture-swipe-up: or down :material-gesture-swipe-down: to navigate between the screens - if your device has a touchscreen.
-
-The SD card is optional, but can be used for firmware upgrades and for storing settings, encrypted mnemonics, XPUBs, QR codes, and CNC/files. It is also useful for saving and loading PSBTs, wallet output descriptors, and messages.
-
-<div style="clear: both"></div>
-
### Datum Tool
<img src="../../../img/maixpy_m5stickv/tools-datum-tool-load-250.png" align="right" class="m5stickv">
<img src="../../../img/maixpy_amigo/tools-datum-tool-load-300.png" align="right" class="amigo">
@@ -24,7 +14,7 @@ Datum Tool is an advanced, educational feature that treats all input as a simple
<img src="../../../img/maixpy_m5stickv/tools-datum-tool-loaded-250.png" align="right" class="m5stickv">
<img src="../../../img/maixpy_amigo/tools-datum-tool-loaded-300.png" align="right" class="amigo">
-Once datum has been loaded, it offers meta information about the contents and ability to View Datum -- whether text or binary. It also offers a menu to Convert Datum, Export to QR, and Export to SD.
+Once datum has been loaded, it offers meta information about the contents and ability to View Datum - whether text or binary. It also offers a menu to Convert Datum, Export to QR, and Export to SD.
<div style="clear: both"></div>
@@ -39,7 +29,17 @@ The Convert Datum menu allows access to common encodings for conversion between
<img src="../../../img/maixpy_m5stickv/device-tests-options-250.png" align="right" class="m5stickv">
<img src="../../../img/maixpy_amigo/device-tests-options-300.png" align="right" class="amigo">
-Simple tests to verify correct functioning of your Krux device.
+Run these quick checks to ensure your Krux device is functioning properly.
+
+<div style="clear: both"></div>
+
+#### Check SD Card
+<img src="../../../img/maixpy_m5stickv/check-sd-card-250.png" align="right" class="m5stickv">
+<img src="../../../img/maixpy_amigo/check-sd-card-300.png" align="right" class="amigo">
+
+Verify whether your device detects and reads the SD card, and browse its contents. Files can be deleted individually. If there are more files than can fit on a single screen, swipe up :material-gesture-swipe-up: or down :material-gesture-swipe-down: to navigate between the screens - if your device has a touchscreen.
+
+The SD card is optional, but can be used for firmware upgrades and for storing settings, encrypted mnemonics, XPUBs, QR codes, and CNC/files. It is also useful for saving and loading PSBTs, wallet output descriptors, and messages.
<div style="clear: both"></div>
@@ -59,6 +59,13 @@ A suite of automated tests to verify important features on your device.
<div style="clear: both"></div>
+#### Touchscreen (Maix Amigo, Yahboom, WonderMV and TZT only)
+<img src="../../../img/maixpy_amigo/touch-test-300.png" align="right" class="amigo">
+
+Confirm that touch input is accurately detected across the entire display surface.
+
+<div style="clear: both"></div>
+
### Descriptor Addresses (Wallet Sans Key)
<img src="../../../img/maixpy_m5stickv/descriptor-addresses-250.png" align="right" class="m5stickv">
<img src="../../../img/maixpy_amigo/descriptor-addresses-300.png" align="right" class="amigo">
diff --git a/docs/getting-started/usage/navigating-the-main-menu.en.md b/docs/getting-started/usage/navigating-the-main-menu.en.md
index de2f626..6dc1b60 100644
--- a/docs/getting-started/usage/navigating-the-main-menu.en.md
+++ b/docs/getting-started/usage/navigating-the-main-menu.en.md
@@ -197,7 +197,7 @@ Bitcoin *BIP85* (aka Deterministic Entropy From BIP32 Keychains) allows for the
<div style="clear: both"></div>
-**BIP39 Mnemonic**
+##### BIP39 Mnemonic
<img src="../../../img/maixpy_amigo/bip85-child-index-300.png" class="amigo">
<img src="../../../img/maixpy_amigo/bip85-load-child-300.png" class="amigo">
@@ -208,7 +208,7 @@ Choose between *12 or 24 words*, then type the desired *index* to export a *chil
**Notice**: Any passphrase from the parent mnemonic will be removed when loading a BIP85 *child mnemonic*.
-**Base64 Password**
+##### Base64 Password
<img src="../../../img/maixpy_amigo/bip85-password-len-300.png" class="amigo">
<img src="../../../img/maixpy_amigo/bip85-password-created-300.png" class="amigo">
diff --git a/docs/img/maixpy_amigo/device-tests-options-300.en.png b/docs/img/maixpy_amigo/device-tests-options-300.en.png
index 448569c..ddc355a 100644
Binary files a/docs/img/maixpy_amigo/device-tests-options-300.en.png and b/docs/img/maixpy_amigo/device-tests-options-300.en.png differ
diff --git a/docs/img/maixpy_amigo/tools-datum-tool-convert-300.en.png b/docs/img/maixpy_amigo/tools-datum-tool-convert-300.en.png
index dcce446..997cd99 100644
Binary files a/docs/img/maixpy_amigo/tools-datum-tool-convert-300.en.png and b/docs/img/maixpy_amigo/tools-datum-tool-convert-300.en.png differ
diff --git a/docs/img/maixpy_amigo/tools-datum-tool-load-300.en.png b/docs/img/maixpy_amigo/tools-datum-tool-load-300.en.png
index 5a43de6..adaf2d7 100644
Binary files a/docs/img/maixpy_amigo/tools-datum-tool-load-300.en.png and b/docs/img/maixpy_amigo/tools-datum-tool-load-300.en.png differ
diff --git a/docs/img/maixpy_amigo/tools-datum-tool-loaded-300.en.png b/docs/img/maixpy_amigo/tools-datum-tool-loaded-300.en.png
index ef837a8..d36cecb 100644
Binary files a/docs/img/maixpy_amigo/tools-datum-tool-loaded-300.en.png and b/docs/img/maixpy_amigo/tools-datum-tool-loaded-300.en.png differ
diff --git a/docs/img/maixpy_amigo/tools-options-300.en.png b/docs/img/maixpy_amigo/tools-options-300.en.png
index 1440bdf..2297066 100644
Binary files a/docs/img/maixpy_amigo/tools-options-300.en.png and b/docs/img/maixpy_amigo/tools-options-300.en.png differ
diff --git a/docs/img/maixpy_amigo/touch-test-300.en.png b/docs/img/maixpy_amigo/touch-test-300.en.png
new file mode 100644
index 0000000..900ca59
Binary files /dev/null and b/docs/img/maixpy_amigo/touch-test-300.en.png differ
diff --git a/docs/img/maixpy_m5stickv/device-tests-options-250.en.png b/docs/img/maixpy_m5stickv/device-tests-options-250.en.png
index 86a0f3c..5c28000 100644
Binary files a/docs/img/maixpy_m5stickv/device-tests-options-250.en.png and b/docs/img/maixpy_m5stickv/device-tests-options-250.en.png differ
diff --git a/docs/img/maixpy_m5stickv/tools-datum-tool-convert-250.en.png b/docs/img/maixpy_m5stickv/tools-datum-tool-convert-250.en.png
index 8e91561..15f7b7e 100644
Binary files a/docs/img/maixpy_m5stickv/tools-datum-tool-convert-250.en.png and b/docs/img/maixpy_m5stickv/tools-datum-tool-convert-250.en.png differ
diff --git a/docs/img/maixpy_m5stickv/tools-datum-tool-load-250.en.png b/docs/img/maixpy_m5stickv/tools-datum-tool-load-250.en.png
index 6ccad52..3ccef45 100644
Binary files a/docs/img/maixpy_m5stickv/tools-datum-tool-load-250.en.png and b/docs/img/maixpy_m5stickv/tools-datum-tool-load-250.en.png differ
diff --git a/docs/img/maixpy_m5stickv/tools-datum-tool-loaded-250.en.png b/docs/img/maixpy_m5stickv/tools-datum-tool-loaded-250.en.png
index 0b9d644..e259db4 100644
Binary files a/docs/img/maixpy_m5stickv/tools-datum-tool-loaded-250.en.png and b/docs/img/maixpy_m5stickv/tools-datum-tool-loaded-250.en.png differ
diff --git a/docs/img/maixpy_m5stickv/tools-options-250.en.png b/docs/img/maixpy_m5stickv/tools-options-250.en.png
index f1e113e..9c39b5d 100644
Binary files a/docs/img/maixpy_m5stickv/tools-options-250.en.png and b/docs/img/maixpy_m5stickv/tools-options-250.en.png differ
diff --git a/simulator/generate-device-screenshots.sh b/simulator/generate-device-screenshots.sh
index 6f0baa2..812e36d 100755
--- a/simulator/generate-device-screenshots.sh
+++ b/simulator/generate-device-screenshots.sh
@@ -68,6 +68,7 @@ poetry run poe simulator --sequence sequences/sign-message.txt --sd --device $d
poetry run poe simulator --sequence sequences/sign-message-at-address.txt --device $device
# Tools
+poetry run poe simulator --sequence sequences/tools-datum-tool.txt --sd --device $device
poetry run poe simulator --sequence sequences/tools-check-sd.txt --sd --device $device
poetry run poe simulator --sequence sequences/tools-create-QR.txt --sd --device $device
# poetry run poe simulator --sequence sequences/tools-mnemonic.txt --sd --device $device
diff --git a/simulator/sequences/qrcodes/datum-example-qr.png b/simulator/sequences/qrcodes/datum-example-qr.png
new file mode 100644
index 0000000..874be6c
Binary files /dev/null and b/simulator/sequences/qrcodes/datum-example-qr.png differ
diff --git a/simulator/sequences/tools-check-sd.txt b/simulator/sequences/tools-check-sd.txt
index 2ae242c..00a0afe 100644
--- a/simulator/sequences/tools-check-sd.txt
+++ b/simulator/sequences/tools-check-sd.txt
@@ -6,6 +6,12 @@ press BUTTON_A
screenshot tools-options.png
+# Device tests
+press BUTTON_B
+press BUTTON_A
+
+screenshot device-tests-options.png
+
# Check SD Card
press BUTTON_A
diff --git a/simulator/sequences/tools-datum-tool.txt b/simulator/sequences/tools-datum-tool.txt
index 53bb59f..f9d0b53 100644
--- a/simulator/sequences/tools-datum-tool.txt
+++ b/simulator/sequences/tools-datum-tool.txt
@@ -5,19 +5,24 @@ x3 press BUTTON_B
press BUTTON_A
# Datum Tool
-press BUTTON_B
press BUTTON_A
-x2 press BUTTON_B
+
screenshot tools-datum-tool-load.png
-# Load File
-x3 press BUTTON_A
+# Load QR
+press BUTTON_A
+qrcode datum-example-qr.png
+# had to place a button first because after QR the screenshot was taken without menu on screen
+press_amigo_only BUTTON_B
screenshot tools-datum-tool-loaded.png
# Convert
-x2 press BUTTON_A
-x4 press BUTTON_B
+press BUTTON_B
+press BUTTON_A
+press BUTTON_B
+press BUTTON_A
+x6 press BUTTON_B
screenshot tools-datum-tool-convert.png
diff --git a/simulator/sequences/tools-print-test-qr.txt b/simulator/sequences/tools-print-test-qr.txt
index 8d90c37..bb0c04d 100644
--- a/simulator/sequences/tools-print-test-qr.txt
+++ b/simulator/sequences/tools-print-test-qr.txt
@@ -12,3 +12,18 @@ press BUTTON_A
press BUTTON_A
screenshot print-test-qr.png
+
+###########################
+# Amigo only touchscreen
+###########################
+
+# Cancel print Test QR
+press_amigo_only BUTTON_A
+x2 press_amigo_only BUTTON_B
+press_amigo_only BUTTON_A
+
+# Navigate to touchscreen
+x2 press_amigo_only BUTTON_B
+press_amigo_only BUTTON_A
+
+screenshot touch-test.png
diff --git a/src/krux/pages/device_tests.py b/src/krux/pages/device_tests.py
index 88d2c2a..739f599 100644
--- a/src/krux/pages/device_tests.py
+++ b/src/krux/pages/device_tests.py
@@ -32,6 +32,7 @@ class DeviceTests(Page):
def __init__(self, ctx):
menu_items = [
+ (t("Check SD Card"), self.sd_check),
(t("Print Test QR"), self.print_test),
(t("Test Suite"), self.test_suite),
]
@@ -46,6 +47,55 @@ class DeviceTests(Page):
)
self.results = []
+ def sd_check(self):
+ """Handler for the 'SD Check' menu item"""
+ import uos
+ from ..format import generate_thousands_separator
+ from ..sd_card import SDHandler
+ from .file_manager import SD_ROOT_PATH
+ from ..display import BOTTOM_PROMPT_LINE
+
+ self.ctx.display.clear()
+ self.ctx.display.draw_centered_text(t("Checking for SD card…"))
+ try:
+ # Check for SD hot-plug
+ with SDHandler():
+ sd_status = uos.statvfs(SD_ROOT_PATH)
+ sd_total_MB = int(sd_status[2] * sd_status[1] / 1024 / 1024)
+ sd_free_MB = int(sd_status[4] * sd_status[1] / 1024 / 1024)
+
+ self.ctx.display.clear()
+ self.ctx.display.draw_hcentered_text(
+ t("SD card")
+ + "\n\n"
+ + t("Size:")
+ + " "
+ + generate_thousands_separator(sd_total_MB)
+ + " MB"
+ + "\n\n"
+ + t("Used:")
+ + " "
+ + generate_thousands_separator(sd_total_MB - sd_free_MB)
+ + " MB"
+ + "\n\n"
+ + t("Free:")
+ + " "
+ + generate_thousands_separator(sd_free_MB)
+ + " MB",
+ highlight_prefix=":",
+ )
+ if self.prompt(t("Explore files?"), BOTTOM_PROMPT_LINE):
+ from .file_manager import FileManager
+
+ file_manager = FileManager(self.ctx)
+ file_manager.select_file(
+ select_file_handler=file_manager.show_file_details
+ )
+ except OSError:
+ self.flash_error(t("SD card not detected."))
+
+ return MENU_CONTINUE
+
def print_test(self):
"""Handler for the 'Print Test QR' menu item"""
title = t("Krux Printer Test QR")
diff --git a/src/krux/pages/file_manager.py b/src/krux/pages/file_manager.py
index e09c402..74d41e1 100644
--- a/src/krux/pages/file_manager.py
+++ b/src/krux/pages/file_manager.py
@@ -45,6 +45,7 @@ class FileManager(Page):
import os
path = SD_ROOT_PATH
+ status = ""
while True:
# if is a dir then list all files in it
if SDHandler.dir_exists(path):
diff --git a/src/krux/pages/tools.py b/src/krux/pages/tools.py
index 77cc904..4e4c650 100644
--- a/src/krux/pages/tools.py
+++ b/src/krux/pages/tools.py
@@ -20,7 +20,6 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-import uos
from . import (
Page,
Menu,
@@ -32,10 +31,6 @@ from . import (
# NUM_SPECIAL_1,
# NUM_SPECIAL_2,
)
-from .file_manager import SD_ROOT_PATH
-from ..format import generate_thousands_separator
-from ..sd_card import SDHandler
-from ..display import BOTTOM_PROMPT_LINE
from ..krux_settings import t
@@ -51,7 +46,6 @@ class Tools(Page):
Menu(
ctx,
[
- (t("Check SD Card"), self.sd_check),
(t("Datum Tool"), self.datum_tool),
(t("Device Tests"), self.device_tests),
# (t("Create QR Code"), self.create_qr),
@@ -72,49 +66,6 @@ class Tools(Page):
flash_tools.flash_tools_menu()
return MENU_CONTINUE
- def sd_check(self):
- """Handler for the 'SD Check' menu item"""
- self.ctx.display.clear()
- self.ctx.display.draw_centered_text(t("Checking for SD card…"))
- try:
- # Check for SD hot-plug
- with SDHandler():
- sd_status = uos.statvfs(SD_ROOT_PATH)
- sd_total_MB = int(sd_status[2] * sd_status[1] / 1024 / 1024)
- sd_free_MB = int(sd_status[4] * sd_status[1] / 1024 / 1024)
-
- self.ctx.display.clear()
- self.ctx.display.draw_hcentered_text(
- t("SD card")
- + "\n\n"
- + t("Size:")
- + " "
- + generate_thousands_separator(sd_total_MB)
- + " MB"
- + "\n\n"
- + t("Used:")
- + " "
- + generate_thousands_separator(sd_total_MB - sd_free_MB)
- + " MB"
- + "\n\n"
- + t("Free:")
- + " "
- + generate_thousands_separator(sd_free_MB)
- + " MB",
- highlight_prefix=":",
- )
- if self.prompt(t("Explore files?"), BOTTOM_PROMPT_LINE):
- from .file_manager import FileManager
-
- file_manager = FileManager(self.ctx)
- file_manager.select_file(
- select_file_handler=file_manager.show_file_details
- )
- except OSError:
- self.flash_error(t("SD card not detected."))
-
- return MENU_CONTINUE
-
def rm_stored_mnemonic(self):
"""Lists and allow deletion of stored mnemonics"""
from .encryption_ui import LoadEncryptedMnemonic
diff --git a/tests/pages/test_device_tests.py b/tests/pages/test_device_tests.py
index 0fc530d..19810f6 100644
--- a/tests/pages/test_device_tests.py
+++ b/tests/pages/test_device_tests.py
@@ -127,6 +127,51 @@ def mock_zlib_code(mocker):
)
+def test_sd_check_no_sd(m5stickv, mocker):
+ from krux.pages.device_tests import DeviceTests
+ from krux.input import BUTTON_PAGE
+ from unittest.mock import ANY
+
+ mocker.patch(
+ "uos.statvfs",
+ new=mocker.MagicMock(return_value=[0, 4096, 4096, 0, 1024]),
+ )
+ ctx = create_ctx(mocker, None)
+ tool = DeviceTests(ctx)
+ tool.flash_text = mocker.MagicMock()
+ tool.sd_check()
+ tool.flash_text.assert_has_calls([mocker.call("SD card not detected.", ANY)])
+
+
+def test_sd_check(m5stickv, mocker):
+ from krux.pages.device_tests import DeviceTests
+ from krux.input import BUTTON_ENTER, BUTTON_PAGE
+
+ BTN_SEQUENCE = [
+ BUTTON_ENTER, # explore files
+ ]
+ mocker.patch(
+ "uos.statvfs",
+ new=mocker.MagicMock(return_value=[0, 4096, 4096, 0, 1024]),
+ )
+ mocker.patch(
+ "os.listdir",
+ new=mocker.MagicMock(return_value=["somefile", "otherfile"]),
+ )
+ ctx = create_ctx(mocker, BTN_SEQUENCE)
+ tool = DeviceTests(ctx)
+ tool.sd_check()
+ ctx.display.draw_hcentered_text.assert_has_calls(
+ [
+ mocker.call(
+ "SD card\n\nSize: 16 MB\n\nUsed: 12 MB\n\nFree: 4 MB",
+ highlight_prefix=":",
+ )
+ ]
+ )
+ assert ctx.input.wait_for_button.call_count == len(BTN_SEQUENCE)
+
+
def test_printer_test_tool(amigo, mocker, mocked_print_qr):
"""Test that the print tool is called with the correct text"""
from krux.pages.device_tests import DeviceTests
diff --git a/tests/pages/test_login.py b/tests/pages/test_login.py
index db5e973..2023fba 100644
--- a/tests/pages/test_login.py
+++ b/tests/pages/test_login.py
@@ -118,11 +118,11 @@ def test_load_new_key_from_dice_module(m5stickv, mocker):
def test_tools_menu(m5stickv, mocker):
from krux.pages.login import Login, MENU_CONTINUE
- from krux.input import BUTTON_ENTER, BUTTON_PAGE
+ from krux.input import BUTTON_ENTER, BUTTON_PAGE_PREV
BTN_SEQUENCE = (
# Back
- [BUTTON_PAGE] * 6
+ [BUTTON_PAGE_PREV]
+ [BUTTON_ENTER]
)
diff --git a/tests/pages/test_tools.py b/tests/pages/test_tools.py
index 735db1d..c7d7edf 100644
--- a/tests/pages/test_tools.py
+++ b/tests/pages/test_tools.py
@@ -67,47 +67,6 @@ def test_delete_mnemonic_from_flash(m5stickv, mocker):
assert ctx.input.wait_for_button.call_count == len(BTN_SEQUENCE)
-def test_sd_check_no_sd(m5stickv, mocker):
- from krux.pages.tools import Tools
- from krux.input import BUTTON_PAGE
- from unittest.mock import ANY
-
- mocker.patch(
- "uos.statvfs",
- new=mocker.MagicMock(return_value=[0, 4096, 4096, 0, 1024]),
- )
- ctx = create_ctx(mocker, None)
- tool = Tools(ctx)
- tool.flash_text = mocker.MagicMock()
- tool.sd_check()
- tool.flash_text.assert_has_calls([mocker.call("SD card not detected.", ANY)])
-
-
-def test_sd_check(m5stickv, mocker, mock_file_operations):
- from krux.pages.tools import Tools
- from krux.input import BUTTON_PAGE
-
- BTN_SEQUENCE = [
- BUTTON_PAGE, # Leave
- ]
- mocker.patch(
- "uos.statvfs",
- new=mocker.MagicMock(return_value=[0, 4096, 4096, 0, 1024]),
- )
- ctx = create_ctx(mocker, BTN_SEQUENCE)
- tool = Tools(ctx)
- tool.sd_check()
- ctx.display.draw_hcentered_text.assert_has_calls(
- [
- mocker.call(
- "SD card\n\nSize: 16 MB\n\nUsed: 12 MB\n\nFree: 4 MB",
- highlight_prefix=":",
- )
- ]
- )
- assert ctx.input.wait_for_button.call_count == len(BTN_SEQUENCE)
-
-
def test_delete_mnemonic_from_sd(m5stickv, mocker, mock_file_operations):
from krux.pages.tools import Tools
from krux.input import BUTTON_ENTER, BUTTON_PAGE, BUTTON_PAGE_PREV
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.