Skip to content

Commit

Permalink
[Core] Feature/#289 ClientID Storage (#294)
Browse files Browse the repository at this point in the history
* ClientIdStorage implementation

* Storage key changed
  • Loading branch information
flypaper0 authored Jun 24, 2022
1 parent b932692 commit 8ac5443
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 26 deletions.
14 changes: 8 additions & 6 deletions Sources/WalletConnectKMS/Keychain/KeychainService.swift
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
import Foundation

protocol KeychainServiceProtocol {
public protocol KeychainServiceProtocol {
func add(_ attributes: CFDictionary, _ result: UnsafeMutablePointer<CFTypeRef?>?) -> OSStatus
func copyMatching(_ query: CFDictionary, _ result: UnsafeMutablePointer<CFTypeRef?>?) -> OSStatus
func update(_ query: CFDictionary, _ attributesToUpdate: CFDictionary) -> OSStatus
func delete(_ query: CFDictionary) -> OSStatus
}

final class KeychainServiceWrapper: KeychainServiceProtocol {
public final class KeychainServiceWrapper: KeychainServiceProtocol {

func add(_ attributes: CFDictionary, _ result: UnsafeMutablePointer<CFTypeRef?>?) -> OSStatus {
public init() { }

public func add(_ attributes: CFDictionary, _ result: UnsafeMutablePointer<CFTypeRef?>?) -> OSStatus {
return SecItemAdd(attributes, result)
}

func copyMatching(_ query: CFDictionary, _ result: UnsafeMutablePointer<CFTypeRef?>?) -> OSStatus {
public func copyMatching(_ query: CFDictionary, _ result: UnsafeMutablePointer<CFTypeRef?>?) -> OSStatus {
return SecItemCopyMatching(query, result)
}

func update(_ query: CFDictionary, _ attributesToUpdate: CFDictionary) -> OSStatus {
public func update(_ query: CFDictionary, _ attributesToUpdate: CFDictionary) -> OSStatus {
return SecItemUpdate(query, attributesToUpdate)
}

func delete(_ query: CFDictionary) -> OSStatus {
public func delete(_ query: CFDictionary) -> OSStatus {
return SecItemDelete(query)
}
}
22 changes: 11 additions & 11 deletions Sources/WalletConnectKMS/Keychain/KeychainStorage.swift
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
import Foundation

protocol KeychainStorageProtocol {
public protocol KeychainStorageProtocol {
func add<T: GenericPasswordConvertible>(_ item: T, forKey key: String) throws
func read<T: GenericPasswordConvertible>(key: String) throws -> T
func delete(key: String) throws
func deleteAll() throws
}

final class KeychainStorage: KeychainStorageProtocol {
public final class KeychainStorage: KeychainStorageProtocol {

private let service: String

private let secItem: KeychainServiceProtocol

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

func add<T>(_ item: T, forKey key: String) throws where T: GenericPasswordConvertible {
public func add<T>(_ item: T, forKey key: String) throws where T: GenericPasswordConvertible {
try add(data: item.rawRepresentation, forKey: key)
}

func add(data: Data, forKey key: String) throws {
public func add(data: Data, forKey key: String) throws {
var query = buildBaseServiceQuery(for: key)
query[kSecValueData] = data

Expand All @@ -33,14 +33,14 @@ final class KeychainStorage: KeychainStorageProtocol {
}
}

func read<T>(key: String) throws -> T where T: GenericPasswordConvertible {
public func read<T>(key: String) throws -> T where T: GenericPasswordConvertible {
guard let data = try readData(key: key) else {
throw KeychainError(errSecItemNotFound)
}
return try T(rawRepresentation: data)
}

func readData(key: String) throws -> Data? {
public func readData(key: String) throws -> Data? {
var query = buildBaseServiceQuery(for: key)
query[kSecReturnData] = true

Expand All @@ -57,11 +57,11 @@ final class KeychainStorage: KeychainStorageProtocol {
}
}

func update<T>(_ item: T, forKey key: String) throws where T: GenericPasswordConvertible {
public func update<T>(_ item: T, forKey key: String) throws where T: GenericPasswordConvertible {
try update(data: item.rawRepresentation, forKey: key)
}

func update(data: Data, forKey key: String) throws {
public func update(data: Data, forKey key: String) throws {
let query = buildBaseServiceQuery(for: key)
let attributes = [kSecValueData: data]

Expand All @@ -72,7 +72,7 @@ final class KeychainStorage: KeychainStorageProtocol {
}
}

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

let status = secItem.delete(query as CFDictionary)
Expand All @@ -82,7 +82,7 @@ final class KeychainStorage: KeychainStorageProtocol {
}
}

func deleteAll() throws {
public func deleteAll() throws {
let query = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: service
Expand Down
16 changes: 14 additions & 2 deletions Sources/WalletConnectRelay/ClientAuth/ClientIdStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,20 @@ protocol ClientIdStoring {
}

actor ClientIdStorage: ClientIdStoring {
func getOrCreateKeyPair() async throws -> SigningPrivateKey {
fatalError("not implemented")
private let key = "com.walletconnect.iridium.client_id"
private let keychain: KeychainStorageProtocol

init(keychain: KeychainStorageProtocol) {
self.keychain = keychain
}

func getOrCreateKeyPair() async throws -> SigningPrivateKey {
do {
return try keychain.read(key: key)
} catch {
let privateKey = SigningPrivateKey()
try keychain.add(privateKey, forKey: key)
return privateKey
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ actor SocketAuthenticator: SocketAuthenticating {
private let didKeyFactory: ED25519DIDKeyFactory

init(authChallengeProvider: AuthChallengeProviding = AuthChallengeProvider(),
clientIdStorage: ClientIdStoring = ClientIdStorage(),
clientIdStorage: ClientIdStoring,
didKeyFactory: ED25519DIDKeyFactory = ED25519DIDKeyFactoryImpl()) {
self.authChallengeProvider = authChallengeProvider
self.clientIdStorage = clientIdStorage
Expand Down
21 changes: 21 additions & 0 deletions Tests/RelayerTests/AuthTests/ClientIdStorageTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Foundation
import XCTest
import TestingUtils
import WalletConnectKMS
@testable import WalletConnectRelay

final class ClientIdStorageTests: XCTestCase {

func testGetOrCreate() async throws {
let keychain = KeychainStorageMock()
let storage = ClientIdStorage(keychain: keychain)

XCTAssertThrowsError(try keychain.read(key: "com.walletconnect.iridium.client_id") as SigningPrivateKey)

let saved = try await storage.getOrCreateKeyPair()
XCTAssertEqual(saved, try keychain.read(key: "com.walletconnect.iridium.client_id"))

let restored = try await storage.getOrCreateKeyPair()
XCTAssertEqual(saved, restored)
}
}
16 changes: 10 additions & 6 deletions Tests/TestingUtils/Mocks/KeychainStorageMock.swift
Original file line number Diff line number Diff line change
@@ -1,33 +1,37 @@
import Foundation
@testable import WalletConnectKMS

final class KeychainStorageMock: KeychainStorageProtocol {
public final class KeychainStorageMock: KeychainStorageProtocol {

var storage: [String: Data] = [:]
public var storage: [String: Data]

private(set) var didCallAdd = false
private(set) var didCallRead = false
private(set) var didCallDelete = false

func add<T>(_ item: T, forKey key: String) throws where T: GenericPasswordConvertible {
public init(storage: [String: Data] = [:]) {
self.storage = storage
}

public func add<T>(_ item: T, forKey key: String) throws where T: GenericPasswordConvertible {
didCallAdd = true
storage[key] = item.rawRepresentation
}

func read<T>(key: String) throws -> T where T: GenericPasswordConvertible {
public func read<T>(key: String) throws -> T where T: GenericPasswordConvertible {
didCallRead = true
if let data = storage[key] {
return try T(rawRepresentation: data)
}
throw KeychainError(errSecItemNotFound)
}

func delete(key: String) throws {
public func delete(key: String) throws {
didCallDelete = true
storage[key] = nil
}

func deleteAll() throws {
public func deleteAll() throws {
storage = [:]
}
}

0 comments on commit 8ac5443

Please sign in to comment.