Skip to content

feat(rn_cli_wallet): encrypt MMKV secret storage with keychain-backed key#566

Draft
ignaciosantise wants to merge 1 commit into
mainfrom
fix/encrypt-mnemonic-storage
Draft

feat(rn_cli_wallet): encrypt MMKV secret storage with keychain-backed key#566
ignaciosantise wants to merge 1 commit into
mainfrom
fix/encrypt-mnemonic-storage

Conversation

@ignaciosantise

Copy link
Copy Markdown
Collaborator

Problem

wallets/rn_cli_wallet persisted every chain's mnemonic / private key / secret key as plaintext in MMKV (new MMKV() with no encryptionKey). Anyone with filesystem or backup access to the device could read the seed phrases. The code itself warned about this ([SECURITY] … stored unencrypted).

Fix

Encrypt the whole MMKV store at rest on native using MMKV's built-in encryptionKey, with a random key generated once and kept in the iOS Keychain / Android Keystore via expo-secure-store. The key that protects the secrets never sits in plaintext.

Chosen deliberately over moving secrets into SecureStore per-chain: all secret access is funneled through a single chokepoint (getEncryptionKey), so every chain is protected at once, the 7 copy-pasted-from-web chain utils stay untouched, and it leaves a clean seam for optional biometric gating later.

Changes

  • package.json / yarn.lock — add expo-secure-store@~56.0.4 (matches the SDK-56 unified versioning track).
  • src/utils/secureEncryptionKey.ts (new) — owns the encryption-key lifecycle. Native-only; generates a 16-char key (8 random bytes hex, respecting MMKV's ≤16-byte limit) via the global crypto.getRandomValues, stores it in SecureStore, and memoizes. Web returns undefined (no native secure storage in a browser).
  • src/utils/storage.ts — MMKV is now opened lazily (the key fetch is async; the storage.* methods were already async so signatures are unchanged). On first run it recrypts the existing plaintext store in place so wallets aren't reset on upgrade; afterwards it opens with encryptionKey. Dev-only console.logs report which path ran.
  • 5 chain utils (Solana/Sui/Ton/Tron/Canton) — reworded the now-inaccurate stored unencrypted dev warnings.

Platform behavior

  • iOS / Android: encrypted at rest, key in Keychain/Keystore.
  • Web: unchanged — localStorage shim, unencrypted by design (documented limitation).

How to verify

  1. cd wallets/rn_cli_wallet && yarn install, rebuild, clear Metro/gradle caches (encryption changes the on-disk format).
  2. Console: on launch you'll see MMKV recrypted in place … (first run) then MMKV opened ENCRYPTED … on subsequent launches.
  3. Definitive — grep the on-disk file (MMKV encrypts key names too, so plaintext key names disappear):
    adb exec-out run-as com.walletconnect.web3wallet.rnsample.debug \
      cat files/mmkv/mmkv.default | strings | grep -E 'EIP155_MNEMONIC|SOLANA_'
    Present before the fix, empty after — while the wallet still loads the same address in-app.

Notes

  • No biometric gating in this PR (silent access on startup). The architecture is ready for it: add requireAuthentication: true in secureEncryptionKey.ts + the expo-secure-store config plugin for NSFaceIDUsageDescription.
  • Not yet built/tested on a device in this workspace (node_modules not installed here) — verification above is the intended path.

🤖 Generated with Claude Code

… key

Wallet mnemonics/private keys were persisted as plaintext in MMKV. Encrypt
the MMKV store at rest on native using MMKV's built-in encryptionKey, with a
random key generated once and kept in the iOS Keychain / Android Keystore via
expo-secure-store. All secret access is funneled through a single chokepoint
(getEncryptionKey), so every chain is protected at once and the design is ready
for optional biometric gating later.

- add expo-secure-store dependency
- new src/utils/secureEncryptionKey.ts owns the key lifecycle (native-only;
  web returns undefined and stays best-effort/unencrypted by design)
- storage.ts lazily opens an encrypted MMKV; on first run it recrypts the
  existing plaintext store in place so wallets aren't reset on upgrade
- reword now-inaccurate "stored unencrypted" dev warnings in the chain utils

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
deposit-app-demo Error Error Jul 8, 2026 6:47pm
pos-demo Ready Ready Preview, Comment Jul 8, 2026 6:47pm

Request Review

@claude

claude Bot commented Jul 8, 2026

Copy link
Copy Markdown

Claude finished @ignaciosantise's task in 2m 33s —— View job


Code Review in Progress

  • Gather context and read changed files
  • Review secureEncryptionKey.ts (new file)
  • Review storage.ts (encryption integration)
  • Review chain utils warning updates
  • License compliance subagent (running)
  • Breaking changes subagent (running)
  • Data classification subagent (running)
  • Consolidate and post findings

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