Skip to content

Krux Apps, small utility programs to enhance Krux#485

Open
tadeubas wants to merge 2 commits intoselfcustody:developfrom
tadeubas:kapps
Open

Krux Apps, small utility programs to enhance Krux#485
tadeubas wants to merge 2 commits intoselfcustody:developfrom
tadeubas:kapps

Conversation

@tadeubas
Copy link
Copy Markdown
Member

@tadeubas tadeubas commented Nov 30, 2024

What is this PR for?

Create a new Tool to enable the execution of developer's signed Krux apps (Kapps) as .mpy files in the SD card.

To test on the simulator, copy the .mpy file and its corresponding .mpy.sig into the simulator/sd folder. When executed, both files are automatically copied to simulator/flash. The simulator then emulates the execution of the selected Kapp by running the corresponding .py file from the simulator’s root directory. To avoid duplication, it’s recommended to create symbolic links to these .py files instead of copying them.

In any case you will need to generate a .mpy file using the tool firmware/MaixPy/components/micropython/core/mpy-cross. Follow its README.md instructions in order to make and then compile a .py file into .mpy. It is implied that all Kapps must implement the run() function to be executed.

You will also need to create a .sig file based on the .mpy. Remember to change the contents of the SIGNER_PUBKEY inside src\krux\metadata.py following instructions here To generate a keypair and Signing the firmware (firmware will be your .mpy file) in order to sign and verify the signature within Krux code (Ex: ./krux sign kapps/nostr.mpy privkey.pem).

Use this nice tool from @kdmukai to test Nostr Kapp: https://nostrtool.com/

It’s also implied (though we can change this later) that Kapps won’t be translated - existing firmware strings will be, but new ones won’t. I also think we need to distribute the .mpy files (and their signatures) with each release.

It is possible to keep more than one Kapp in flash memory. There is a security setting to enable the startup execution of a Kapp (before Krux firmware for OPSec), only Kapps that have the const ALLOW_STARTUP = True are listed there.

Other changes

  • New Kapp - k_QR: QR scanner for inconspicuous startup (OPSec) [scan QR with text: krux]
  • New Kapp - Steganography: Capture photos as bmp, view them and hide and revel data in the pixels of the img
  • Krux script: signing now produces only compact 70-byte signatures, preventing the 71- or 72-byte variants that embit would flag as invalid
  • pyproject.toml: New tasks to handle mpy generation (poetry run poe mpy-all)
  • Simulator: flash folder now simulates device's user flash space
  • firmaware.py: Refactored code to allow reuse of get_pubkey and check_signature
  • New settings: allow_kapp and startup_kapp

Changes made to:

  • Code
  • Tests
  • Docs
  • CHANGELOG

Did you build the code and tested on device?

  • Yes, build and tested on yahboom and others

What is the purpose of this pull request?

  • Bug fix
  • New feature
  • Docs update
  • Other

TODO LIST:

  • Create the Nostr Krux app for airgap signing (idea from @odudex) .
  • Reformulate Nostr Kapp to use NIP06 (mnemonic to nostr key) and NIP19 (nsec1...).
    • Allow Nostr Kapp to sign a Nostr event.
  • Create a C function in Maixpy to allow the Python code to change SPIFFS VFS exec_allowed attr.
  • Create test cases for Kapps:
    • Tests for kapp Nostr.
  • Create docs.
  • Add info to CHANGELOG.

@codecov
Copy link
Copy Markdown

codecov Bot commented Nov 30, 2024

Codecov Report

❌ Patch coverage is 89.39394% with 21 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.17%. Comparing base (5ae4ff5) to head (104b03f).
⚠️ Report is 13 commits behind head on develop.

Files with missing lines Patch % Lines
src/krux/pages/kapps.py 90.06% 15 Missing ⚠️
src/krux/krux_settings.py 45.45% 6 Missing ⚠️

❌ Your patch check has failed because the patch coverage (89.39%) is below the target coverage (95.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files
@@             Coverage Diff             @@
##           develop     #485      +/-   ##
===========================================
- Coverage    97.31%   97.17%   -0.15%     
===========================================
  Files           83       84       +1     
  Lines        10568    10749     +181     
===========================================
+ Hits         10284    10445     +161     
- Misses         284      304      +20     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@tadeubas
Copy link
Copy Markdown
Member Author

tadeubas commented Dec 13, 2024

Use my PUBKEY to test the signed apps (kapp, nostr) more easily, just change your metadata.py and flash the device:

SIGNER_PUBKEY = "0473d0a15dd45c336749dd548f73a2d3e4d4eb35a63bed31339959616a8b9d9e6e0060acc43bb915e89dc0d7f2b1c480be36d4f9883f1be1e525cddcd01d495805"

@joaozinhom
Copy link
Copy Markdown

This idea (krux apps) seems great to solve some open issues like this one #203 , krux devices have limited hardware in general, and probably for the future of the project we will need changes like this to allow continuous addition of features. some tests are broken in my machine, a notebook nix os ryzen 7 with 16 gb ram(I can run the krux main branch), i try by poetry and get some lib import errors, "No module named..."
image
The most part of the poetry commands return this warning probably the problem are in this location
image
if you want some help or i type some wrong information please let me know.

@tadeubas
Copy link
Copy Markdown
Member Author

Hi @j-moreno-c-r
It appears that you don't have the dependencies installed. In order to test the project plz follow the instructions here: https://github.com/selfcustody/krux?tab=readme-ov-file#development

I think you have to use this cmd before trying to execute tests: git submodule update --init --recursive

@joaozinhom
Copy link
Copy Markdown

ok sorry for the innatention, everithing run fine now

@tadeubas
Copy link
Copy Markdown
Member Author

tadeubas commented Oct 18, 2025

I’ve opened an issue for the Amber mobile app.
Another issue for nos2x Chrome Extension.
And other for nostr-login javascript lib for clients.

@tadeubas
Copy link
Copy Markdown
Member Author

Example of steganography app usage: https://excalidraw.com/#json=3C163DUVqLssMRl1_rl8A,nfbdz4uv1AY95q725OaZ-g

KEF-BMP.txt

@tadeubas
Copy link
Copy Markdown
Member Author

I’ve tested this extensively and believe the PR is ready to merge. I know that @jdlcdl has tested both the Nostr and Steganography Kapps, and another person has successfully tested only the Steganography Kapp without any issues.

This PR does not yet include the inconspicuous startup feature #203 ; we’ll need to discuss the best approach for implementing that separately.

@Pentecost1
Copy link
Copy Markdown

In the NosTR app, the fingerprint keeps blinking. I’m not sure if this is intentional behavior or if there’s something I haven’t understood yet

IMG_0614.MOV

@tadeubas
Copy link
Copy Markdown
Member Author

Thx @Pentecost1 you found a bug 👍 will fix ASAP

@tadeubas
Copy link
Copy Markdown
Member Author

tadeubas commented Jan 7, 2026

I discussed inconspicuous startup (#203) with other devs on Telegram. Since Kapps can do anything, it’s hard to guarantee the firmware remains intact after they run. I initially suggested disabling Kapp startup and forcing a reboot before entering the firmware, which would require users to re-enable the Kapp each time.

On further thought, we could instead allow only specific Kapps, those that don’t access or modify firmware code, to run at startup, and then safely continue to the firmware without disabling the Kapp or rebooting.

I’ll update this PR accordingly and add a small, simple Kapp for inconspicuous startup.

@qlrd
Copy link
Copy Markdown
Member

qlrd commented Jan 9, 2026

Pleeeeaase 🙏🏽 squash commits so i can review, is confusing to me 🥺.

Also we need to edit commits compliant to CI and appear to have some tests to make.

@tadeubas
Copy link
Copy Markdown
Member Author

tadeubas commented Jan 9, 2026

I understand the concern 🙂
Given that we already have around ~90% test coverage, I think the most valuable step at this point would be to go through the PR description and test the PR on the device to gather initial feedback.

Once that’s done, we can move on to refining commit messages, addressing CI expectations, and doing a more detailed code review if needed 😉

@qlrd
Copy link
Copy Markdown
Member

qlrd commented Jan 19, 2026

In any case you will need to generate a .mpy file using the tool firmware/MaixPy/components/micropython/core/mpy-cross. Follow its README.md instructions in order to make and then compile a .py file into .mpy. It is implied that all Kapps must implement the run() function to be executed.

It's worth to add some detailed information in #821 or followup?

@qlrd
Copy link
Copy Markdown
Member

qlrd commented Jan 25, 2026

Needs rebase and squash commits

@tadeubas tadeubas force-pushed the kapps branch 2 times, most recently from 5f50af2 to 31b2266 Compare January 25, 2026 20:16
@qlrd
Copy link
Copy Markdown
Member

qlrd commented Jan 25, 2026

Just some doc nits. Also wdyt about have more commits, but organized like as follow?

  • docs: CHANGELOG, images, md files;
  • chore(i18n): i18n files and scripts;
  • feat(simulator): simulator scripts, sequences;
  • feat: kapps files;
  • feat(test): tests;
  • feat(src): source files
  • feat(build): pyproject.toml, krux, .gitignore.

Comment thread src/krux/pages/kapps.py
Comment thread docs/getting-started/features/tools.en.md
Comment thread kapps/k_qr.py
Comment thread kapps/nostr.py
Comment thread kapps/nostr.py
Comment thread kapps/nostr.py
Comment thread kapps/steganography.py
Comment thread src/krux/pages/login.py
Comment thread .gitignore
Comment thread CHANGELOG.md
@tadeubas
Copy link
Copy Markdown
Member Author

Maybe the commits could be separated as follow, wdyt?

Thank you for the suggestion. At this point, the changes are complete, so I'll leave it as is.

Comment thread kapps/nostr.py
Comment on lines +793 to +794
sing_message = SignMessage(self.ctx)
data, qr_format, message_filename = sing_message._load_message()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
sing_message = SignMessage(self.ctx)
data, qr_format, message_filename = sing_message._load_message()
sign_message = SignMessage(self.ctx)
data, qr_format, message_filename = sign_message._load_message()

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice typo, will fix asap

@qlrd
Copy link
Copy Markdown
Member

qlrd commented Feb 3, 2026

Maybe the commits could be separated as follow, wdyt?

Thank you for the suggestion. At this point, the changes are complete, so I'll leave it as is.

Nice, is very neat work. Congrats!
It's better to resolve the suggestions so when merge they will not appear anymore.

@odudex
Copy link
Copy Markdown
Member

odudex commented Apr 22, 2026

First, sorry about taking so long to help moving this forward.

Now, catching up, we'll need a few changes, for which you can count on me helping to achieve them.

  • A rebase is needed to incorporate last changes, including security fixes which may interact with Kapps code.

  • I created a Kapps repository, and gave you maintainer privileges, so you can move apps there and become responsible for new submissions.
    (it's just a role, doesn't mean you'll have to maintain it alone, count me and others to help!)

  • You need to created a key, dedicated for Kapps, and add it to the firmware. As Kapps maintainer, you'll use it to sign the apps. We can't use same key which signs the main firmware.

  • Finally, I run an AI analysis and some issues were raised. Some of them are fixed but requirements above, and I believe most of the others also require being addressed:

    There are critical issues. The branch looks like it was never rebased on current develop, and in the
    process it reverts several recent security fixes. There are also real design issues with the new feature
    itself.

    A. Security fixes lost (likely accidental reverts vs. develop)

    The branch diverged before these merged and now deletes the protections:

    Fix on develop: PR fix: reject PSBT inputs with non-standard sighash types before signing #844 (e531811) — reject PSBT inputs with non-standard sighash
    What the kapps branch reverts: Removes check_sighash() call in src/krux/psbt.py:437
    Impact: Fund redirection: SIGHASH_NONE/SINGLE/ANYONECANPAY accepted again
    ────────────────────────────────────────
    Fix on develop: PR Fix: Reject multisig policies with m=0 or m>n and guard against ZeroDivisionError in fee calculation and  #845 (3398294) — multisig m=0 / m>n + ZeroDivisionError guard
    What the kapps branch reverts: src/krux/wallet.py (m<1, m>n), src/krux/psbt.py out_amount>0 guard
    Impact: Invalid multisigs accepted; crash/hang on zero-output PSBTs
    ────────────────────────────────────────
    Fix on develop: PR Message Signing Warning #846 (23bc73b) — warn before signing raw hashes
    What the kapps branch reverts: src/krux/pages/home_pages/sign_message_ui.py:234 strips the "Signing raw
    hash.
    Proceed only if you trust the source" prompt
    Impact: User can be tricked into signing arbitrary 32-byte hashes
    ────────────────────────────────────────
    Fix on develop: PR Security Audit: 8 Critical + 12 High findings (AI-assisted audit) #843/fix: add zip bomb protection and QR part limit enforcement #848 (bd95e82) — zip-bomb cap + QR part limits
    What the kapps branch reverts: src/krux/bbqr.py (part_total<1, DEFLATE size cap), src/krux/kef.py,
    src/krux/qr.py pMofN bounds
    Impact: DoS via malicious QR
    ────────────────────────────────────────
    Fix on develop: sign_release.py
    What the kapps branch reverts: Drops maixpy_embed_fire and maixpy_wonder_k
    Impact: Release for those devices would be skipped

    Accompanying test files (test_psbt.py, test_wallet.py, test_bbqr.py, test_kef.py, test_settings.py,
    shared_mocks.py) have the corresponding tests deleted — so CI won't catch the regressions. Rebase on develop
    before merging.

    B. Kapps design issues worth addressing

    1. Startup kapp bypasses signature verification entirely. src/boot.py:138-151 →
      Kapps.execute_flash_kapp(app_name, prompt=False). The only signature gate is parse_all_flash_apps()
      (src/krux/pages/kapps.py:64), called from Kapps.init, which is NOT on the startup path —
      execute_flash_kapp itself just imports and runs. Anyone with flash write (malware, another bug, physical)
      can swap /flash/.mpy and it runs without a re-check on every boot.
    2. Startup kapp runs before tc_code_verification and the TC Flash Hash boot check (boot.py:160 vs 178). This
      defeats the flash-integrity feature precisely in the window it was supposed to cover.
    3. Startup kapp does not restart after execution (kapps.py:162-163 if not prompt: return None). The comment
      "better safe than sorry" rationale for a restart is dropped exactly where it matters most — a
      misbehaving/bad kapp leaves a poisoned Python/module state that then drives login, TC check, and wallet
      handling.
    4. Same SIGNER_PUBKEY for firmware and kapps (metadata.py:23, firmware.py:31). No privilege separation;
      signer-key compromise gives both firmware and kapp execution. Consider a distinct kapp pubkey (and ideally a
      per-kapp allowlist/hash pinning).
    5. No sys.modules cleanup after execute_flash_kapp (contrast with load_sd_kapp at kapps.py:289-291). Kapp
      module stays loaded; any monkey-patching by the kapp persists into the rest of the session.
    6. vfs.exec_allowed(True) with chdir to /flash lets the kapp import any other .mpy/.py in flash, not
      just itself. parse_all_flash_apps tidies the menu path but the startup path never runs it.
    7. Bare except: around signature verification (kapps.py:88) silently classifies crypto errors as "unsigned"
      → user is prompted to delete, not warned of a crypto failure.
    8. Firmware upgrade is skipped when a startup_kapp is set — check_for_updates() sits inside the if not
      startup_kapp(ctx): branch (boot.py:160-163), so SD firmware updates require disabling the kapp first.
    9. TOCTOU between sha256(sd_path+filename) and the subsequent copy from SD (kapps.py:188 vs 245-250) — hash
      the bytes you actually write, or re-verify the signature against the flash copy before executing.

    Minor: startup.json list is append-only with no cleanup when the underlying .mpy is deleted; prompt text
    "will change flash memory and TC hash" misleads when TC hash isn't enabled; no .mpy size cap.

    Recommendation

    Block on (A) — rebase on develop and restore the deleted security tests; those fixes are not optional. For
    (B), at minimum require 1, 2, 3, and 5 before shipping: always re-verify the signature inside
    execute_flash_kapp (both paths), run the startup kapp after TC/flash-hash checks (or explicitly document the
    trade-off in the UI), force restart after any kapp execution, and clean sys.modules. A distinct kapp
    signing key is the big remaining design question.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants