Skip to content

Commit

Permalink
Merge pull request #1243 from WalletConnect/keychain-group-migration
Browse files Browse the repository at this point in the history
Keychain group migration
  • Loading branch information
llbartekll authored Dec 1, 2023
2 parents ef8dab0 + 409ea7c commit 3867a65
Show file tree
Hide file tree
Showing 15 changed files with 91 additions and 51 deletions.
7 changes: 0 additions & 7 deletions Example/ExampleApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,6 @@
C56EE276293F56D7004840D1 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C56EE26C293F56D6004840D1 /* UIViewController.swift */; };
C56EE279293F56D7004840D1 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = C56EE268293F56D6004840D1 /* Color.swift */; };
C56EE27B293F56F8004840D1 /* WalletConnectAuth in Frameworks */ = {isa = PBXBuildFile; productRef = C56EE27A293F56F8004840D1 /* WalletConnectAuth */; };
C56EE27D293F56F8004840D1 /* WalletConnectChat in Frameworks */ = {isa = PBXBuildFile; productRef = C56EE27C293F56F8004840D1 /* WalletConnectChat */; };
C56EE288293F5757004840D1 /* ThirdPartyConfigurator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C56EE286293F5757004840D1 /* ThirdPartyConfigurator.swift */; };
C56EE289293F5757004840D1 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = C56EE280293F5757004840D1 /* Application.swift */; };
C56EE28A293F5757004840D1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C56EE27F293F5757004840D1 /* AppDelegate.swift */; };
Expand Down Expand Up @@ -781,7 +780,6 @@
files = (
A573C53D29EC366500E3CBFD /* HDWalletKit in Frameworks */,
A59D25EE2AB3672700D7EA3A /* AsyncButton in Frameworks */,
C56EE27D293F56F8004840D1 /* WalletConnectChat in Frameworks */,
C5133A78294125CC00A8314C /* Web3 in Frameworks */,
C5B2F7052970573D000DBA0E /* SolanaSwift in Frameworks */,
8487A9462A836C3F0003D5AF /* Sentry in Frameworks */,
Expand Down Expand Up @@ -2129,7 +2127,6 @@
packageProductDependencies = (
C56EE254293F569A004840D1 /* Starscream */,
C56EE27A293F56F8004840D1 /* WalletConnectAuth */,
C56EE27C293F56F8004840D1 /* WalletConnectChat */,
C5133A77294125CC00A8314C /* Web3 */,
C55D349829630D440004314A /* Web3Wallet */,
C5B2F7042970573D000DBA0E /* SolanaSwift */,
Expand Down Expand Up @@ -3576,10 +3573,6 @@
isa = XCSwiftPackageProductDependency;
productName = WalletConnectAuth;
};
C56EE27C293F56F8004840D1 /* WalletConnectChat */ = {
isa = XCSwiftPackageProductDependency;
productName = WalletConnectChat;
};
C579FEB52AFA86CD008855EB /* Web3Modal */ = {
isa = XCSwiftPackageProductDependency;
package = A5F1526D2ACDC46B00D745A6 /* XCRemoteSwiftPackageReference "web3modal-swift" */;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,20 @@
ReferencedContainer = "container:..">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "NO"
buildForRunning = "NO"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "ChatTests"
BuildableName = "ChatTests"
BlueprintName = "ChatTests"
ReferencedContainer = "container:..">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
Expand Down Expand Up @@ -188,16 +202,6 @@
ReferencedContainer = "container:..">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "ChatTests"
BuildableName = "ChatTests"
BlueprintName = "ChatTests"
ReferencedContainer = "container:..">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
Expand Down
3 changes: 2 additions & 1 deletion Example/WalletApp/ApplicationLayer/Application.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Foundation
import WalletConnectChat
import WalletConnectUtils
import WalletConnectSigner

final class Application {
var uri: WalletConnectURI?
Expand Down
2 changes: 1 addition & 1 deletion Sources/Auth/AuthClientFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public struct AuthClientFactory {
guard let keyValueStorage = UserDefaults(suiteName: groupIdentifier) else {
fatalError("Could not instantiate UserDefaults for a group identifier \(groupIdentifier)")
}
let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk")
let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk", accessGroup: groupIdentifier)
let iatProvider = DefaultIATProvider()

return AuthClientFactory.create(
Expand Down
3 changes: 2 additions & 1 deletion Sources/Chat/ChatClientFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import Foundation
public struct ChatClientFactory {

static func create(keyserverUrl: String, relayClient: RelayClient, networkingInteractor: NetworkingInteractor, syncClient: SyncClient, historyClient: HistoryClient) -> ChatClient {
let keychain = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk")
fatalError("fix access group")
let keychain = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk", accessGroup: "")
let keyserverURL = URL(string: keyserverUrl)!
return ChatClientFactory.create(
keyserverURL: keyserverURL,
Expand Down
2 changes: 1 addition & 1 deletion Sources/WalletConnectHistory/HistoryClientFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Foundation
class HistoryClientFactory {

static func create() -> HistoryClient {
let keychain = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk")
let keychain = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk", accessGroup: "")
let keyValueStorage = UserDefaults.standard
let logger = ConsoleLogger()
return HistoryClientFactory.create(
Expand Down
74 changes: 53 additions & 21 deletions Sources/WalletConnectKMS/Keychain/KeychainStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,19 @@ public protocol KeychainStorageProtocol {
public final class KeychainStorage: KeychainStorageProtocol {

private let service: String
private let accessGroup: String
private let synchronizationQueue = DispatchQueue(label: "com.yourapp.KeychainStorage")

private let secItem: KeychainServiceProtocol

public init(keychainService: KeychainServiceProtocol = KeychainServiceWrapper(), serviceIdentifier: String) {
public init(
keychainService: KeychainServiceProtocol = KeychainServiceWrapper(),
serviceIdentifier: String,
accessGroup: String
) {
self.secItem = keychainService
service = serviceIdentifier
self.accessGroup = accessGroup
self.service = serviceIdentifier
}

public func add<T>(_ item: T, forKey key: String) throws where T: GenericPasswordConvertible {
Expand Down Expand Up @@ -55,7 +62,16 @@ public final class KeychainStorage: KeychainStorageProtocol {
case errSecSuccess:
return item as? Data
case errSecItemNotFound:
return tryMigrateAttrAccessibleOnRead(key: key) // TODO: Replace with nil once migration period ends
return try synchronizationQueue.sync {
// Try to update the accessibility attribute first - migration V1
tryUpdateAccessibilityAttribute(key: key)
// Then attempt to migrate to the new access group and return if item exists - migration V2
if let updatedData = try tryToMigrateKeyToNewAccessGroupOnRead(key: key) {
return updatedData
} else {
return nil
}
}
default:
throw KeychainError(status)
}
Expand All @@ -70,17 +86,21 @@ public final class KeychainStorage: KeychainStorageProtocol {
let attributes = [kSecValueData: data]

let status = secItem.update(query as CFDictionary, attributes as CFDictionary)

switch status {
case errSecSuccess:
return
case errSecItemNotFound:
try tryMigrateAttrAccessibleOnUpdate(data: data, key: key) // TODO: Remove once migration period ends
// Try to update the accessibility attribute - migration V1
tryUpdateAccessibilityAttribute(key: key)
// Then attempt to migrate to the new access group - migration V2
try tryToMigrateKeyToNewAccessGroupOnUpdate(data: data, key: key)
default:
throw KeychainError(status)
}
}


public func delete(key: String) throws {
let query = buildBaseServiceQuery(for: key)

Expand Down Expand Up @@ -109,40 +129,41 @@ public final class KeychainStorage: KeychainStorageProtocol {
kSecAttrIsInvisible: true,
kSecUseDataProtectionKeychain: true,
kSecAttrService: service,
kSecAttrAccessGroup: accessGroup,
kSecAttrAccount: key
]
}

private func tryMigrateAttrAccessibleOnRead(key: String) -> Data? {

private func tryUpdateAccessibilityAttribute(key: String) {
var updateQuery = buildBaseServiceQuery(for: key)
updateQuery.removeValue(forKey: kSecAttrAccessGroup)
updateQuery[kSecAttrAccessible] = kSecAttrAccessibleWhenUnlockedThisDeviceOnly

let attributes = [kSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly]
let status = secItem.update(updateQuery as CFDictionary, attributes as CFDictionary)
let _ = secItem.update(updateQuery as CFDictionary, attributes as CFDictionary)
}

guard status == errSecSuccess else {
return nil
}
private func tryToMigrateKeyToNewAccessGroupOnRead(key: String) throws -> Data? {
tryToMigrateToNewAccessGroup(key: key)

// Try to read the item again with updated accessibility
var readQuery = buildBaseServiceQuery(for: key)
readQuery[kSecReturnData] = true

var item: CFTypeRef?
_ = secItem.copyMatching(readQuery as CFDictionary, &item)
let readStatus = secItem.copyMatching(readQuery as CFDictionary, &item)

return item as? Data
if readStatus == errSecSuccess, let data = item as? Data {
return data
} else {
return nil
}
}

private func tryMigrateAttrAccessibleOnUpdate(data: Data, key: String) throws {
var updateAccessQuery = buildBaseServiceQuery(for: key)
updateAccessQuery[kSecAttrAccessible] = kSecAttrAccessibleWhenUnlockedThisDeviceOnly
private func tryToMigrateKeyToNewAccessGroupOnUpdate(data: Data, key: String) throws {

let accessAttributes = [kSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly]
let accessStatus = secItem.update(updateAccessQuery as CFDictionary, accessAttributes as CFDictionary)

guard accessStatus == errSecSuccess else {
throw KeychainError.itemNotFound
}
tryToMigrateToNewAccessGroup(key: key)

let updateQuery = buildBaseServiceQuery(for: key)
let updateAttributes = [kSecValueData: data]
Expand All @@ -153,4 +174,15 @@ public final class KeychainStorage: KeychainStorageProtocol {
throw KeychainError.itemNotFound
}
}

private func tryToMigrateToNewAccessGroup(key: String) {
var query = buildBaseServiceQuery(for: key)
query.removeValue(forKey: kSecAttrAccessGroup)

let attributesToUpdate = [
kSecAttrAccessGroup: accessGroup
] as [CFString: Any]

let _ = secItem.update(query as CFDictionary, attributesToUpdate as CFDictionary)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ public struct NetworkingClientFactory {
groupIdentifier: String
) -> NetworkingInteractor {
let logger = ConsoleLogger(prefix: "🕸️", loggingLevel: .off)

guard let keyValueStorage = UserDefaults(suiteName: groupIdentifier) else {
fatalError("Could not instantiate UserDefaults for a group identifier \(groupIdentifier)")
}
let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk")

let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk", accessGroup: groupIdentifier)
return NetworkingClientFactory.create(relayClient: relayClient, logger: logger, keychainStorage: keychainStorage, keyValueStorage: keyValueStorage)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ public struct NotifyClientFactory {
public static func create(projectId: String, groupIdentifier: String, networkInteractor: NetworkInteracting, pairingRegisterer: PairingRegisterer, pushClient: PushClient, crypto: CryptoProvider, notifyHost: String, explorerHost: String) -> NotifyClient {
let logger = ConsoleLogger(prefix: "🔔",loggingLevel: .debug)
let keyserverURL = URL(string: "https://keys.walletconnect.com")!
let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk")
let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk", accessGroup: groupIdentifier)
let groupKeychainService = GroupKeychainStorage(serviceIdentifier: groupIdentifier)
let databasePath = databasePath(appGroup: groupIdentifier, database: "notify.db")
let sqlite = DiskSqlite(path: databasePath)
Expand Down
4 changes: 3 additions & 1 deletion Sources/WalletConnectPairing/PairingClientFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ public struct PairingClientFactory {
groupIdentifier: String
) -> PairingClient {
let logger = ConsoleLogger(loggingLevel: .off)

guard let keyValueStorage = UserDefaults(suiteName: groupIdentifier) else {
fatalError("Could not instantiate UserDefaults for a group identifier \(groupIdentifier)")
}
let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk")
let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk", accessGroup: groupIdentifier)

return PairingClientFactory.create(logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, networkingClient: networkingClient)
}

Expand Down
3 changes: 2 additions & 1 deletion Sources/WalletConnectPush/PushClientFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ public struct PushClientFactory {
environment: APNSEnvironment
) -> PushClient {

let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk")

guard let keyValueStorage = UserDefaults(suiteName: groupIdentifier) else {
fatalError("Could not instantiate UserDefaults for a group identifier \(groupIdentifier)")
}
let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk", accessGroup: groupIdentifier)

return PushClientFactory.create(
projectId: projectId,
Expand Down
4 changes: 2 additions & 2 deletions Sources/WalletConnectRelay/RelayClientFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ public struct RelayClientFactory {
socketConnectionType: SocketConnectionType
) -> RelayClient {


guard let keyValueStorage = UserDefaults(suiteName: groupIdentifier) else {
fatalError("Could not instantiate UserDefaults for a group identifier \(groupIdentifier)")
}

let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk")
let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk", accessGroup: groupIdentifier)

let logger = ConsoleLogger(prefix: "🚄" ,loggingLevel: .off)

Expand Down
4 changes: 3 additions & 1 deletion Sources/WalletConnectSign/Sign/SignClientFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ public struct SignClientFactory {
groupIdentifier: String
) -> SignClient {
let logger = ConsoleLogger(loggingLevel: .debug)

guard let keyValueStorage = UserDefaults(suiteName: groupIdentifier) else {
fatalError("Could not instantiate UserDefaults for a group identifier \(groupIdentifier)")
}
let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk")
let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk", accessGroup: groupIdentifier)

return SignClientFactory.create(metadata: metadata, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, pairingClient: pairingClient, networkingClient: networkingClient)
}

Expand Down
3 changes: 2 additions & 1 deletion Sources/WalletConnectSync/SyncClientFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import Foundation
final class SyncClientFactory {

static func create(networkInteractor: NetworkInteracting, bip44: BIP44Provider) -> SyncClient {
let keychain = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk")
fatalError("fix access group")
let keychain = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk", accessGroup: "")
return create(networkInteractor: networkInteractor, bip44: bip44, keychain: keychain)
}

Expand Down
3 changes: 2 additions & 1 deletion Tests/WalletConnectKMSTests/KeychainStorageTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ final class KeychainStorageTests: XCTestCase {
fakeKeychain = KeychainServiceFake()
sut = KeychainStorage(
keychainService: fakeKeychain,
serviceIdentifier: "")
serviceIdentifier: "",
accessGroup: "")
}

override func tearDown() {
Expand Down

0 comments on commit 3867a65

Please sign in to comment.