fix: refresh paykit key after wallet reset - #761
ben-kaufman wants to merge 5 commits into
Conversation
|
jvsena42
left a comment
There was a problem hiding this comment.
No findings.
Checked:
- No stale key after a reset. Every wallet replacement goes through
AppReset.wipe→Keychain.wipeEntireKeychain(), which deletespaykit_receiver_noise_secret_keyalong with the mnemonic.loadBytes()then returns nil, the cache clears, and the new seed's key is derived and upserted. - Locked-device read.
Keychain.loadthrows for any status other thanerrSecItemNotFound, so anerrSecInteractionNotAllowedread fails before the cache is cleared or anything is written. A mismatched stored key is rejected, never overwritten. - Concurrency.
loadOrDeriveandpersisthold the store'sNSLockacross the whole check-then-set. The injected closures don't re-enter the store, and lock order is always provider → store. A wipe racing a sign-in fails atpersistinstead of persisting a stale key. - Upgrade from v2.5.0 (pre-fix store). When stored matches derived, the first call re-validates and caches. No migration is needed.
- Tests. Both new tests fail on the pre-fix code, where the cached bytes were returned.
- Parity with synonymdev/bitkit-android@64ca85f2c. Android dropped the cache; iOS keys the cache on keychain equality. The outcome is the same.
There was a problem hiding this comment.
Advice: ✅ Approve
Review: diff 3 files.
Counterpart synonymdev/bitkit-android#1106: equivalent.
Findings:
1 inline (non-blocking)
Audit:
Awaits QA.
Coverage:
QA: journeys and manual tests await green CI checks
Reviewed by gpt-6-sol-xhigh via gh-pr-review-loop skill
Commands: @ovi-reviewer review · test · retest · audit (author or owner)
There was a problem hiding this comment.
Advice: ✅ Approve
Reaudit: diff 1 file.
No new findings; the rest is in the review.
Counterpart synonymdev/bitkit-android#1106: equivalent.
Reviewed by gpt-6-sol-xhigh via gh-pr-review-loop skill
Commands: @ovi-reviewer review · test · retest · audit (author or owner)
jvsena42
left a comment
There was a problem hiding this comment.
Delta since 7ba939a8e: three master merges and 8b98438c, which adds testCachedKeyDoesNotBypassKeychainReadFailure. No production change since my earlier review. No findings.
Checked and clean at head:
- The cache fix itself.
validatedKeyBytes()reads the keychain first and reuses the cache only when the bytes still match, and it nils the cache before deriving, so a derive that fails between a wipe and a new wallet leaves nothing stale behind. The session provider lives on the shared actor, which is why the cache survived a reset in the first place. - Derivation is deterministic from the seed and passphrase, so a restore on any device reproduces the same key.
ReceiverNoiseSecretKey.random()has no callers in the app. - No derive-before-seed. The mnemonic and passphrase are saved in one function,
saveStringrefuses to overwrite, and everyloadOrDerivecaller is user-driven or needs a stored session that the wipe removes. - Wipe. The noise secret has exactly one storage key and
wipeEntireKeychain()removes it along with the mnemonic, the passphrase and the SDK state. The SDK blob holds only the noise public key.forgetSessionAccesstakes the operation lock, so an in-flight sign-in cannot land after the wipe. - Reinstall. A fresh install with no installation marker wipes the keychain before anything else, so an orphaned key cannot be inherited; and a leftover key with no mnemonic is rejected rather than used.
- Counterparties fetch the receiver noise public key per operation rather than pinning it, so a reset does not misdirect a payment; a payer holding the old identity targets a dead identity.
- Late auth completion. A reset clears the active request, so the continuation throws before activation, and
persistre-validates against the keychain. - Parity. Android already shipped the equivalent in
64ca85f2c(inside synonymdev/bitkit-android#1106, in v2.5.0). It dropped the cache entirely; iOS keeps it but re-checks the bytes. Same outcome.
Description
This PR prevents private payment connections from reusing a previous wallet's Noise key after a wallet reset.
No SDK update, key format change, migration, UI change, or peer-state reset is included. This fixes the reproduced local key-cache bug; it does not establish that the staging server retained an old peer key or repair any already-stalled remote handshake.
Linked Issues/Tasks
Design
N/A — no UI changes.
Screenshot / Video
N/A — no UI changes.
QA Notes
Manual Tests
Automated Checks
BitkitTests/PaykitReceiverNoiseKeyStoreTests.swift: both new tests failed against the original code and passed with this fix; all seven key-store tests pass.PaykitSdkServiceTestpassed all 15 tests on current master, including the wallet-replacement regression. Published dependencies were used instead of incompatible local Maven artifacts.