Skip to content

feat(keys): import ncryptsec and scan key QR codes on onboarding + add account - #100

Open
dmnyc wants to merge 3 commits into
DocNR:mainfrom
dmnyc:feat/ncryptsec-qr-key-import
Open

dmnyc wants to merge 3 commits into
DocNR:mainfrom
dmnyc:feat/ncryptsec-qr-key-import

Conversation

@dmnyc

@dmnyc dmnyc commented Oct 31, 2026

Copy link
Copy Markdown

What

Adds NIP-49 (ncryptsec) import and key QR-code scanning to both key-import surfaces:

  • Add Account sheet — new ncryptsec segment (Generate new | nsec / hex | ncryptsec) with a password field, plus a Scan QR Code button.
  • Onboarding — the import field now accepts ncryptsec1… (a password card appears automatically when the input is an ncryptsec) and gets the same QR scan button.

How

  • addAccount(ncryptsec:password:) decrypts via NostrSDK's EncryptedSecretKey (NIP-49), then re-enters the existing addAccount(nsec:) path — so dedupe, the per-device cap, Keychain storage, and proxy registration behave identically to nsec import.
  • Malformed keys and wrong passwords surface as one actionable AccountError.invalidNcryptsec ("Couldn't decrypt this ncryptsec key. Check the password…").
  • QR scans strip a nostr: prefix and auto-route: an ncryptsec1… value lands in the password mode, anything else in nsec/hex. Pasting an ncryptsec into the nsec field auto-flips modes instead of erroring.
  • Reuses the existing QRScannerView component; NSCameraUsageDescription updated to also cover key QR codes.

Testing

  • AppStateMultiAccountTests 17/17 pass, including 5 new cases: decrypt-and-import stores the nsec under the right pubkey; wrong password and malformed input throw .invalidNcryptsec with no account row and no Keychain write; re-importing an existing key via ncryptsec dedupes; importing past the per-device cap throws .accountCapReached and writes nothing.
  • Onboarding ncryptsec import verified end-to-end on the simulator using the NIP-49 spec decryption test vector (password nostr).
  • Pre-PR delegate review: verdict approve (findings addressed in c16daeb).

⚠️ Maintainer: please test QR scanning on a physical device

The simulator has no camera, so the QR scanning path (onboarding scan button, Add Account sheet scanner, the camera permission prompt, and scanning a real key QR) could not be exercised during development — please verify on hardware.

Known follow-up (non-blocking)

EncryptedSecretKey.decrypt is handed untrusted input (e.g. scanned QRs) with no way to peek at the declared scrypt log_n first — a crafted ncryptsec1… with a huge log_n could force an expensive derivation. SDK-level (no FFI accessor); worth a rust-nostr feature request or an app-side bech32 peek before decrypt.

dmnyc added 3 commits October 31, 2026 09:05
Auto-approved requests deliver a blank passive banner (iOS has no
supported full-suppression in an NSE) and rely on an immediate
removeDeliveredNotifications to retract it. That remove raced process
exit and often no-oped, leaving blank banners parked in Notification
Center. Await the delivered-notifications lookup after the remove —
the same keep-alive pattern sweepPriorBlankNotifications already uses —
so the retraction actually lands.
…d account

- addAccount(ncryptsec:password:) decrypts a NIP-49 ncryptsec via
  NostrSDK EncryptedSecretKey and re-enters addAccount(nsec:) so
  dedupe, per-device cap, Keychain write, and proxy registration
  behave identically to nsec import; decrypt failures surface as
  AccountError.invalidNcryptsec
- Add Account sheet gains an ncryptsec segment (password field) and
  a QR scanner entry point; scans strip a nostr: prefix and auto-
  route ncryptsec1 values to the password mode
- Onboarding import field accepts ncryptsec (password card appears
  on input) with the same QR scanner; camera string in Info.plist
  now covers key QR codes
- tests: ncryptsec decrypt/wrong-password/malformed coverage in
  AppStateMultiAccountTests
- pass NIP-49 passwords raw (never whitespace-trimmed); they are
  arbitrary strings
- strip a nostr: prefix on manual paste too, and route it through
  the same normalization as scans (paste + auto-flip + onboarding)
- scanner now ignores QRs that are not nsec/ncryptsec/hex keys and
  keeps scanning instead of dumping any URI into the field
- snapshot-protect the onboarding screen (plain TextField renders
  the pasted secret; audit ref A10.1)
- clear stale error text when a paste auto-flips to ncryptsec mode
- tests: ncryptsec-path dedupe, cap enforcement writes nothing, and
  failed-decrypt Keychain-first ordering
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.

1 participant