Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Core] ClientID public and private part #1079

Merged
merged 10 commits into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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