Skip to content

Commit

Permalink
Merge pull request #1079 from WalletConnect/feature/clienid-from-defa…
Browse files Browse the repository at this point in the history
…ults

[Core] ClientID public and private part
  • Loading branch information
flypaper0 committed Oct 9, 2023
2 parents 9bab50d + 4fd6d9b commit c17686e
Show file tree
Hide file tree
Showing 18 changed files with 174 additions and 39 deletions.
1 change: 1 addition & 0 deletions Example/ExampleApp.xcodeproj/IntegrationTests.xctestplan
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"AuthTests\/testEIP1271RespondSuccess()",
"ChatTests",
"ENSResolverTests",
"HistoryTests",
"SyncDerivationServiceTests",
"SyncTests"
],
Expand Down
4 changes: 3 additions & 1 deletion Example/IntegrationTests/Chat/ChatTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ final class ChatTests: XCTestCase {
let historyClient = HistoryClientFactory.create(
historyUrl: "https://history.walletconnect.com",
relayUrl: "wss://relay.walletconnect.com",
keychain: keychain
keyValueStorage: keyValueStorage,
keychain: keychain,
logger: logger
)

let clientId = try! networkingInteractor.getClientId()
Expand Down
8 changes: 5 additions & 3 deletions Example/IntegrationTests/History/HistoryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ final class HistoryTests: XCTestCase {
override func setUp() {
let keychain1 = KeychainStorageMock()
let keychain2 = KeychainStorageMock()
let logger1 = ConsoleLoggerMock()
let defaults1 = RuntimeKeyValueStorage()
relayClient1 = makeRelayClient(prefix: "🐄", keychain: keychain1)
relayClient2 = makeRelayClient(prefix: "🐫", keychain: keychain2)
historyClient = makeHistoryClient(keychain: keychain1)
historyClient = makeHistoryClient(defaults: defaults1, keychain: keychain1, logger: logger1)
}

private func makeRelayClient(prefix: String, keychain: KeychainStorageProtocol) -> RelayClient {
Expand All @@ -33,8 +35,8 @@ final class HistoryTests: XCTestCase {
logger: ConsoleLogger(prefix: prefix + " [Relay]", loggingLevel: .debug))
}

private func makeHistoryClient(keychain: KeychainStorageProtocol) -> HistoryNetworkService {
let clientIdStorage = ClientIdStorage(keychain: keychain)
private func makeHistoryClient(defaults: KeyValueStorage, keychain: KeychainStorageProtocol, logger: ConsoleLogging) -> HistoryNetworkService {
let clientIdStorage = ClientIdStorage(defaults: defaults, keychain: keychain, logger: logger)
return HistoryNetworkService(clientIdStorage: clientIdStorage)
}

Expand Down
5 changes: 4 additions & 1 deletion Example/IntegrationTests/Pairing/PairingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,14 @@ final class PairingTests: XCTestCase {
let prefix = "🐶 Wallet: "
let (pairingClient, networkingInteractor, keychain, keyValueStorage) = makeClientDependencies(prefix: prefix)
let notifyLogger = ConsoleLogger(prefix: prefix + " [Notify]", loggingLevel: .debug)
let defaults = RuntimeKeyValueStorage()
walletPairingClient = pairingClient
let historyClient = HistoryClientFactory.create(
historyUrl: "https://history.walletconnect.com",
relayUrl: "wss://relay.walletconnect.com",
keychain: keychain
keyValueStorage: defaults,
keychain: keychain,
logger: notifyLogger
)
appAuthClient = AuthClientFactory.create(
metadata: AppMetadata(name: name, description: "", url: "", icons: [""]),
Expand Down
1 change: 1 addition & 0 deletions Example/IntegrationTests/Push/NotifyTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ final class NotifyTests: XCTestCase {
let notifyLogger = ConsoleLogger(prefix: prefix + " [Notify]", loggingLevel: .debug)
let pushClient = PushClientFactory.create(projectId: "",
pushHost: "echo.walletconnect.com",
keyValueStorage: keyValueStorage,
keychainStorage: keychain,
environment: .sandbox)
let keyserverURL = URL(string: "https://keys.walletconnect.com")!
Expand Down
6 changes: 3 additions & 3 deletions Example/RelayIntegrationTests/RelayClientEndToEndTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ final class RelayClientEndToEndTests: XCTestCase {
private var publishers = Set<AnyCancellable>()

func makeRelayClient(prefix: String) -> RelayClient {
let clientIdStorage = ClientIdStorage(keychain: KeychainStorageMock())
let keyValueStorage = RuntimeKeyValueStorage()
let logger = ConsoleLogger(prefix: prefix, loggingLevel: .debug)
let clientIdStorage = ClientIdStorage(defaults: keyValueStorage, keychain: KeychainStorageMock(), logger: logger)
let socketAuthenticator = ClientIdAuthenticator(
clientIdStorage: clientIdStorage,
url: InputConfig.relayUrl
Expand All @@ -43,15 +45,13 @@ final class RelayClientEndToEndTests: XCTestCase {
)
let socket = WebSocket(url: urlFactory.create(fallback: false))
let webSocketFactory = WebSocketFactoryMock(webSocket: socket)
let logger = ConsoleLogger(prefix: prefix, loggingLevel: .debug)
let dispatcher = Dispatcher(
socketFactory: webSocketFactory,
relayUrlFactory: urlFactory,
socketConnectionType: .manual,
logger: logger
)
let keychain = KeychainStorageMock()
let keyValueStorage = RuntimeKeyValueStorage()
let relayClient = RelayClientFactory.create(
relayHost: InputConfig.relayHost,
projectId: InputConfig.projectId,
Expand Down
10 changes: 7 additions & 3 deletions Sources/WalletConnectHistory/HistoryClientFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@ class HistoryClientFactory {

static func create() -> HistoryClient {
let keychain = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk")
let keyValueStorage = UserDefaults.standard
let logger = ConsoleLogger()
return HistoryClientFactory.create(
historyUrl: "https://history.walletconnect.com",
relayUrl: "wss://relay.walletconnect.com",
keychain: keychain
keyValueStorage: keyValueStorage,
keychain: keychain,
logger: logger
)
}

static func create(historyUrl: String, relayUrl: String, keychain: KeychainStorageProtocol) -> HistoryClient {
let clientIdStorage = ClientIdStorage(keychain: keychain)
static func create(historyUrl: String, relayUrl: String, keyValueStorage: KeyValueStorage, keychain: KeychainStorageProtocol, logger: ConsoleLogging) -> HistoryClient {
let clientIdStorage = ClientIdStorage(defaults: keyValueStorage, keychain: keychain, logger: logger)
let kms = KeyManagementService(keychain: keychain)
let serializer = Serializer(kms: kms, logger: ConsoleLogger(prefix: "🔐", loggingLevel: .off))
let historyNetworkService = HistoryNetworkService(clientIdStorage: clientIdStorage)
Expand Down
11 changes: 11 additions & 0 deletions Sources/WalletConnectJWT/JSONEncoder+JWT.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Foundation

extension JSONEncoder {

public static var jwt: JSONEncoder {
let jsonEncoder = JSONEncoder()
jsonEncoder.outputFormatting = .withoutEscapingSlashes
jsonEncoder.dateEncodingStrategy = .secondsSince1970
return jsonEncoder
}
}
6 changes: 3 additions & 3 deletions Sources/WalletConnectJWT/JWT.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ struct JWT<JWTClaims: JWTEncodable>: Codable, Equatable {
let signature: String
let string: String

init(claims: JWTClaims, signer: JWTSigning) throws {
init(claims: JWTClaims, signer: JWTSigning, jsonEncoder: JSONEncoder = .jwt) throws {
self.header = JWTHeader(alg: signer.alg)
self.claims = claims

let headerString = try header.encode()
let claimsString = try claims.encode()
let headerString = try header.encode(jsonEncoder: jsonEncoder)
let claimsString = try claims.encode(jsonEncoder: jsonEncoder)
let signature = try signer.sign(header: headerString, claims: claimsString)

self.signature = signature
Expand Down
7 changes: 2 additions & 5 deletions Sources/WalletConnectJWT/JWTEncodable.swift
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import Foundation

public protocol JWTEncodable: Codable, Equatable {
func encode() throws -> String
func encode(jsonEncoder: JSONEncoder) throws -> String

static func decode(from string: String) throws -> Self
}

extension JWTEncodable {

public func encode() throws -> String {
let jsonEncoder = JSONEncoder()
jsonEncoder.outputFormatting = .withoutEscapingSlashes
jsonEncoder.dateEncodingStrategy = .secondsSince1970
public func encode(jsonEncoder: JSONEncoder) throws -> String {
let data = try jsonEncoder.encode(self)
return JWTEncoder.base64urlEncodedString(data: data)
}
Expand Down
5 changes: 4 additions & 1 deletion Sources/WalletConnectPush/PushClientFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,20 @@ public struct PushClientFactory {
environment: APNSEnvironment) -> PushClient {

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

return PushClientFactory.create(
projectId: projectId,
pushHost: pushHost,
keyValueStorage: keyValueStorage,
keychainStorage: keychainStorage,
environment: environment)
}

public static func create(
projectId: String,
pushHost: String,
keyValueStorage: KeyValueStorage,
keychainStorage: KeychainStorageProtocol,
environment: APNSEnvironment
) -> PushClient {
Expand All @@ -28,7 +31,7 @@ public struct PushClientFactory {
let logger = ConsoleLogger(prefix: "👂🏻", loggingLevel: .off)
let httpClient = HTTPNetworkClient(host: pushHost, session: session)

let clientIdStorage = ClientIdStorage(keychain: keychainStorage)
let clientIdStorage = ClientIdStorage(defaults: keyValueStorage, keychain: keychainStorage, logger: logger)

let pushAuthenticator = PushAuthenticator(clientIdStorage: clientIdStorage, pushHost: pushHost)

Expand Down
77 changes: 70 additions & 7 deletions Sources/WalletConnectRelay/ClientAuth/ClientIdStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,89 @@ public protocol ClientIdStoring {
}

public struct ClientIdStorage: ClientIdStoring {
private let key = "com.walletconnect.iridium.client_id"
private let oldStorageKey = "com.walletconnect.iridium.client_id"
private let publicStorageKey = "com.walletconnect.iridium.client_id.public"

private let defaults: KeyValueStorage
private let keychain: KeychainStorageProtocol
private let logger: ConsoleLogging

public init(keychain: KeychainStorageProtocol) {
public init(defaults: KeyValueStorage, keychain: KeychainStorageProtocol, logger: ConsoleLogging) {
self.defaults = defaults
self.keychain = keychain
self.logger = logger

migrateIfNeeded()
}

public func getOrCreateKeyPair() throws -> SigningPrivateKey {
do {
return try keychain.read(key: key)
let publicPart = try getPublicPart()
return try getPrivatePart(for: publicPart)
} catch {
let privateKey = SigningPrivateKey()
try keychain.add(privateKey, forKey: key)
try setPrivatePart(privateKey)
setPublicPart(privateKey.publicKey)
return privateKey
}
}

public func getClientId() throws -> String {
let privateKey: SigningPrivateKey = try keychain.read(key: key)
let pubKey = privateKey.publicKey.rawRepresentation
return DIDKey(rawData: pubKey).did(variant: .ED25519)
let pubKey = try getPublicPart()
let _ = try getPrivatePart(for: pubKey)
return DIDKey(rawData: pubKey.rawRepresentation).did(variant: .ED25519)
}
}

private extension ClientIdStorage {

enum Errors: Error {
case publicPartNotFound
case privatePartNotFound
}

func migrateIfNeeded() {
guard let privateKey: SigningPrivateKey = try? keychain.read(key: oldStorageKey) else {
return
}

do {
try setPrivatePart(privateKey)
setPublicPart(privateKey.publicKey)
try keychain.delete(key: oldStorageKey)
logger.debug("ClientID migrated")
} catch {
logger.debug("ClientID migration failed with: \(error.localizedDescription)")
}
}

func getPublicPart() throws -> SigningPublicKey {
guard let data = defaults.data(forKey: publicStorageKey) else {
throw Errors.publicPartNotFound
}
return try SigningPublicKey(rawRepresentation: data)
}

func setPublicPart(_ newValue: SigningPublicKey) {
defaults.set(newValue.rawRepresentation, forKey: publicStorageKey)
}

func getPrivatePart(for publicPart: SigningPublicKey) throws -> SigningPrivateKey {
do {
return try keychain.read(key: publicPart.storageId)
} catch {
throw Errors.privatePartNotFound
}
}

func setPrivatePart(_ newValue: SigningPrivateKey) throws {
try keychain.add(newValue, forKey: newValue.publicKey.storageId)
}
}

private extension SigningPublicKey {

var storageId: String {
return rawRepresentation.sha256().toHexString()
}
}
2 changes: 1 addition & 1 deletion Sources/WalletConnectRelay/RelayClientFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public struct RelayClientFactory {
logger: ConsoleLogging
) -> RelayClient {

let clientIdStorage = ClientIdStorage(keychain: keychainStorage)
let clientIdStorage = ClientIdStorage(defaults: keyValueStorage, keychain: keychainStorage, logger: logger)

let socketAuthenticator = ClientIdAuthenticator(
clientIdStorage: clientIdStorage,
Expand Down
6 changes: 3 additions & 3 deletions Tests/CommonsTests/AnyCodableTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ private struct SampleStruct: Codable, Equatable {
SampleStruct(
bool: true,
int: 1337,
double: 13.37,
double: 13,
string: "verystringwow",
object: SubObject(
string: "0xdeadbeef"
Expand All @@ -40,7 +40,7 @@ private struct SampleStruct: Codable, Equatable {
{
"bool": true,
"int": 1337,
"double": 13.37,
"double": 13,
"string": "verystringwow",
"object": {
"string": "0xdeadbeef"
Expand All @@ -52,7 +52,7 @@ private struct SampleStruct: Codable, Equatable {
{
"bool": ****,
"int": 1337,
"double": 13.37,
"double": 13,
"string": "verystringwow",
}
""".data(using: .utf8)!
Expand Down
Loading

0 comments on commit c17686e

Please sign in to comment.