From 7ba939a8e6063149617c646fcb14a6b939ca53ca Mon Sep 17 00:00:00 2001 From: benk10 Date: Wed, 16 Sep 2026 20:10:45 +0300 Subject: [PATCH] fix: refresh paykit key after wallet reset --- Bitkit/Services/PubkyService.swift | 6 ++- .../PaykitReceiverNoiseKeyStoreTests.swift | 43 +++++++++++++++++++ changelog.d/next/761.fixed.md | 1 + 3 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 changelog.d/next/761.fixed.md diff --git a/Bitkit/Services/PubkyService.swift b/Bitkit/Services/PubkyService.swift index c1117fe38..ba587f9ff 100644 --- a/Bitkit/Services/PubkyService.swift +++ b/Bitkit/Services/PubkyService.swift @@ -1490,16 +1490,18 @@ final class PaykitReceiverNoiseKeyStore: @unchecked Sendable { } private func validatedKeyBytes() throws -> Data { - if let validatedBytes { + let storedBytes = try loadBytes() + if let validatedBytes, storedBytes == validatedBytes { return validatedBytes } + validatedBytes = nil let derivedBytes = try deriveBytes() guard derivedBytes.count == Self.keyLength else { throw invalidKeyError("Derived Paykit receiver Noise key is invalid") } - if let storedBytes = try loadBytes() { + if let storedBytes { guard storedBytes.count == Self.keyLength else { throw invalidKeyError("Stored Paykit receiver Noise key is invalid") } diff --git a/BitkitTests/PaykitReceiverNoiseKeyStoreTests.swift b/BitkitTests/PaykitReceiverNoiseKeyStoreTests.swift index 017b2828d..de0638def 100644 --- a/BitkitTests/PaykitReceiverNoiseKeyStoreTests.swift +++ b/BitkitTests/PaykitReceiverNoiseKeyStoreTests.swift @@ -54,6 +54,49 @@ final class PaykitReceiverNoiseKeyStoreTests: XCTestCase { XCTAssertEqual(persistedBytes, Data(repeating: 1, count: 32)) } + func testReceiverNoiseKeyFollowsWalletReplacementAfterKeychainWipe() throws { + var persistedBytes: Data? + var derivedBytes = Data(repeating: 1, count: 32) + let store = PaykitReceiverNoiseKeyStore( + loadBytes: { persistedBytes }, + upsertBytes: { persistedBytes = $0 }, + deriveBytes: { derivedBytes } + ) + let previousKey = try store.loadOrDerive() + + persistedBytes = nil + derivedBytes = Data(repeating: 2, count: 32) + + let replacementKey = try store.loadOrDerive().exportBytes() + XCTAssertEqual(replacementKey, derivedBytes) + XCTAssertEqual(persistedBytes, derivedBytes) + XCTAssertThrowsError(try store.persist(previousKey)) + + let restoredStore = PaykitReceiverNoiseKeyStore( + loadBytes: { persistedBytes }, + upsertBytes: { persistedBytes = $0 }, + deriveBytes: { derivedBytes } + ) + XCTAssertEqual(try restoredStore.loadOrDerive().exportBytes(), replacementKey) + } + + func testRejectsChangedPersistedKeyAfterCaching() throws { + let derivedBytes = Data(repeating: 1, count: 32) + var persistedBytes: Data? = derivedBytes + let store = PaykitReceiverNoiseKeyStore( + loadBytes: { persistedBytes }, + upsertBytes: { _ in XCTFail("Invalid bytes must not be overwritten") }, + deriveBytes: { derivedBytes } + ) + _ = try store.loadOrDerive() + + for invalidBytes in [Data(repeating: 1, count: 31), Data(repeating: 2, count: 32)] { + persistedBytes = invalidBytes + XCTAssertThrowsError(try store.loadOrDerive()) + XCTAssertEqual(persistedBytes, invalidBytes) + } + } + func testRejectsInvalidPersistedReceiverNoiseKey() { let store = PaykitReceiverNoiseKeyStore( loadBytes: { Data(repeating: 0, count: 31) }, diff --git a/changelog.d/next/761.fixed.md b/changelog.d/next/761.fixed.md new file mode 100644 index 000000000..d5aade432 --- /dev/null +++ b/changelog.d/next/761.fixed.md @@ -0,0 +1 @@ +Fixed private payment connections reusing a previous wallet’s key after resetting the wallet.