diff --git a/Sources/WalletConnectKMS/Crypto/KeyManagementService.swift b/Sources/WalletConnectKMS/Crypto/KeyManagementService.swift index 53a271b14..975a39251 100644 --- a/Sources/WalletConnectKMS/Crypto/KeyManagementService.swift +++ b/Sources/WalletConnectKMS/Crypto/KeyManagementService.swift @@ -93,7 +93,7 @@ public class KeyManagementService: KeyManagementServiceProtocol { public func getPrivateKey(for publicKey: AgreementPublicKey) throws -> AgreementPrivateKey? { do { return try keychain.read(key: publicKey.hexRepresentation) as AgreementPrivateKey - } catch let error where (error as? KeychainError)?.status == errSecItemNotFound { + } catch KeychainError.itemNotFound { return nil } catch { throw error diff --git a/Sources/WalletConnectKMS/Keychain/KeychainError.swift b/Sources/WalletConnectKMS/Keychain/KeychainError.swift index 78c16dcf7..de1865aeb 100644 --- a/Sources/WalletConnectKMS/Keychain/KeychainError.swift +++ b/Sources/WalletConnectKMS/Keychain/KeychainError.swift @@ -1,23 +1,41 @@ import Foundation -// TODO: Integrate with WalletConnectError -struct KeychainError: Error, LocalizedError { +public enum KeychainError: Error, LocalizedError { + case itemNotFound + case other(OSStatus) - let status: OSStatus + public init(_ status: OSStatus) { + switch status { + case errSecItemNotFound: + self = .itemNotFound + default: + self = .other(status) + } + } - init(_ status: OSStatus) { - self.status = status + public var status: OSStatus { + switch self { + case .itemNotFound: + return errSecItemNotFound + case .other(let status): + return status + } } - var errorDescription: String? { - return "OSStatus: \(status), message: \(status.message)" + public var errorDescription: String? { + switch self { + case .itemNotFound: + return "Keychain item not found" + case .other(let status): + return "OSStatus: \(status), message: \(status.message)" + } } } extension KeychainError: CustomStringConvertible { - var description: String { - status.message + public var description: String { + return errorDescription ?? "" } } diff --git a/Sources/WalletConnectKMS/Keychain/KeychainStorage.swift b/Sources/WalletConnectKMS/Keychain/KeychainStorage.swift index c8c3ee083..f52f4c828 100644 --- a/Sources/WalletConnectKMS/Keychain/KeychainStorage.swift +++ b/Sources/WalletConnectKMS/Keychain/KeychainStorage.swift @@ -55,7 +55,7 @@ public final class KeychainStorage: KeychainStorageProtocol { case errSecSuccess: return item as? Data case errSecItemNotFound: - return nil + return tryMigrateAttrAccessible(key: key) // TODO: Replace with nil once migration period ends default: throw KeychainError(status) } @@ -100,11 +100,31 @@ public final class KeychainStorage: KeychainStorageProtocol { private func buildBaseServiceQuery(for key: String) -> [CFString: Any] { return [ kSecClass: kSecClassGenericPassword, - kSecAttrAccessible: kSecAttrAccessibleWhenUnlockedThisDeviceOnly, + kSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, kSecAttrIsInvisible: true, kSecUseDataProtectionKeychain: true, kSecAttrService: service, kSecAttrAccount: key ] } + + private func tryMigrateAttrAccessible(key: String) -> Data? { + var updateQuery = buildBaseServiceQuery(for: key) + updateQuery[kSecAttrAccessible] = kSecAttrAccessibleWhenUnlockedThisDeviceOnly + + let attributes = [kSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly] + let status = secItem.update(updateQuery as CFDictionary, attributes as CFDictionary) + + guard status == errSecSuccess else { + return nil + } + + var readQuery = buildBaseServiceQuery(for: key) + readQuery[kSecReturnData] = true + + var item: CFTypeRef? + _ = secItem.copyMatching(readQuery as CFDictionary, &item) + + return item as? Data + } } diff --git a/Sources/WalletConnectRelay/ClientAuth/ClientIdStorage.swift b/Sources/WalletConnectRelay/ClientAuth/ClientIdStorage.swift index 272451b96..e07333198 100644 --- a/Sources/WalletConnectRelay/ClientAuth/ClientIdStorage.swift +++ b/Sources/WalletConnectRelay/ClientAuth/ClientIdStorage.swift @@ -25,11 +25,13 @@ public struct ClientIdStorage: ClientIdStoring { do { let publicPart = try getPublicPart() return try getPrivatePart(for: publicPart) - } catch { + } catch Errors.privatePartNotFound, Errors.publicPartNotFound { let privateKey = SigningPrivateKey() try setPrivatePart(privateKey) setPublicPart(privateKey.publicKey) return privateKey + } catch { + throw error } } @@ -76,8 +78,10 @@ private extension ClientIdStorage { func getPrivatePart(for publicPart: SigningPublicKey) throws -> SigningPrivateKey { do { return try keychain.read(key: publicPart.storageId) - } catch { + } catch KeychainError.itemNotFound { throw Errors.privatePartNotFound + } catch { + throw error } }