gui: add air-gapped signer support over QR and microSD - #2243
Conversation
b5457c7 to
b5526b1
Compare
Add generic air-gapped signer workflows to the desktop app for account import, wallet-policy registration, address verification, and PSBT signing over animated BC-UR or microSD. Passport Core and Prime provide the initial interoperable protocol implementation. Use bounded camera capture and decoding, visible scan progress, three distinct QR density presets, strict payload and fragment limits, and signature-only PSBT merging. Persist only public signer configuration and require explicit registration confirmation. Document the protocol, dependency purposes and licenses, security bounds, and manual test flow. Add fixtures and tests for UR, CBOR accounts, registration, address verification, multipart scanning, PSBT validation, cancellation, and density selection, plus the minimal macOS camera packaging and Linux build prerequisites.
Keep address verification on the QR transport implemented by the signer, add the Linux bindgen release dependency, document protocol provenance and physical validation status, and remove unrelated window-setting churn.
b5526b1 to
e96c8b3
Compare
|
while i like the concept (but need to study the code), the microSD flow is already used on krux (see #2240) in different way stated in this PR. Since this i will just wait for reviewers words :) |
Yes to be clear the microSD flow I demonstrated is using the public build of Liana. This PR only concerns adding QR code support for Passport, but also for any other QR based signer that adopts the same standards. This covers Seed Signer, Krux, Jade and Specter DIY. |
|
Thanks @BitcoinQnA for looking into it, FYI I've an wip implem of https://delvingbitcoin.org/t/qr-based-signing-flow-payloads-in-miniscript-context/2464 that I'd like to use for the QR Code implem in Liana but I'll try to find some time to test this PR next week |
|
Personal note: I'd like to keep away from BC-UR, I understand fountain-codes can be interesting, but I dont think it worth the cost of the BC-UR complexity. (And I've a strong bias against BC-UR as it's a shitcoin-friendly protocol tbh) |
Summary
This adds an air-gapped signer workflow to the Liana desktop application using animated QR codes or microSD, while leaving the existing USB hardware-wallet workflow unchanged.
The implementation was developed against Passport, but the desktop UI and exchange state are intentionally described in device-neutral terms so other compatible signers can implement the same wire contract.
Supported workflows are:
crypto-accountQR or a descriptor-key file;crypto-psbtQR or binary PSBT files on microSD;QR presentation includes Very low, Low, and Standard density settings. Lower-density settings use more frames with simpler QR symbols for cameras that struggle with dense codes.
Why
Liana currently expects signing devices to be connected through its hardware-wallet interface. Air-gapped devices instead require an asynchronous exchange: Liana presents or exports a request, the signer reviews and processes it, and Liana scans or imports the response.
This PR adds that exchange without representing an air-gapped signer as a USB device or altering the connected-device path.
Security and data handling
Protocols and formats
The implementation uses:
crypto-account/crypto-psbtregistry structures.Wallet-policy registration and address verification use versioned Foundation-defined JSON envelopes carried as
ur:bytes. These two envelopes are integration-specific wire formats, not external standards. Their canonical fields, identity calculation, validation rules, and versioning requirements are documented in the protocol document below.Liana uses one air-gap protocol path for this integration; there is no separate Passport Core or Passport Prime path in the desktop application. A matching Passport Prime implementation exists in KeyOS development and uses the same wire contract and cross-implementation vectors, but it has not been validated on physical Passport Prime hardware.
Camera implementation
The same Rust scanner and protocol state are compiled for all three desktop targets. Physical camera testing in this work was performed only on macOS; Windows and Linux still require physical camera smoke tests before release.
Added direct dependencies
All added direct dependencies are permissively licensed and locked in
Cargo.lock:foundation-ur0.4.0minicbor0.24.4nokhwa0.10.11quircs0.10.3rxing0.9.2zeroize1.8.1nokhwa-bindings-macos0.2.4flume0.11.1objc0.2.7qrcode0.14.1Documentation
Demonstrations
I have made two short end-to-end videos:
Both demonstrations were recorded on macOS using the forked Liana application in this PR and an unreleased Passport Core firmware build. They are not evidence of physical Passport Prime, Windows, or Linux validation.
Validation performed
cargo test --workspace— 364 tests passed;cargo test --workspace --no-default-features— 364 tests passed;cargo clippy -p liana-gui --all-features --all-targets -- -D warnings— passed;cargo fmt --all -- --check— passed;git diff --check— passed;nix build .#liana.x86_64-pc-windows-gnu --no-link— passed;The full workspace Clippy invocation still reports pre-existing warnings in
lianad(unused testutils::*and a deadnew_serverhelper); this PR does not change those unrelated files. The complete release aggregation was also attempted, but the macOS build requires the repository's existing manually supplied Xcode 12.2 Nix input. Guix manifest syntax was validated, but a physical Linux camera test was not available in this environment.Validation still required before release
Disclosure
I am not a software engineer. This pull request was developed and reviewed with the assistance of OpenAI's GPT-5.6-Sol coding agent. I have personally tested the described workflows on macOS using an unreleased Passport Core firmware build, but the Liana maintainers should independently review the implementation, security assumptions, and test coverage before merging.