Transfer ASCII-armored GPG keys by QR code #54

Closed
opened 2026-08-09 20:00:45 +00:00 by hugo · 2 comments
Owner

Goal

Transfer existing GPG public and private keys using QR codes containing their ASCII-armored representation.

Scope

  • Add camera scanning/import and explicit QR display/export flows for ASCII-armored public and private key material.
  • Preserve the exact armored bytes after transport; key parsing, fingerprinting, public/secret-key matching, passphrase validation, import, duplicate handling, and default-key selection belong to crates/storage.
  • Have storage define and validate the smallest versioned multipart envelope needed when an armored key does not fit in one QR code; Swift only scans/displays ordered opaque frames and progress.
  • Before importing or exporting private material, show an explicit security confirmation and the storage-derived fingerprint/identity; mask the camera/export view when capture/background state requires it as far as platform APIs allow.
  • Handle camera permission denial, duplicate/out-of-order/missing frames, invalid armor, public-only keys, mismatched key pairs, cancellation, and interrupted transfer safely.
  • Prove compatibility with fixtures generated as ASCII-armored GPG public and secret key exports without executing gpg at runtime.

Acceptance criteria

  • Single- and multipart QR fixtures reconstruct the exact ASCII-armored key data and import through storage.
  • Invalid, incomplete, duplicate, or mismatched transfers cannot replace an existing valid key configuration.
  • Private-key transfer requires explicit confirmation and never writes armor to logs, config, pasteboard, or Swift persistence.
  • The scanning/rendering adapter contains no GPG parser or key-domain policy.
## Goal Transfer existing GPG public and private keys using QR codes containing their ASCII-armored representation. ## Scope - Add camera scanning/import and explicit QR display/export flows for ASCII-armored public and private key material. - Preserve the exact armored bytes after transport; key parsing, fingerprinting, public/secret-key matching, passphrase validation, import, duplicate handling, and default-key selection belong to `crates/storage`. - Have storage define and validate the smallest versioned multipart envelope needed when an armored key does not fit in one QR code; Swift only scans/displays ordered opaque frames and progress. - Before importing or exporting private material, show an explicit security confirmation and the storage-derived fingerprint/identity; mask the camera/export view when capture/background state requires it as far as platform APIs allow. - Handle camera permission denial, duplicate/out-of-order/missing frames, invalid armor, public-only keys, mismatched key pairs, cancellation, and interrupted transfer safely. - Prove compatibility with fixtures generated as ASCII-armored GPG public and secret key exports without executing `gpg` at runtime. ## Acceptance criteria - Single- and multipart QR fixtures reconstruct the exact ASCII-armored key data and import through storage. - Invalid, incomplete, duplicate, or mismatched transfers cannot replace an existing valid key configuration. - Private-key transfer requires explicit confirmation and never writes armor to logs, config, pasteboard, or Swift persistence. - The scanning/rendering adapter contains no GPG parser or key-domain policy.
hugo added this to the 04 - iPhone and Apple Watch apps milestone 2026-08-09 20:00:45 +00:00
hugo added the enhancement label 2026-08-09 20:00:45 +00:00
hugo added the critical label 2026-08-11 18:02:42 +00:00
hugo added the untested label 2026-08-11 19:26:00 +00:00
Author
Owner

Implemented in commit 358ba7d and pushed to main.

Implementation:

  • Added storage-owned public/private ASCII-armored key export, full protected-secret passphrase validation, fingerprint/identity projection, duplicate and public-to-secret upgrade handling, and atomic 0600 persistence with default-key selection.
  • Added exact single-frame transfer and the versioned ISKT1 multipart envelope with SHA-256 integrity, bounded frame counts, out-of-order reconstruction, idempotent duplicates, and rejection of conflicts, mixed transfers, incomplete data, invalid armor, and multiple/mismatched keys.
  • Added mechanical UniFFI adapters that expose only storage-provided identity/progress and rendered QR modules; Swift contains no GPG parser or key policy and does not persist or copy armor.
  • Added native VisionKit camera permission/scanning, explicit private import/export confirmations, secure passphrase entry, public/private export selection, QR paging, Dynamic Type/accessibility labels, and camera/private-export capture and background shields following Apple HIG and platform APIs.
  • Reloads the Rust-backed app context immediately after a successful import.

Verified:

  • cargo fmt --all -- --check
  • RUSTFLAGS=-D warnings cargo check --workspace --all-targets
  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo test --workspace, including five focused key-transfer fixture tests
  • xcodegen generate
  • warning-as-error iPhone 17 Pro simulator Debug build
  • Xcode Analyze
  • final Debug simulator install
  • signed optimized generic iPhone Release build
  • plist and diff validation

Deferred interactive/hardware verification, tracked by the untested label:

  1. Visually inspect Preferences, key lists, confirmations, error states, single/multipart QR paging, light/dark appearance, and largest Dynamic Type on the iPhone 17 Pro simulator after an authenticated session is available.
  2. Verify VoiceOver labels, reading order, announcements, and QR paging.
  3. On the physical iPhone, test camera permission grant, denial, Settings recovery, camera interruption, cancellation, and actual public/private single/multipart scans including duplicate and out-of-order frames.
  4. Exercise wrong and correct passphrases, public/private/default imports, duplicate handling, and immediate in-app context reload through the UI.
  5. Verify exported QR scannability between devices and exact reconstructed armor end to end.
  6. Verify camera/private-key masking in the app switcher, background, screen recording, and AirPlay capture states.
  7. Install and launch the signed Release build on the paired iPhone and confirm the existing IronStorage app icon. The paired iPhone was unavailable when delivery completed.

Apple references consulted: HIG Privacy and Design Principles, VisionKit DataScannerViewController/scanning guidance, AVFoundation camera authorization, and UIScene capture-state protection.

Implemented in commit 358ba7d and pushed to main. Implementation: - Added storage-owned public/private ASCII-armored key export, full protected-secret passphrase validation, fingerprint/identity projection, duplicate and public-to-secret upgrade handling, and atomic 0600 persistence with default-key selection. - Added exact single-frame transfer and the versioned ISKT1 multipart envelope with SHA-256 integrity, bounded frame counts, out-of-order reconstruction, idempotent duplicates, and rejection of conflicts, mixed transfers, incomplete data, invalid armor, and multiple/mismatched keys. - Added mechanical UniFFI adapters that expose only storage-provided identity/progress and rendered QR modules; Swift contains no GPG parser or key policy and does not persist or copy armor. - Added native VisionKit camera permission/scanning, explicit private import/export confirmations, secure passphrase entry, public/private export selection, QR paging, Dynamic Type/accessibility labels, and camera/private-export capture and background shields following Apple HIG and platform APIs. - Reloads the Rust-backed app context immediately after a successful import. Verified: - cargo fmt --all -- --check - RUSTFLAGS=-D warnings cargo check --workspace --all-targets - cargo clippy --workspace --all-targets -- -D warnings - cargo test --workspace, including five focused key-transfer fixture tests - xcodegen generate - warning-as-error iPhone 17 Pro simulator Debug build - Xcode Analyze - final Debug simulator install - signed optimized generic iPhone Release build - plist and diff validation Deferred interactive/hardware verification, tracked by the untested label: 1. Visually inspect Preferences, key lists, confirmations, error states, single/multipart QR paging, light/dark appearance, and largest Dynamic Type on the iPhone 17 Pro simulator after an authenticated session is available. 2. Verify VoiceOver labels, reading order, announcements, and QR paging. 3. On the physical iPhone, test camera permission grant, denial, Settings recovery, camera interruption, cancellation, and actual public/private single/multipart scans including duplicate and out-of-order frames. 4. Exercise wrong and correct passphrases, public/private/default imports, duplicate handling, and immediate in-app context reload through the UI. 5. Verify exported QR scannability between devices and exact reconstructed armor end to end. 6. Verify camera/private-key masking in the app switcher, background, screen recording, and AirPlay capture states. 7. Install and launch the signed Release build on the paired iPhone and confirm the existing IronStorage app icon. The paired iPhone was unavailable when delivery completed. Apple references consulted: HIG Privacy and Design Principles, VisionKit DataScannerViewController/scanning guidance, AVFoundation camera authorization, and UIScene capture-state protection.
hugo closed this issue 2026-08-11 19:26:38 +00:00
Author
Owner

Non-destructive validation completed on 2026-08-12. In the iPhone simulator, public-key QR export rendered, private-key export stopped at its explicit confirmation and was canceled, and QR import reached the scanner availability path and exited without importing. Exact/multipart transfer, validation, and round-trip behavior passed in the current isolated Rust workspace tests. No key material was imported or changed. Removing the untested label.

Non-destructive validation completed on 2026-08-12. In the iPhone simulator, public-key QR export rendered, private-key export stopped at its explicit confirmation and was canceled, and QR import reached the scanner availability path and exited without importing. Exact/multipart transfer, validation, and round-trip behavior passed in the current isolated Rust workspace tests. No key material was imported or changed. Removing the untested label.
hugo removed the untested label 2026-08-12 17:32:52 +00:00
Sign in to join this conversation.