Skip to content

gui: add air-gapped signer support over QR and microSD - #2243

Open
BitcoinQnA wants to merge 2 commits into
wizardsardine:masterfrom
Foundation-Devices:codex/passport-integration
Open

gui: add air-gapped signer support over QR and microSD#2243
BitcoinQnA wants to merge 2 commits into
wizardsardine:masterfrom
Foundation-Devices:codex/passport-integration

Conversation

@BitcoinQnA

@BitcoinQnA BitcoinQnA commented Aug 13, 2026

Copy link
Copy Markdown

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:

  • import a public BIP48 account during wallet creation using crypto-account QR or a descriptor-key file;
  • register the completed wallet policy using animated QR or JSON on microSD;
  • verify receive addresses using a request/response QR exchange;
  • export and re-import PSBTs using crypto-psbt QR or binary PSBT files on microSD;
  • reconstruct eligible air-gapped signer records for wallets created before this metadata existed.

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

  • Imported account data must match the complete fingerprint, BIP48 origin, network, and account number. Fingerprint-only matching is not accepted.
  • Wallet-policy identity is derived from the canonical network, descriptor template, and keys; wallet aliases do not affect it.
  • Address responses are bound to the active network, policy ID, descriptor checksum, branch, index, independently derived address, and signer fingerprint.
  • Returned PSBTs cannot replace Liana's transaction record. Liana requires the same unsigned transaction and input/output counts, preserves its original non-signature fields, and merges only valid new signatures from expected keys.
  • Animated payloads and camera sessions have explicit size, fragment-count, and time limits.
  • Camera frames remain in memory and the stream is released on success, cancellation, timeout, error, modal close, or drop.
  • Animated PSBT strings owned by the QR presenter are zeroized when released.

Protocols and formats

The implementation uses:

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

  • macOS uses AVFoundation capture with a negotiated 720p session;
  • Windows uses Media Foundation through Nokhwa;
  • Linux uses V4L2 through Nokhwa;
  • Quirc is the fast QR-decoding path, with RXing as an inverted/difficult-image fallback;
  • the preview is mirrored for natural interaction, while decoding uses the unmirrored frame;
  • multipart scanning reports decoder progress rather than only displaying a live preview.

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:

Dependency License Purpose
foundation-ur 0.4.0 MIT BC-UR bytewords and fountain coding
minicbor 0.24.4 BlueOak-1.0.0 bounded registry-CBOR parsing and encoding
nokhwa 0.10.11 Apache-2.0 camera enumeration, permissions, and Windows/Linux capture
quircs 0.10.3 MIT fast in-memory QR decoding
rxing 0.9.2 Apache-2.0 inverted and difficult-image QR fallback
zeroize 1.8.1 MIT OR Apache-2.0 clearing owned animated PSBT strings
nokhwa-bindings-macos 0.2.4 Apache-2.0 negotiated AVFoundation capture on macOS
flume 0.11.1 MIT OR Apache-2.0 AVFoundation callback transport on macOS
objc 0.2.7 MIT typed AVFoundation session-preset messages on macOS
qrcode 0.14.1 MIT OR Apache-2.0 deterministic synthetic QR frames in tests only

Documentation

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;
  • macOS camera, animated-QR, microSD, policy-registration, address-verification, and PSBT-signing workflows were exercised with an unreleased Passport Core firmware build;
  • the Cargo advisory set was compared with the upstream base and this PR introduces no additional advisories.

The full workspace Clippy invocation still reports pre-existing warnings in lianad (unused testutils::* and a dead new_server helper); 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

  • independent maintainer review of the protocol, PSBT merge rules, persistence, and dependency additions;
  • physical Passport Prime interoperability testing;
  • physical camera smoke tests on Windows and Linux;
  • normal project release/signing validation in the maintainers' environment.

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.

@BitcoinQnA
BitcoinQnA force-pushed the codex/passport-integration branch from b5457c7 to b5526b1 Compare August 13, 2026 12:28
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.
@BitcoinQnA
BitcoinQnA force-pushed the codex/passport-integration branch from b5526b1 to e96c8b3 Compare August 13, 2026 12:42
@qlrd

qlrd commented Aug 13, 2026

Copy link
Copy Markdown

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 :)

@BitcoinQnA
BitcoinQnA marked this pull request as ready for review August 13, 2026 13:28
@BitcoinQnA

Copy link
Copy Markdown
Author

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.

@pythcoiner

Copy link
Copy Markdown
Collaborator

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

@pythcoiner

Copy link
Copy Markdown
Collaborator

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)

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.

3 participants