From 332ff603de355f776515e593bfe05380635623ad Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 22 Feb 2023 15:33:34 +0100 Subject: [PATCH 01/18] update to type 1 envelope --- Example/IntegrationTests/Push/PushTests.swift | 2 +- .../Client/Dapp/ProposalResponseSubscriber.swift | 7 ++++--- .../Client/Dapp/PushProposer.swift | 3 +++ .../Client/Wallet/PushRequestResponder.swift | 14 ++++++++++---- .../RPCRequests/PushResponseParams.swift | 2 ++ 5 files changed, 20 insertions(+), 8 deletions(-) diff --git a/Example/IntegrationTests/Push/PushTests.swift b/Example/IntegrationTests/Push/PushTests.swift index 4131872e2..0d6fc4b25 100644 --- a/Example/IntegrationTests/Push/PushTests.swift +++ b/Example/IntegrationTests/Push/PushTests.swift @@ -25,7 +25,7 @@ final class PushTests: XCTestCase { let keychain = KeychainStorageMock() let keyValueStorage = RuntimeKeyValueStorage() - let relayLogger = ConsoleLogger(suffix: prefix + " [Relay]", loggingLevel: .debug) + let relayLogger = ConsoleLogger(suffix: prefix + " [Relay]", loggingLevel: .off) let pairingLogger = ConsoleLogger(suffix: prefix + " [Pairing]", loggingLevel: .debug) let networkingLogger = ConsoleLogger(suffix: prefix + " [Networking]", loggingLevel: .debug) diff --git a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift index 5e4b85ebe..251c6d123 100644 --- a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift @@ -47,9 +47,10 @@ class ProposalResponseSubscriber { let (topic, _) = try generateAgreementKeys(peerPublicKeyHex: peerPublicKeyHex, selfpublicKeyHex: selfpublicKeyHex) let pushSubscription = PushSubscription(topic: topic, account: payload.request.account, relay: relay, metadata: metadata) - subscriptionsStore.set(pushSubscription, forKey: topic) - kms.deletePrivateKey(for: selfpublicKeyHex) - try await networkingInteractor.subscribe(topic: topic) + + logger.debug("Subscribing to Push Subscription topic: \(topic)") + subscriptionsStore.set(pushSubscription, forKey: payload.topic) + try await networkingInteractor.subscribe(topic: payload.topic) return pushSubscription } diff --git a/Sources/WalletConnectPush/Client/Dapp/PushProposer.swift b/Sources/WalletConnectPush/Client/Dapp/PushProposer.swift index 973e29edf..f76fdb075 100644 --- a/Sources/WalletConnectPush/Client/Dapp/PushProposer.swift +++ b/Sources/WalletConnectPush/Client/Dapp/PushProposer.swift @@ -22,8 +22,11 @@ class PushProposer { logger.debug("PushProposer: Sending Push Proposal") let protocolMethod = PushRequestProtocolMethod() let pubKey = try kms.createX25519KeyPair() + let responseTopic = pubKey.rawRepresentation.sha256().toHexString() + try kms.setPublicKey(publicKey: pubKey, for: responseTopic) let params = PushRequestParams(publicKey: pubKey.hexRepresentation, metadata: appMetadata, account: account) let request = RPCRequest(method: protocolMethod.method, params: params) try await networkingInteractor.request(request, topic: topic, protocolMethod: protocolMethod) + try await networkingInteractor.subscribe(topic: responseTopic) } } diff --git a/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift b/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift index c674d63a0..0596a0a32 100644 --- a/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift +++ b/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift @@ -35,26 +35,32 @@ class PushRequestResponder { let requestRecord = try getRecord(requestId: requestId) let peerPublicKey = try getPeerPublicKey(for: requestRecord) - let pairingTopic = requestRecord.topic + let responseTopic = peerPublicKey.rawRepresentation.sha256().toHexString() let keys = try generateAgreementKeys(peerPublicKey: peerPublicKey) let pushTopic = keys.derivedTopic() + try kms.setAgreementSecret(keys, topic: responseTopic) + + logger.debug("PushRequestResponder: responding on response topic \(responseTopic) \(pushTopic)") + try kms.setAgreementSecret(keys, topic: pushTopic) try groupKeychainStorage.add(keys, forKey: pushTopic) + logger.debug("Subscribing to push topic: \(pushTopic)") try await networkingInteractor.subscribe(topic: pushTopic) - let responseParams = PushResponseParams(publicKey: keys.publicKey.hexRepresentation) + let responseParams = PushResponseParams(subscriptionAuth: "to_do", publicKey: keys.publicKey.hexRepresentation) let response = RPCResponse(id: requestId, result: responseParams) let requestParams = try requestRecord.request.params!.get(PushRequestParams.self) let pushSubscription = PushSubscription(topic: pushTopic, account: requestParams.account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: requestParams.metadata) + subscriptionsStore.set(pushSubscription, forKey: pushTopic) - try await networkingInteractor.respond(topic: pairingTopic, response: response, protocolMethod: PushRequestProtocolMethod()) + try await networkingInteractor.respond(topic: responseTopic, response: response, protocolMethod: PushRequestProtocolMethod(), envelopeType: .type1(pubKey: keys.publicKey.rawRepresentation)) kms.deletePrivateKey(for: keys.publicKey.hexRepresentation) } @@ -74,7 +80,7 @@ class PushRequestResponder { } private func getPeerPublicKey(for record: RPCHistory.Record) throws -> AgreementPublicKey { - guard let params = try record.request.params?.get(PushResponseParams.self) + guard let params = try record.request.params?.get(PushRequestParams.self) else { throw Errors.malformedRequestParams } let peerPublicKey = try AgreementPublicKey(hex: params.publicKey) diff --git a/Sources/WalletConnectPush/RPCRequests/PushResponseParams.swift b/Sources/WalletConnectPush/RPCRequests/PushResponseParams.swift index 04fafb7e2..7607f0852 100644 --- a/Sources/WalletConnectPush/RPCRequests/PushResponseParams.swift +++ b/Sources/WalletConnectPush/RPCRequests/PushResponseParams.swift @@ -1,5 +1,7 @@ import Foundation public struct PushResponseParams: Codable, Equatable { + let subscriptionAuth: String + //move to jwt pke let publicKey: String } From c2f3732067756854d5c93ecd87cfd028217d6f00 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 14 Mar 2023 11:58:39 +0100 Subject: [PATCH 02/18] fix testDappDeletePushSubscription --- .../Client/Dapp/ProposalResponseSubscriber.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift index 251c6d123..995c72e60 100644 --- a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift @@ -49,7 +49,7 @@ class ProposalResponseSubscriber { let pushSubscription = PushSubscription(topic: topic, account: payload.request.account, relay: relay, metadata: metadata) logger.debug("Subscribing to Push Subscription topic: \(topic)") - subscriptionsStore.set(pushSubscription, forKey: payload.topic) + subscriptionsStore.set(pushSubscription, forKey: topic) try await networkingInteractor.subscribe(topic: payload.topic) return pushSubscription } From f097a3d2e2eac707a2293fce292bbab489273dab Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 14 Mar 2023 12:12:45 +0100 Subject: [PATCH 03/18] fix testWalletDeletePushSubscription --- Example/IntegrationTests/Push/PushTests.swift | 2 +- .../Client/Dapp/ProposalResponseSubscriber.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Example/IntegrationTests/Push/PushTests.swift b/Example/IntegrationTests/Push/PushTests.swift index 0d6fc4b25..4131872e2 100644 --- a/Example/IntegrationTests/Push/PushTests.swift +++ b/Example/IntegrationTests/Push/PushTests.swift @@ -25,7 +25,7 @@ final class PushTests: XCTestCase { let keychain = KeychainStorageMock() let keyValueStorage = RuntimeKeyValueStorage() - let relayLogger = ConsoleLogger(suffix: prefix + " [Relay]", loggingLevel: .off) + let relayLogger = ConsoleLogger(suffix: prefix + " [Relay]", loggingLevel: .debug) let pairingLogger = ConsoleLogger(suffix: prefix + " [Pairing]", loggingLevel: .debug) let networkingLogger = ConsoleLogger(suffix: prefix + " [Networking]", loggingLevel: .debug) diff --git a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift index 995c72e60..8ae2ee372 100644 --- a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift @@ -50,7 +50,7 @@ class ProposalResponseSubscriber { logger.debug("Subscribing to Push Subscription topic: \(topic)") subscriptionsStore.set(pushSubscription, forKey: topic) - try await networkingInteractor.subscribe(topic: payload.topic) + try await networkingInteractor.subscribe(topic: topic) return pushSubscription } From 99118fa8b15cf0c1f4e2cd72f207fe4b712dd9e6 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 14 Mar 2023 12:24:34 +0100 Subject: [PATCH 04/18] savepoint --- Example/IntegrationTests/Push/PushTests.swift | 4 ++-- .../Client/Dapp/ProposalResponseSubscriber.swift | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Example/IntegrationTests/Push/PushTests.swift b/Example/IntegrationTests/Push/PushTests.swift index 4131872e2..61546a57f 100644 --- a/Example/IntegrationTests/Push/PushTests.swift +++ b/Example/IntegrationTests/Push/PushTests.swift @@ -25,8 +25,8 @@ final class PushTests: XCTestCase { let keychain = KeychainStorageMock() let keyValueStorage = RuntimeKeyValueStorage() - let relayLogger = ConsoleLogger(suffix: prefix + " [Relay]", loggingLevel: .debug) - let pairingLogger = ConsoleLogger(suffix: prefix + " [Pairing]", loggingLevel: .debug) + let relayLogger = ConsoleLogger(suffix: prefix + " [Relay]", loggingLevel: .off) + let pairingLogger = ConsoleLogger(suffix: prefix + " [Pairing]", loggingLevel: .off) let networkingLogger = ConsoleLogger(suffix: prefix + " [Networking]", loggingLevel: .debug) let relayClient = RelayClient( diff --git a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift index 8ae2ee372..a7d6106d0 100644 --- a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift @@ -44,22 +44,20 @@ class ProposalResponseSubscriber { private func handleResponse(payload: ResponseSubscriptionPayload) async throws -> PushSubscription { let peerPublicKeyHex = payload.response.publicKey let selfpublicKeyHex = payload.request.publicKey - let (topic, _) = try generateAgreementKeys(peerPublicKeyHex: peerPublicKeyHex, selfpublicKeyHex: selfpublicKeyHex) - + let topic = try generateAgreementKeys(peerPublicKeyHex: peerPublicKeyHex, selfpublicKeyHex: selfpublicKeyHex) let pushSubscription = PushSubscription(topic: topic, account: payload.request.account, relay: relay, metadata: metadata) - logger.debug("Subscribing to Push Subscription topic: \(topic)") subscriptionsStore.set(pushSubscription, forKey: topic) try await networkingInteractor.subscribe(topic: topic) return pushSubscription } - private func generateAgreementKeys(peerPublicKeyHex: String, selfpublicKeyHex: String) throws -> (topic: String, keys: AgreementKeys) { + private func generateAgreementKeys(peerPublicKeyHex: String, selfpublicKeyHex: String) throws -> String { let selfPublicKey = try AgreementPublicKey(hex: selfpublicKeyHex) let keys = try kms.performKeyAgreement(selfPublicKey: selfPublicKey, peerPublicKey: peerPublicKeyHex) let topic = keys.derivedTopic() try kms.setAgreementSecret(keys, topic: topic) - return (topic: topic, keys: keys) + return topic } private func subscribeForProposalErrors() { From 51d475f19e0872103bf87dda68219b207fc91e38 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 15 Mar 2023 10:25:57 +0100 Subject: [PATCH 05/18] add AcceptSubscriptionJWTPayload --- .../RPCRequests/PushResponseParams.swift | 59 +++++++++++++++++-- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/Sources/WalletConnectPush/RPCRequests/PushResponseParams.swift b/Sources/WalletConnectPush/RPCRequests/PushResponseParams.swift index 7607f0852..0fed6bc69 100644 --- a/Sources/WalletConnectPush/RPCRequests/PushResponseParams.swift +++ b/Sources/WalletConnectPush/RPCRequests/PushResponseParams.swift @@ -1,7 +1,58 @@ import Foundation -public struct PushResponseParams: Codable, Equatable { - let subscriptionAuth: String - //move to jwt pke - let publicKey: String +struct AcceptSubscriptionJWTPayload: JWTClaimsCodable { + + struct Claims: JWTClaims { + /// timestamp when jwt was issued + let iat: UInt64 + /// timestamp when jwt must expire + let exp: UInt64 + /// did:key of an identity key. Enables to resolve attached blockchain account. + let iss: String + /// key server for identity key verification + let ksu: String + /// dapp's url + let aud: String + /// blockchain account that push subscription has been proposed for (did:pkh) + let sub: String + } + + struct Wrapper: JWTWrapper { + let subscriptionAuth: String + + init(jwtString: String) { + self.subscriptionAuth = jwtString + } + + var jwtString: String { + return subscriptionAuth + } + } + + let keyserver: URL + let subscriptionAccount: Account + let dappUrl: String + + init(keyserver: URL, subscriptionAccount: Account, dappUrl: String) { + self.keyserver = keyserver + self.subscriptionAccount = subscriptionAccount + self.dappUrl = dappUrl + } + + init(claims: Claims) throws { + self.keyserver = try claims.ksu.asURL() + self.subscriptionAccount = try Account(DIDPKHString: claims.sub) + self.dappUrl = claims.aud + } + + func encode(iss: String) throws -> Claims { + return Claims( + iat: expiry(days: 30), + exp: defaultIatMilliseconds(), + iss: iss, + ksu: keyserver.absoluteString, + aud: dappUrl, + sub: subscriptionAccount.did + ) + } } From f61f57c82c045faa82cddf00f17028421a82565b Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 15 Mar 2023 12:53:38 +0100 Subject: [PATCH 06/18] update push request respond method to use JWTs --- Package.swift | 2 +- .../Client/Wallet/PushRequestResponder.swift | 25 +++++++++++++++---- ...ift => AcceptSubscriptionJWTPayload.swift} | 0 3 files changed, 21 insertions(+), 6 deletions(-) rename Sources/WalletConnectPush/RPCRequests/{PushResponseParams.swift => AcceptSubscriptionJWTPayload.swift} (100%) diff --git a/Package.swift b/Package.swift index a4393df00..35879ff53 100644 --- a/Package.swift +++ b/Package.swift @@ -64,7 +64,7 @@ let package = Package( path: "Sources/Web3Wallet"), .target( name: "WalletConnectPush", - dependencies: ["WalletConnectPairing", "WalletConnectEcho", "WalletConnectNetworking"], + dependencies: ["WalletConnectPairing", "WalletConnectEcho", "WalletConnectNetworking", "WalletConnectIdentity"], path: "Sources/WalletConnectPush"), .target( name: "WalletConnectEcho", diff --git a/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift b/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift index 0596a0a32..872b454b1 100644 --- a/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift +++ b/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift @@ -1,4 +1,5 @@ import WalletConnectNetworking +import WalletConnectIdentity import Foundation class PushRequestResponder { @@ -6,6 +7,8 @@ class PushRequestResponder { case recordForIdNotFound case malformedRequestParams } + private let keyserverURL: URL + private let identityClient: IdentityClient private let networkingInteractor: NetworkInteracting private let kms: KeyManagementService private let rpcHistory: RPCHistory @@ -15,13 +18,17 @@ class PushRequestResponder { private let groupKeychainStorage: KeychainStorageProtocol - init(networkingInteractor: NetworkInteracting, + init(keyserverURL: URL, + networkingInteractor: NetworkInteracting, + identityClient: IdentityClient, logger: ConsoleLogging, kms: KeyManagementService, groupKeychainStorage: KeychainStorageProtocol, rpcHistory: RPCHistory, subscriptionsStore: CodableStore ) { + self.keyserverURL = keyserverURL + self.identityClient = identityClient self.networkingInteractor = networkingInteractor self.logger = logger self.kms = kms @@ -39,6 +46,7 @@ class PushRequestResponder { let keys = try generateAgreementKeys(peerPublicKey: peerPublicKey) let pushTopic = keys.derivedTopic() + let requestParams = try requestRecord.request.params!.get(PushRequestParams.self) try kms.setAgreementSecret(keys, topic: responseTopic) @@ -49,13 +57,11 @@ class PushRequestResponder { try groupKeychainStorage.add(keys, forKey: pushTopic) logger.debug("Subscribing to push topic: \(pushTopic)") - try await networkingInteractor.subscribe(topic: pushTopic) - let responseParams = PushResponseParams(subscriptionAuth: "to_do", publicKey: keys.publicKey.hexRepresentation) + try await networkingInteractor.subscribe(topic: pushTopic) - let response = RPCResponse(id: requestId, result: responseParams) + let response = createJWTResponse(requestId: requestId, subscriptionAccount: requestParams.account, dappUrl: requestParams.metadata.url) - let requestParams = try requestRecord.request.params!.get(PushRequestParams.self) let pushSubscription = PushSubscription(topic: pushTopic, account: requestParams.account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: requestParams.metadata) subscriptionsStore.set(pushSubscription, forKey: pushTopic) @@ -73,6 +79,15 @@ class PushRequestResponder { try await networkingInteractor.respondError(topic: pairingTopic, requestId: requestId, protocolMethod: PushRequestProtocolMethod(), reason: PushError.rejected) } + private func createJWTResponse(requestId: RPCID, subscriptionAccount: Account, dappUrl: String) -> RPCResponse { + let jwtPayload = AcceptSubscriptionJWTPayload(keyserver: keyserverURL, subscriptionAccount: subscriptionAccount, dappUrl: dappUrl) + let wrapper = try identityClient.signAndCreateWrapper( + payload: jwtPayload, + account: subscriptionAccount + ) + return RPCResponse(id: requestId, result: wrapper) + } + private func getRecord(requestId: RPCID) throws -> RPCHistory.Record { guard let record = rpcHistory.get(recordId: requestId) else { throw Errors.recordForIdNotFound } diff --git a/Sources/WalletConnectPush/RPCRequests/PushResponseParams.swift b/Sources/WalletConnectPush/RPCRequests/AcceptSubscriptionJWTPayload.swift similarity index 100% rename from Sources/WalletConnectPush/RPCRequests/PushResponseParams.swift rename to Sources/WalletConnectPush/RPCRequests/AcceptSubscriptionJWTPayload.swift From b51a88e84f12cfe8902e1137f76460a75b8c098e Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 15 Mar 2023 13:00:41 +0100 Subject: [PATCH 07/18] savepoint --- .../Client/Dapp/ProposalResponseSubscriber.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift index a7d6106d0..c503b7ea6 100644 --- a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift @@ -32,7 +32,7 @@ class ProposalResponseSubscriber { private func subscribeForProposalResponse() { let protocolMethod = PushRequestProtocolMethod() networkingInteractor.responseSubscription(on: protocolMethod) - .sink { [unowned self] (payload: ResponseSubscriptionPayload) in + .sink { [unowned self] (payload: ResponseSubscriptionPayload) in logger.debug("Received Push Proposal response") Task(priority: .userInitiated) { let pushSubscription = try await handleResponse(payload: payload) @@ -41,8 +41,9 @@ class ProposalResponseSubscriber { }.store(in: &publishers) } - private func handleResponse(payload: ResponseSubscriptionPayload) async throws -> PushSubscription { + private func handleResponse(payload: ResponseSubscriptionPayload) async throws -> PushSubscription { let peerPublicKeyHex = payload.response.publicKey + let selfpublicKeyHex = payload.request.publicKey let topic = try generateAgreementKeys(peerPublicKeyHex: peerPublicKeyHex, selfpublicKeyHex: selfpublicKeyHex) let pushSubscription = PushSubscription(topic: topic, account: payload.request.account, relay: relay, metadata: metadata) From 60472c83cf30aea090b53738c39a2b91f9fb93c4 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 16 Mar 2023 10:47:37 +0100 Subject: [PATCH 08/18] add derived topic for Networking as a result on type1 envelope symmkey derivation --- .../Serialiser/Serializer.swift | 13 +++---- .../Serialiser/Serializing.swift | 4 +-- .../NetworkInteracting.swift | 2 +- .../NetworkingInteractor.swift | 34 +++++++++---------- .../RequestSubscriptionPayload.swift | 4 ++- .../ResponseSubscriptionPayload.swift | 4 ++- .../Dapp/ProposalResponseSubscriber.swift | 7 ++++ .../Client/Wallet/PushRequestResponder.swift | 4 +-- 8 files changed, 42 insertions(+), 30 deletions(-) diff --git a/Sources/WalletConnectKMS/Serialiser/Serializer.swift b/Sources/WalletConnectKMS/Serialiser/Serializer.swift index 96fa968ce..745d3c565 100644 --- a/Sources/WalletConnectKMS/Serialiser/Serializer.swift +++ b/Sources/WalletConnectKMS/Serialiser/Serializer.swift @@ -40,11 +40,12 @@ public class Serializer: Serializing { /// - topic: Topic that is associated with a symetric key for decrypting particular codable object /// - encodedEnvelope: Envelope to deserialize and decrypt /// - Returns: Deserialized object - public func deserialize(topic: String, encodedEnvelope: String) throws -> T { + public func deserialize(topic: String, encodedEnvelope: String) throws -> (T, String?) { let envelope = try Envelope(encodedEnvelope) switch envelope.type { case .type0: - return try handleType0Envelope(topic, envelope) + let deserialisedType: T = try handleType0Envelope(topic, envelope) + return (deserialisedType, nil) case .type1(let peerPubKey): return try handleType1Envelope(topic, peerPubKey: peerPubKey, sealbox: envelope.sealbox) } @@ -58,15 +59,15 @@ public class Serializer: Serializing { } } - private func handleType1Envelope(_ topic: String, peerPubKey: Data, sealbox: Data) throws -> T { + private func handleType1Envelope(_ topic: String, peerPubKey: Data, sealbox: Data) throws -> (T, String) { guard let selfPubKey = kms.getPublicKey(for: topic) else { throw Errors.publicKeyForTopicNotFound } let agreementKeys = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: peerPubKey.toHexString()) let decodedType: T = try decode(sealbox: sealbox, symmetricKey: agreementKeys.sharedKey.rawRepresentation) - let newTopic = agreementKeys.derivedTopic() - try kms.setAgreementSecret(agreementKeys, topic: newTopic) - return decodedType + let derivedTopic = agreementKeys.derivedTopic() + try kms.setAgreementSecret(agreementKeys, topic: derivedTopic) + return (decodedType, derivedTopic) } private func decode(sealbox: Data, symmetricKey: Data) throws -> T { diff --git a/Sources/WalletConnectKMS/Serialiser/Serializing.swift b/Sources/WalletConnectKMS/Serialiser/Serializing.swift index 353e846b3..837a4c329 100644 --- a/Sources/WalletConnectKMS/Serialiser/Serializing.swift +++ b/Sources/WalletConnectKMS/Serialiser/Serializing.swift @@ -2,11 +2,11 @@ import Foundation public protocol Serializing { func serialize(topic: String, encodable: Encodable, envelopeType: Envelope.EnvelopeType) throws -> String - func deserialize(topic: String, encodedEnvelope: String) throws -> T + func deserialize(topic: String, encodedEnvelope: String) throws -> (T, String?) } public extension Serializing { - func tryDeserialize(topic: String, encodedEnvelope: String) -> T? { + func tryDeserialize(topic: String, encodedEnvelope: String) -> (T, String?)? { return try? deserialize(topic: topic, encodedEnvelope: encodedEnvelope) } diff --git a/Sources/WalletConnectNetworking/NetworkInteracting.swift b/Sources/WalletConnectNetworking/NetworkInteracting.swift index 92a82a402..8f47c6cc9 100644 --- a/Sources/WalletConnectNetworking/NetworkInteracting.swift +++ b/Sources/WalletConnectNetworking/NetworkInteracting.swift @@ -3,7 +3,7 @@ import Combine public protocol NetworkInteracting { var socketConnectionStatusPublisher: AnyPublisher { get } - var requestPublisher: AnyPublisher<(topic: String, request: RPCRequest, publishedAt: Date), Never> { get } + var requestPublisher: AnyPublisher<(topic: String, request: RPCRequest, publishedAt: Date, derivedTopic: String?), Never> { get } func subscribe(topic: String) async throws func unsubscribe(topic: String) func batchSubscribe(topics: [String]) async throws diff --git a/Sources/WalletConnectNetworking/NetworkingInteractor.swift b/Sources/WalletConnectNetworking/NetworkingInteractor.swift index 425be69fd..7c092867b 100644 --- a/Sources/WalletConnectNetworking/NetworkingInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkingInteractor.swift @@ -9,14 +9,14 @@ public class NetworkingInteractor: NetworkInteracting { private let rpcHistory: RPCHistory private let logger: ConsoleLogging - private let requestPublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, publishedAt: Date), Never>() - private let responsePublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, response: RPCResponse, publishedAt: Date), Never>() + private let requestPublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, publishedAt: Date, derivedTopic: String?), Never>() + private let responsePublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, response: RPCResponse, publishedAt: Date, derivedTopic: String?), Never>() - public var requestPublisher: AnyPublisher<(topic: String, request: RPCRequest, publishedAt: Date), Never> { + public var requestPublisher: AnyPublisher<(topic: String, request: RPCRequest, publishedAt: Date, derivedTopic: String?), Never> { requestPublisherSubject.eraseToAnyPublisher() } - private var responsePublisher: AnyPublisher<(topic: String, request: RPCRequest, response: RPCResponse, publishedAt: Date), Never> { + private var responsePublisher: AnyPublisher<(topic: String, request: RPCRequest, response: RPCResponse, publishedAt: Date, derivedTopic: String?), Never> { responsePublisherSubject.eraseToAnyPublisher() } @@ -71,9 +71,9 @@ public class NetworkingInteractor: NetworkInteracting { .filter { rpcRequest in return rpcRequest.request.method == request.method } - .compactMap { (topic, rpcRequest, publishedAt) in + .compactMap { topic, rpcRequest, publishedAt, derivedTopic in guard let id = rpcRequest.id, let request = try? rpcRequest.params?.get(RequestParams.self) else { return nil } - return RequestSubscriptionPayload(id: id, topic: topic, request: request, publishedAt: publishedAt) + return RequestSubscriptionPayload(id: id, topic: topic, request: request, publishedAt: publishedAt, derivedTopic: derivedTopic) } .eraseToAnyPublisher() } @@ -83,12 +83,12 @@ public class NetworkingInteractor: NetworkInteracting { .filter { rpcRequest in return rpcRequest.request.method == request.method } - .compactMap { topic, rpcRequest, rpcResponse, publishedAt in + .compactMap { topic, rpcRequest, rpcResponse, publishedAt, derivedTopic in guard let id = rpcRequest.id, let request = try? rpcRequest.params?.get(Request.self), let response = try? rpcResponse.result?.get(Response.self) else { return nil } - return ResponseSubscriptionPayload(id: id, topic: topic, request: request, response: response, publishedAt: publishedAt) + return ResponseSubscriptionPayload(id: id, topic: topic, request: request, response: response, publishedAt: publishedAt, derivedTopic: derivedTopic) } .eraseToAnyPublisher() } @@ -96,7 +96,7 @@ public class NetworkingInteractor: NetworkInteracting { public func responseErrorSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { return responsePublisher .filter { $0.request.method == request.method } - .compactMap { topic, rpcRequest, rpcResponse, publishedAt in + .compactMap { topic, rpcRequest, rpcResponse, publishedAt, _ in guard let id = rpcResponse.id, let request = try? rpcRequest.params?.get(Request.self), let error = rpcResponse.error else { return nil } return ResponseSubscriptionErrorPayload(id: id, topic: topic, request: request, error: error) } @@ -131,29 +131,29 @@ public class NetworkingInteractor: NetworkInteracting { } private func manageSubscription(_ topic: String, _ encodedEnvelope: String, _ publishedAt: Date) { - if let deserializedJsonRpcRequest: RPCRequest = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { - handleRequest(topic: topic, request: deserializedJsonRpcRequest, publishedAt: publishedAt) - } else if let response: RPCResponse = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { - handleResponse(response: response, publishedAt: publishedAt) + if let (deserializedJsonRpcRequest, derivedTopic): (RPCRequest, String?) = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { + handleRequest(topic: topic, request: deserializedJsonRpcRequest, publishedAt: publishedAt, derivedTopic: derivedTopic) + } else if let (response, derivedTopic): (RPCResponse, String?) = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { + handleResponse(response: response, publishedAt: publishedAt, derivedTopic: derivedTopic) } else { logger.debug("Networking Interactor - Received unknown object type from networking relay") } } - private func handleRequest(topic: String, request: RPCRequest, publishedAt: Date) { + private func handleRequest(topic: String, request: RPCRequest, publishedAt: Date, derivedTopic: String?) { do { try rpcHistory.set(request, forTopic: topic, emmitedBy: .remote) - requestPublisherSubject.send((topic, request, publishedAt)) + requestPublisherSubject.send((topic, request, publishedAt, derivedTopic)) } catch { logger.debug(error) } } - private func handleResponse(response: RPCResponse, publishedAt: Date) { + private func handleResponse(response: RPCResponse, publishedAt: Date, derivedTopic: String?) { do { try rpcHistory.resolve(response) let record = rpcHistory.get(recordId: response.id!)! - responsePublisherSubject.send((record.topic, record.request, response, publishedAt)) + responsePublisherSubject.send((record.topic, record.request, response, publishedAt, derivedTopic)) } catch { logger.debug("Handle json rpc response error: \(error)") } diff --git a/Sources/WalletConnectNetworking/RequestSubscriptionPayload.swift b/Sources/WalletConnectNetworking/RequestSubscriptionPayload.swift index c68192b27..1f34ceec0 100644 --- a/Sources/WalletConnectNetworking/RequestSubscriptionPayload.swift +++ b/Sources/WalletConnectNetworking/RequestSubscriptionPayload.swift @@ -5,11 +5,13 @@ public struct RequestSubscriptionPayload: Codable, Subscriptio public let topic: String public let request: Request public let publishedAt: Date + public let derivedTopic: String? - public init(id: RPCID, topic: String, request: Request, publishedAt: Date) { + public init(id: RPCID, topic: String, request: Request, publishedAt: Date, derivedTopic: String?) { self.id = id self.topic = topic self.request = request self.publishedAt = publishedAt + self.derivedTopic = derivedTopic } } diff --git a/Sources/WalletConnectNetworking/ResponseSubscriptionPayload.swift b/Sources/WalletConnectNetworking/ResponseSubscriptionPayload.swift index cd6d1c7f5..119b849b0 100644 --- a/Sources/WalletConnectNetworking/ResponseSubscriptionPayload.swift +++ b/Sources/WalletConnectNetworking/ResponseSubscriptionPayload.swift @@ -6,12 +6,14 @@ public struct ResponseSubscriptionPayload: public let request: Request public let response: Response public let publishedAt: Date + public let derivedTopic: String? - public init(id: RPCID, topic: String, request: Request, response: Response, publishedAt: Date) { + public init(id: RPCID, topic: String, request: Request, response: Response, publishedAt: Date, derivedTopic: String?) { self.id = id self.topic = topic self.request = request self.response = response self.publishedAt = publishedAt + self.derivedTopic = derivedTopic } } diff --git a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift index c503b7ea6..dde5eb435 100644 --- a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift @@ -42,10 +42,17 @@ class ProposalResponseSubscriber { } private func handleResponse(payload: ResponseSubscriptionPayload) async throws -> PushSubscription { + + +// need to figure out how to derive peer's pub key let peerPublicKeyHex = payload.response.publicKey let selfpublicKeyHex = payload.request.publicKey + let topic = try generateAgreementKeys(peerPublicKeyHex: peerPublicKeyHex, selfpublicKeyHex: selfpublicKeyHex) + + + let pushSubscription = PushSubscription(topic: topic, account: payload.request.account, relay: relay, metadata: metadata) logger.debug("Subscribing to Push Subscription topic: \(topic)") subscriptionsStore.set(pushSubscription, forKey: topic) diff --git a/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift b/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift index 872b454b1..30dd21201 100644 --- a/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift +++ b/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift @@ -60,7 +60,7 @@ class PushRequestResponder { try await networkingInteractor.subscribe(topic: pushTopic) - let response = createJWTResponse(requestId: requestId, subscriptionAccount: requestParams.account, dappUrl: requestParams.metadata.url) + let response = try createJWTResponse(requestId: requestId, subscriptionAccount: requestParams.account, dappUrl: requestParams.metadata.url) let pushSubscription = PushSubscription(topic: pushTopic, account: requestParams.account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: requestParams.metadata) @@ -79,7 +79,7 @@ class PushRequestResponder { try await networkingInteractor.respondError(topic: pairingTopic, requestId: requestId, protocolMethod: PushRequestProtocolMethod(), reason: PushError.rejected) } - private func createJWTResponse(requestId: RPCID, subscriptionAccount: Account, dappUrl: String) -> RPCResponse { + private func createJWTResponse(requestId: RPCID, subscriptionAccount: Account, dappUrl: String) throws -> RPCResponse { let jwtPayload = AcceptSubscriptionJWTPayload(keyserver: keyserverURL, subscriptionAccount: subscriptionAccount, dappUrl: dappUrl) let wrapper = try identityClient.signAndCreateWrapper( payload: jwtPayload, From 07b8a05890c319b7adf628f78d81eb690f5db963 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 16 Mar 2023 11:12:08 +0100 Subject: [PATCH 09/18] fix build --- .../Pairing/PairingTests.swift | 4 +++- Example/IntegrationTests/Push/PushTests.swift | 5 +++-- .../Serialiser/Serializer.swift | 2 +- .../Serialiser/Serializing.swift | 5 +++-- .../PairingRequestsSubscriber.swift | 2 +- .../Client/Common/PushDecryptionService.swift | 2 +- .../Dapp/ProposalResponseSubscriber.swift | 21 +++++++------------ .../Wallet/WalletPushClientFactory.swift | 19 ++++++++++++++--- 8 files changed, 36 insertions(+), 24 deletions(-) diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift index 83e44ca16..dc0dbbfdb 100644 --- a/Example/IntegrationTests/Pairing/PairingTests.swift +++ b/Example/IntegrationTests/Pairing/PairingTests.swift @@ -70,7 +70,9 @@ final class PairingTests: XCTestCase { let pushLogger = ConsoleLogger(suffix: prefix + " [Push]", loggingLevel: .debug) walletPairingClient = pairingClient let echoClient = EchoClientFactory.create(projectId: "", clientId: "", echoHost: "echo.walletconnect.com", environment: .sandbox) - walletPushClient = WalletPushClientFactory.create(logger: pushLogger, + let keyserverURL = URL(string: "https://keys.walletconnect.com")! + walletPushClient = WalletPushClientFactory.create(keyserverURL: keyserverURL, + logger: pushLogger, keyValueStorage: keyValueStorage, keychainStorage: keychain, groupKeychainStorage: KeychainStorageMock(), diff --git a/Example/IntegrationTests/Push/PushTests.swift b/Example/IntegrationTests/Push/PushTests.swift index 61546a57f..2f5ab88ea 100644 --- a/Example/IntegrationTests/Push/PushTests.swift +++ b/Example/IntegrationTests/Push/PushTests.swift @@ -70,7 +70,9 @@ final class PushTests: XCTestCase { let pushLogger = ConsoleLogger(suffix: prefix + " [Push]", loggingLevel: .debug) walletPairingClient = pairingClient let echoClient = EchoClientFactory.create(projectId: "", clientId: "", echoHost: "echo.walletconnect.com", environment: .sandbox) - walletPushClient = WalletPushClientFactory.create(logger: pushLogger, + let keyserverURL = URL(string: "https://keys.walletconnect.com")! + walletPushClient = WalletPushClientFactory.create(keyserverURL: keyserverURL, + logger: pushLogger, keyValueStorage: keyValueStorage, keychainStorage: keychain, groupKeychainStorage: KeychainStorageMock(), @@ -106,7 +108,6 @@ final class PushTests: XCTestCase { try! await dappPushClient.request(account: Account.stub(), topic: uri.topic) walletPushClient.requestPublisher.sink { [unowned self] (id, _, _) in - Task(priority: .high) { try! await walletPushClient.approve(id: id) } }.store(in: &publishers) diff --git a/Sources/WalletConnectKMS/Serialiser/Serializer.swift b/Sources/WalletConnectKMS/Serialiser/Serializer.swift index 745d3c565..88abeda39 100644 --- a/Sources/WalletConnectKMS/Serialiser/Serializer.swift +++ b/Sources/WalletConnectKMS/Serialiser/Serializer.swift @@ -40,7 +40,7 @@ public class Serializer: Serializing { /// - topic: Topic that is associated with a symetric key for decrypting particular codable object /// - encodedEnvelope: Envelope to deserialize and decrypt /// - Returns: Deserialized object - public func deserialize(topic: String, encodedEnvelope: String) throws -> (T, String?) { + public func deserialize(topic: String, encodedEnvelope: String) throws -> (T, derivedTopic: String?) { let envelope = try Envelope(encodedEnvelope) switch envelope.type { case .type0: diff --git a/Sources/WalletConnectKMS/Serialiser/Serializing.swift b/Sources/WalletConnectKMS/Serialiser/Serializing.swift index 837a4c329..a5ee59cc9 100644 --- a/Sources/WalletConnectKMS/Serialiser/Serializing.swift +++ b/Sources/WalletConnectKMS/Serialiser/Serializing.swift @@ -2,11 +2,12 @@ import Foundation public protocol Serializing { func serialize(topic: String, encodable: Encodable, envelopeType: Envelope.EnvelopeType) throws -> String - func deserialize(topic: String, encodedEnvelope: String) throws -> (T, String?) + /// - derivedTopic: topic derived from symmetric key as a result of key exchange if peers has sent envelope prefixed with it's public key + func deserialize(topic: String, encodedEnvelope: String) throws -> (T, derivedTopic: String?) } public extension Serializing { - func tryDeserialize(topic: String, encodedEnvelope: String) -> (T, String?)? { + func tryDeserialize(topic: String, encodedEnvelope: String) -> (T, derivedTopic: String?)? { return try? deserialize(topic: topic, encodedEnvelope: encodedEnvelope) } diff --git a/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift b/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift index 66f1358db..6291cd1a4 100644 --- a/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift +++ b/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift @@ -28,7 +28,7 @@ public class PairingRequestsSubscriber { .filter { [unowned self] in !pairingProtocolMethods.contains($0.request.method)} .filter { [unowned self] in pairingStorage.hasPairing(forTopic: $0.topic)} .filter { [unowned self] in !registeredProtocolMethods.contains($0.request.method)} - .sink { [unowned self] topic, request, _ in + .sink { [unowned self] topic, request, _, _ in Task(priority: .high) { let protocolMethod = UnsupportedProtocolMethod(method: request.method) logger.debug("PairingRequestsSubscriber: responding unregistered request method") diff --git a/Sources/WalletConnectPush/Client/Common/PushDecryptionService.swift b/Sources/WalletConnectPush/Client/Common/PushDecryptionService.swift index d77c906bb..91248a150 100644 --- a/Sources/WalletConnectPush/Client/Common/PushDecryptionService.swift +++ b/Sources/WalletConnectPush/Client/Common/PushDecryptionService.swift @@ -17,7 +17,7 @@ public class PushDecryptionService { } public func decryptMessage(topic: String, ciphertext: String) throws -> PushMessage { - let rpcRequest: RPCRequest = try serializer.deserialize(topic: topic, encodedEnvelope: ciphertext) + let (rpcRequest, _): (RPCRequest, String?) = try serializer.deserialize(topic: topic, encodedEnvelope: ciphertext) guard let params = rpcRequest.params else { throw Errors.malformedPushMessage } return try params.get(PushMessage.self) } diff --git a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift index dde5eb435..1e9874562 100644 --- a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift @@ -4,6 +4,9 @@ import WalletConnectKMS import WalletConnectNetworking class ProposalResponseSubscriber { + enum Errors: Error { + case subscriptionTopicNotDerived + } private let networkingInteractor: NetworkInteracting private let kms: KeyManagementServiceProtocol private let logger: ConsoleLogging @@ -43,20 +46,12 @@ class ProposalResponseSubscriber { private func handleResponse(payload: ResponseSubscriptionPayload) async throws -> PushSubscription { + guard let subscriptionTopic = payload.derivedTopic else { throw Errors.subscriptionTopicNotDerived } -// need to figure out how to derive peer's pub key - let peerPublicKeyHex = payload.response.publicKey - - let selfpublicKeyHex = payload.request.publicKey - - let topic = try generateAgreementKeys(peerPublicKeyHex: peerPublicKeyHex, selfpublicKeyHex: selfpublicKeyHex) - - - - let pushSubscription = PushSubscription(topic: topic, account: payload.request.account, relay: relay, metadata: metadata) - logger.debug("Subscribing to Push Subscription topic: \(topic)") - subscriptionsStore.set(pushSubscription, forKey: topic) - try await networkingInteractor.subscribe(topic: topic) + let pushSubscription = PushSubscription(topic: subscriptionTopic, account: payload.request.account, relay: relay, metadata: metadata) + logger.debug("Subscribing to Push Subscription topic: \(subscriptionTopic)") + subscriptionsStore.set(pushSubscription, forKey: subscriptionTopic) + try await networkingInteractor.subscribe(topic: subscriptionTopic) return pushSubscription } diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift index 442904fcf..ee88eb8de 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift @@ -1,17 +1,19 @@ import Foundation import WalletConnectUtils import WalletConnectEcho +import WalletConnectIdentity public struct WalletPushClientFactory { public static func create(networkInteractor: NetworkInteracting, pairingRegisterer: PairingRegisterer, echoClient: EchoClient) -> WalletPushClient { let logger = ConsoleLogger(loggingLevel: .debug) let keyValueStorage = UserDefaults.standard - + let keyserverURL = URL(string: "https://keys.walletconnect.com")! let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") let groupKeychainService = GroupKeychainStorage(serviceIdentifier: "group.com.walletconnect.sdk") return WalletPushClientFactory.create( + keyserverURL: keyserverURL, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, @@ -22,14 +24,25 @@ public struct WalletPushClientFactory { ) } - static func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, groupKeychainStorage: KeychainStorageProtocol, networkInteractor: NetworkInteracting, pairingRegisterer: PairingRegisterer, echoClient: EchoClient) -> WalletPushClient { + static func create( + keyserverURL: URL, + logger: ConsoleLogging, + keyValueStorage: KeyValueStorage, + keychainStorage: KeychainStorageProtocol, + groupKeychainStorage: KeychainStorageProtocol, + networkInteractor: NetworkInteracting, + pairingRegisterer: PairingRegisterer, + echoClient: EchoClient + ) -> WalletPushClient { let kms = KeyManagementService(keychain: keychainStorage) let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) let subscriptionStore = CodableStore(defaults: keyValueStorage, identifier: PushStorageIdntifiers.pushSubscription) - let proposeResponder = PushRequestResponder(networkingInteractor: networkInteractor, logger: logger, kms: kms, groupKeychainStorage: groupKeychainStorage, rpcHistory: history, subscriptionsStore: subscriptionStore) + let identityClient = IdentityClientFactory.create(keyserver: keyserverURL, keychain: keychainStorage, logger: logger) + + let proposeResponder = PushRequestResponder(keyserverURL: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, logger: logger, kms: kms, groupKeychainStorage: groupKeychainStorage, rpcHistory: history, subscriptionsStore: subscriptionStore) let pushMessagesRecordsStore = CodableStore(defaults: keyValueStorage, identifier: PushStorageIdntifiers.pushMessagesRecords) let pushMessagesDatabase = PushMessagesDatabase(store: pushMessagesRecordsStore) From 2c2e7c3b2d03e5421c441888f7a8542193518f03 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 16 Mar 2023 11:52:32 +0100 Subject: [PATCH 10/18] Add jwt response validation --- .../Client/Dapp/DappPushClient.swift | 4 ++-- .../Dapp/ProposalResponseSubscriber.swift | 19 ++++++++++++++----- .../AcceptSubscriptionJWTPayload.swift | 2 +- .../Types/PushSubscriptionResult.swift | 7 +++++++ 4 files changed, 24 insertions(+), 8 deletions(-) create mode 100644 Sources/WalletConnectPush/Types/PushSubscriptionResult.swift diff --git a/Sources/WalletConnectPush/Client/Dapp/DappPushClient.swift b/Sources/WalletConnectPush/Client/Dapp/DappPushClient.swift index f95235373..d267b68f1 100644 --- a/Sources/WalletConnectPush/Client/Dapp/DappPushClient.swift +++ b/Sources/WalletConnectPush/Client/Dapp/DappPushClient.swift @@ -4,9 +4,9 @@ import WalletConnectUtils public class DappPushClient { - private let responsePublisherSubject = PassthroughSubject<(id: RPCID, result: Result), Never>() + private let responsePublisherSubject = PassthroughSubject<(id: RPCID, result: Result), Never>() - public var responsePublisher: AnyPublisher<(id: RPCID, result: Result), Never> { + public var responsePublisher: AnyPublisher<(id: RPCID, result: Result), Never> { responsePublisherSubject.eraseToAnyPublisher() } diff --git a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift index 1e9874562..727bcd171 100644 --- a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift @@ -13,7 +13,7 @@ class ProposalResponseSubscriber { private var publishers = [AnyCancellable]() private let metadata: AppMetadata private let relay: RelayProtocolOptions - var onResponse: ((_ id: RPCID, _ result: Result) -> Void)? + var onResponse: ((_ id: RPCID, _ result: Result) -> Void)? private let subscriptionsStore: CodableStore init(networkingInteractor: NetworkInteracting, @@ -38,13 +38,22 @@ class ProposalResponseSubscriber { .sink { [unowned self] (payload: ResponseSubscriptionPayload) in logger.debug("Received Push Proposal response") Task(priority: .userInitiated) { - let pushSubscription = try await handleResponse(payload: payload) - onResponse?(payload.id, .success(pushSubscription)) + do { + let (pushSubscription, jwt) = try await handleResponse(payload: payload) + let result = PushSubscriptionResult(pushSubscription: pushSubscription, subscriptionAuth: jwt) + onResponse?(payload.id, .success(result)) + } catch { + logger.error(error) + } } }.store(in: &publishers) } - private func handleResponse(payload: ResponseSubscriptionPayload) async throws -> PushSubscription { + private func handleResponse(payload: ResponseSubscriptionPayload) async throws -> (PushSubscription, String) { + + let jwt = payload.response.jwtString + _ = try AcceptSubscriptionJWTPayload.decode(from: payload.response) + logger.debug("subscriptionAuth JWT validated") guard let subscriptionTopic = payload.derivedTopic else { throw Errors.subscriptionTopicNotDerived } @@ -52,7 +61,7 @@ class ProposalResponseSubscriber { logger.debug("Subscribing to Push Subscription topic: \(subscriptionTopic)") subscriptionsStore.set(pushSubscription, forKey: subscriptionTopic) try await networkingInteractor.subscribe(topic: subscriptionTopic) - return pushSubscription + return (pushSubscription, jwt) } private func generateAgreementKeys(peerPublicKeyHex: String, selfpublicKeyHex: String) throws -> String { diff --git a/Sources/WalletConnectPush/RPCRequests/AcceptSubscriptionJWTPayload.swift b/Sources/WalletConnectPush/RPCRequests/AcceptSubscriptionJWTPayload.swift index 0fed6bc69..3ca3f0bcc 100644 --- a/Sources/WalletConnectPush/RPCRequests/AcceptSubscriptionJWTPayload.swift +++ b/Sources/WalletConnectPush/RPCRequests/AcceptSubscriptionJWTPayload.swift @@ -47,7 +47,7 @@ struct AcceptSubscriptionJWTPayload: JWTClaimsCodable { func encode(iss: String) throws -> Claims { return Claims( - iat: expiry(days: 30), + iat: expiry(days: 1), exp: defaultIatMilliseconds(), iss: iss, ksu: keyserver.absoluteString, diff --git a/Sources/WalletConnectPush/Types/PushSubscriptionResult.swift b/Sources/WalletConnectPush/Types/PushSubscriptionResult.swift new file mode 100644 index 000000000..93e77293b --- /dev/null +++ b/Sources/WalletConnectPush/Types/PushSubscriptionResult.swift @@ -0,0 +1,7 @@ + +import Foundation + +public struct PushSubscriptionResult: Equatable, Codable { + let pushSubscription: PushSubscription + let subscriptionAuth: String +} From 6f3f6d5b80be5f75c299b8c2dd5d5ab4d7dc6043 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 16 Mar 2023 11:54:45 +0100 Subject: [PATCH 11/18] fix tests compitalion --- Example/IntegrationTests/Push/PushTests.swift | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Example/IntegrationTests/Push/PushTests.swift b/Example/IntegrationTests/Push/PushTests.swift index 2f5ab88ea..201799689 100644 --- a/Example/IntegrationTests/Push/PushTests.swift +++ b/Example/IntegrationTests/Push/PushTests.swift @@ -158,12 +158,12 @@ final class PushTests: XCTestCase { }.store(in: &publishers) dappPushClient.responsePublisher.sink { [unowned self] (_, result) in - guard case .success(let subscription) = result else { + guard case .success(let result) = result else { XCTFail() return } - pushSubscription = subscription - Task(priority: .userInitiated) { try! await dappPushClient.notify(topic: subscription.topic, message: pushMessage) } + pushSubscription = result.pushSubscription + Task(priority: .userInitiated) { try! await dappPushClient.notify(topic: result.pushSubscription.topic, message: pushMessage) } }.store(in: &publishers) walletPushClient.pushMessagePublisher.sink { [unowned self] receivedPushMessageRecord in @@ -190,12 +190,12 @@ final class PushTests: XCTestCase { }.store(in: &publishers) dappPushClient.responsePublisher.sink { [unowned self] (_, result) in - guard case .success(let subscription) = result else { + guard case .success(let result) = result else { XCTFail() return } - subscriptionTopic = subscription.topic - Task(priority: .userInitiated) { try! await walletPushClient.deleteSubscription(topic: subscription.topic)} + subscriptionTopic = result.pushSubscription.topic + Task(priority: .userInitiated) { try! await walletPushClient.deleteSubscription(topic: result.pushSubscription.topic)} }.store(in: &publishers) dappPushClient.deleteSubscriptionPublisher.sink { topic in @@ -217,12 +217,12 @@ final class PushTests: XCTestCase { }.store(in: &publishers) dappPushClient.responsePublisher.sink { [unowned self] (_, result) in - guard case .success(let subscription) = result else { + guard case .success(let result) = result else { XCTFail() return } - subscriptionTopic = subscription.topic - Task(priority: .userInitiated) { try! await dappPushClient.delete(topic: subscription.topic)} + subscriptionTopic = result.pushSubscription.topic + Task(priority: .userInitiated) { try! await dappPushClient.delete(topic: result.pushSubscription.topic)} }.store(in: &publishers) walletPushClient.deleteSubscriptionPublisher.sink { topic in From 5a22257e37af034c5dd4c0d6a86d37292fbfc304 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 20 Mar 2023 10:14:22 +0100 Subject: [PATCH 12/18] register account on approve --- Example/IntegrationTests/Push/PushTests.swift | 16 ++++++++++++---- Example/IntegrationTests/Stubs/Account.swift | 2 +- .../WalletConnectIdentity/IdentityClient.swift | 9 +++++++++ .../Client/Wallet/PushRequestResponder.swift | 5 ++++- .../Client/Wallet/WalletPushClient.swift | 5 +++-- 5 files changed, 29 insertions(+), 8 deletions(-) diff --git a/Example/IntegrationTests/Push/PushTests.swift b/Example/IntegrationTests/Push/PushTests.swift index 201799689..2ba203c8b 100644 --- a/Example/IntegrationTests/Push/PushTests.swift +++ b/Example/IntegrationTests/Push/PushTests.swift @@ -8,6 +8,8 @@ import WalletConnectNetworking import WalletConnectEcho @testable import WalletConnectPush @testable import WalletConnectPairing +import WalletConnectIdentity +import WalletConnectSigner final class PushTests: XCTestCase { @@ -108,7 +110,7 @@ final class PushTests: XCTestCase { try! await dappPushClient.request(account: Account.stub(), topic: uri.topic) walletPushClient.requestPublisher.sink { [unowned self] (id, _, _) in - Task(priority: .high) { try! await walletPushClient.approve(id: id) } + Task(priority: .high) { try! await walletPushClient.approve(id: id, onSign: sign) } }.store(in: &publishers) dappPushClient.responsePublisher.sink { (_, result) in @@ -154,7 +156,7 @@ final class PushTests: XCTestCase { try! await dappPushClient.request(account: Account.stub(), topic: uri.topic) walletPushClient.requestPublisher.sink { [unowned self] (id, _, _) in - Task(priority: .high) { try! await walletPushClient.approve(id: id) } + Task(priority: .high) { try! await walletPushClient.approve(id: id, onSign: sign) } }.store(in: &publishers) dappPushClient.responsePublisher.sink { [unowned self] (_, result) in @@ -186,7 +188,7 @@ final class PushTests: XCTestCase { var subscriptionTopic: String! walletPushClient.requestPublisher.sink { [unowned self] (id, _, _) in - Task(priority: .high) { try! await walletPushClient.approve(id: id) } + Task(priority: .high) { try! await walletPushClient.approve(id: id, onSign: sign) } }.store(in: &publishers) dappPushClient.responsePublisher.sink { [unowned self] (_, result) in @@ -213,7 +215,7 @@ final class PushTests: XCTestCase { var subscriptionTopic: String! walletPushClient.requestPublisher.sink { [unowned self] (id, _, _) in - Task(priority: .high) { try! await walletPushClient.approve(id: id) } + Task(priority: .high) { try! await walletPushClient.approve(id: id, onSign: sign) } }.store(in: &publishers) dappPushClient.responsePublisher.sink { [unowned self] (_, result) in @@ -231,4 +233,10 @@ final class PushTests: XCTestCase { }.store(in: &publishers) wait(for: [expectation], timeout: InputConfig.defaultTimeout) } + + private func sign(_ message: String) -> SigningResult { + let privateKey = Data(hex: "305c6cde3846927892cd32762f6120539f3ec74c9e3a16b9b798b1e85351ae2a") + let signer = MessageSignerFactory(signerFactory: DefaultSignerFactory()).create(projectId: InputConfig.projectId) + return .signed(try! signer.sign(message: message, privateKey: privateKey, type: .eip191)) + } } diff --git a/Example/IntegrationTests/Stubs/Account.swift b/Example/IntegrationTests/Stubs/Account.swift index 3363fc459..986090e41 100644 --- a/Example/IntegrationTests/Stubs/Account.swift +++ b/Example/IntegrationTests/Stubs/Account.swift @@ -3,6 +3,6 @@ import WalletConnectUtils extension Account { static func stub() -> Account { - return Account(chainIdentifier: "eip155:1", address: "0x724d0D2DaD3fbB0C168f947B87Fa5DBe36F1A8bf")! + return Account(chainIdentifier: "eip155:1", address: "0x15bca56b6e2728aec2532df9d436bd1600e86688")! } } diff --git a/Sources/WalletConnectIdentity/IdentityClient.swift b/Sources/WalletConnectIdentity/IdentityClient.swift index bbccb14b5..f8a709273 100644 --- a/Sources/WalletConnectIdentity/IdentityClient.swift +++ b/Sources/WalletConnectIdentity/IdentityClient.swift @@ -28,6 +28,15 @@ public final class IdentityClient { return pubKey } + public func registerIfNeeded(account: Account, onSign: SigningCallback) async throws { + if let _ = try? identityStorage.getIdentityKey(for: account) { + return + } else { + let _ = try await identityService.registerIdentity(account: account, onSign: onSign) + logger.debug("Did register an account: \(account)") + } + } + public func goPublic(account: Account) async throws -> AgreementPublicKey { let inviteKey = try await identityService.registerInvite(account: account) logger.debug("Did goPublic an account: \(account)") diff --git a/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift b/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift index 30dd21201..99194550d 100644 --- a/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift +++ b/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift @@ -37,7 +37,8 @@ class PushRequestResponder { self.subscriptionsStore = subscriptionsStore } - func respond(requestId: RPCID) async throws { + func respond(requestId: RPCID, onSign: SigningCallback) async throws { + logger.debug("Approving Push Proposal") let requestRecord = try getRecord(requestId: requestId) @@ -48,6 +49,8 @@ class PushRequestResponder { let pushTopic = keys.derivedTopic() let requestParams = try requestRecord.request.params!.get(PushRequestParams.self) + try await identityClient.registerIfNeeded(account: requestParams.account, onSign: onSign) + try kms.setAgreementSecret(keys, topic: responseTopic) logger.debug("PushRequestResponder: responding on response topic \(responseTopic) \(pushTopic)") diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift index 7a55a6b65..450095eca 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift @@ -3,6 +3,7 @@ import Combine import WalletConnectNetworking import WalletConnectPairing import WalletConnectEcho +import WalletConnectIdentity public class WalletPushClient { @@ -72,8 +73,8 @@ public class WalletPushClient { setupSubscriptions() } - public func approve(id: RPCID) async throws { - try await proposeResponder.respond(requestId: id) + public func approve(id: RPCID, onSign: SigningCallback) async throws { + try await proposeResponder.respond(requestId: id, onSign: onSign) } public func reject(id: RPCID) async throws { From 78f9365be1545e078334b6bb24a2dfcd541c8b69 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 20 Mar 2023 17:23:24 +0100 Subject: [PATCH 13/18] savepoint --- .../Wallet/PushRequest/PushRequestInteractor.swift | 9 ++++++++- Sources/WalletConnectPush/PushImports.swift | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift index eb6d6f329..c50f28fc5 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift @@ -3,10 +3,17 @@ import WalletConnectPush final class PushRequestInteractor { func approve(pushRequest: PushRequest) async throws { - try await Push.wallet.approve(id: pushRequest.id) + try await Push.wallet.approve(id: pushRequest.id, onSign: onSign) } func reject(pushRequest: PushRequest) async throws { try await Push.wallet.reject(id: pushRequest.id) } + + private func onSing(_ message: String) -> SigningResult { + let privateKey = Data(hex: "e56da0e170b5e09a8bb8f1b693392c7d56c3739a9c75740fbc558a2877868540") + let signer = MessageSignerFactory(signerFactory: DefaultSignerFactory()).create() + let signature = try! signer.sign(message: message, privateKey: privateKey, type: .eip191) + return .signed(signature) + } } diff --git a/Sources/WalletConnectPush/PushImports.swift b/Sources/WalletConnectPush/PushImports.swift index 27245bda6..98325f10c 100644 --- a/Sources/WalletConnectPush/PushImports.swift +++ b/Sources/WalletConnectPush/PushImports.swift @@ -1,3 +1,5 @@ #if !CocoaPods @_exported import WalletConnectPairing +@_exported import WalletConnectSigner +@_exported import WalletConnectIdentity #endif From 2e0939f41b9eb0594efaca70915ff98054e52d62 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 24 Mar 2023 14:27:38 +0100 Subject: [PATCH 14/18] integrate updated push protocol into sample apps --- Example/DApp/Sign/Accounts/AccountsViewController.swift | 6 +++--- .../Wallet/PushRequest/PushRequestInteractor.swift | 4 ++-- Sources/WalletConnectIdentity/IdentityClient.swift | 9 --------- .../Client/Wallet/PushRequestResponder.swift | 4 ++-- .../Client/Wallet/WalletPushClient.swift | 3 +-- .../WalletConnectPush/Types/PushSubscriptionResult.swift | 4 ++-- 6 files changed, 10 insertions(+), 20 deletions(-) diff --git a/Example/DApp/Sign/Accounts/AccountsViewController.swift b/Example/DApp/Sign/Accounts/AccountsViewController.swift index d08e5287c..f51b87b0c 100644 --- a/Example/DApp/Sign/Accounts/AccountsViewController.swift +++ b/Example/DApp/Sign/Accounts/AccountsViewController.swift @@ -66,10 +66,10 @@ final class AccountsViewController: UIViewController, UITableViewDataSource, UIT let account = session.namespaces.values.first!.accounts.first! Task(priority: .high){ try! await Push.dapp.request(account: account, topic: session.pairingTopic)} - Push.dapp.responsePublisher.sink { (id: RPCID, result: Result) in + Push.dapp.responsePublisher.sink { (id: RPCID, result: Result) in switch result { - case .success(let subscription): - self.pushSubscription = subscription + case .success(let subscriptionResult): + self.pushSubscription = subscriptionResult.pushSubscription case .failure(let error): print(error) } diff --git a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift index c50f28fc5..e6a52aa89 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift @@ -3,14 +3,14 @@ import WalletConnectPush final class PushRequestInteractor { func approve(pushRequest: PushRequest) async throws { - try await Push.wallet.approve(id: pushRequest.id, onSign: onSign) + try await Push.wallet.approve(id: pushRequest.id, onSign: onSing(_:)) } func reject(pushRequest: PushRequest) async throws { try await Push.wallet.reject(id: pushRequest.id) } - private func onSing(_ message: String) -> SigningResult { + func onSing(_ message: String) async -> SigningResult { let privateKey = Data(hex: "e56da0e170b5e09a8bb8f1b693392c7d56c3739a9c75740fbc558a2877868540") let signer = MessageSignerFactory(signerFactory: DefaultSignerFactory()).create() let signature = try! signer.sign(message: message, privateKey: privateKey, type: .eip191) diff --git a/Sources/WalletConnectIdentity/IdentityClient.swift b/Sources/WalletConnectIdentity/IdentityClient.swift index f8a709273..bbccb14b5 100644 --- a/Sources/WalletConnectIdentity/IdentityClient.swift +++ b/Sources/WalletConnectIdentity/IdentityClient.swift @@ -28,15 +28,6 @@ public final class IdentityClient { return pubKey } - public func registerIfNeeded(account: Account, onSign: SigningCallback) async throws { - if let _ = try? identityStorage.getIdentityKey(for: account) { - return - } else { - let _ = try await identityService.registerIdentity(account: account, onSign: onSign) - logger.debug("Did register an account: \(account)") - } - } - public func goPublic(account: Account) async throws -> AgreementPublicKey { let inviteKey = try await identityService.registerInvite(account: account) logger.debug("Did goPublic an account: \(account)") diff --git a/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift b/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift index 99194550d..ae7b83357 100644 --- a/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift +++ b/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift @@ -37,7 +37,7 @@ class PushRequestResponder { self.subscriptionsStore = subscriptionsStore } - func respond(requestId: RPCID, onSign: SigningCallback) async throws { + func respond(requestId: RPCID, onSign: @escaping SigningCallback) async throws { logger.debug("Approving Push Proposal") @@ -49,7 +49,7 @@ class PushRequestResponder { let pushTopic = keys.derivedTopic() let requestParams = try requestRecord.request.params!.get(PushRequestParams.self) - try await identityClient.registerIfNeeded(account: requestParams.account, onSign: onSign) + _ = try await identityClient.register(account: requestParams.account, onSign: onSign) try kms.setAgreementSecret(keys, topic: responseTopic) diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift index 450095eca..c7848a79e 100644 --- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift +++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift @@ -3,7 +3,6 @@ import Combine import WalletConnectNetworking import WalletConnectPairing import WalletConnectEcho -import WalletConnectIdentity public class WalletPushClient { @@ -73,7 +72,7 @@ public class WalletPushClient { setupSubscriptions() } - public func approve(id: RPCID, onSign: SigningCallback) async throws { + public func approve(id: RPCID, onSign: @escaping SigningCallback) async throws { try await proposeResponder.respond(requestId: id, onSign: onSign) } diff --git a/Sources/WalletConnectPush/Types/PushSubscriptionResult.swift b/Sources/WalletConnectPush/Types/PushSubscriptionResult.swift index 93e77293b..3e7044917 100644 --- a/Sources/WalletConnectPush/Types/PushSubscriptionResult.swift +++ b/Sources/WalletConnectPush/Types/PushSubscriptionResult.swift @@ -2,6 +2,6 @@ import Foundation public struct PushSubscriptionResult: Equatable, Codable { - let pushSubscription: PushSubscription - let subscriptionAuth: String + public let pushSubscription: PushSubscription + public let subscriptionAuth: String } From d6609a52f4e931f2972a9ff22d53f2e0072e3eec Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 28 Mar 2023 10:22:05 +0200 Subject: [PATCH 15/18] rename decode and verify method --- Sources/Chat/ProtocolServices/Common/MessagingService.swift | 6 +++--- .../Invitee/InvitationHandlingService.swift | 2 +- Sources/Chat/ProtocolServices/Inviter/InviteService.swift | 4 ++-- Sources/WalletConnectJWT/JWTDecodable.swift | 2 +- Sources/WalletConnectKMS/Serialiser/Serializing.swift | 3 ++- .../Client/Dapp/ProposalResponseSubscriber.swift | 2 +- 6 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Sources/Chat/ProtocolServices/Common/MessagingService.swift b/Sources/Chat/ProtocolServices/Common/MessagingService.swift index 4beaa3e27..783aa0a24 100644 --- a/Sources/Chat/ProtocolServices/Common/MessagingService.swift +++ b/Sources/Chat/ProtocolServices/Common/MessagingService.swift @@ -64,8 +64,8 @@ private extension MessagingService { logger.debug("Received Receipt response") guard - let (message, _) = try? MessagePayload.decode(from: payload.request), - let (receipt, _) = try? ReceiptPayload.decode(from: payload.response) + let (message, _) = try? MessagePayload.decodeAndVerify(from: payload.request), + let (receipt, _) = try? ReceiptPayload.decodeAndVerify(from: payload.response) else { fatalError() /* TODO: Handle error */ } let newMessage = Message( @@ -85,7 +85,7 @@ private extension MessagingService { logger.debug("Received Message Request") - guard let (message, messageClaims) = try? MessagePayload.decode(from: payload.request) + guard let (message, messageClaims) = try? MessagePayload.decodeAndVerify(from: payload.request) else { fatalError() /* TODO: Handle error */ } Task(priority: .high) { diff --git a/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift b/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift index 75fcffcb4..80d1e45f5 100644 --- a/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift +++ b/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift @@ -115,7 +115,7 @@ private extension InvitationHandlingService { .sink { [unowned self] (payload: RequestSubscriptionPayload) in logger.debug("Did receive an invite") - guard let (invite, claims) = try? InvitePayload.decode(from: payload.request) + guard let (invite, claims) = try? InvitePayload.decodeAndVerify(from: payload.request) else { fatalError() /* TODO: Handle error */ } Task(priority: .high) { diff --git a/Sources/Chat/ProtocolServices/Inviter/InviteService.swift b/Sources/Chat/ProtocolServices/Inviter/InviteService.swift index bde61bb5f..cc0eca34f 100644 --- a/Sources/Chat/ProtocolServices/Inviter/InviteService.swift +++ b/Sources/Chat/ProtocolServices/Inviter/InviteService.swift @@ -88,8 +88,8 @@ private extension InviteService { logger.debug("Invite has been accepted") guard - let (invite, _) = try? InvitePayload.decode(from: payload.request), - let (accept, _) = try? AcceptPayload.decode(from: payload.response) + let (invite, _) = try? InvitePayload.decodeAndVerify(from: payload.request), + let (accept, _) = try? AcceptPayload.decodeAndVerify(from: payload.response) else { fatalError() /* TODO: Handle error */ } Task(priority: .high) { diff --git a/Sources/WalletConnectJWT/JWTDecodable.swift b/Sources/WalletConnectJWT/JWTDecodable.swift index 58fe76ca6..f28493016 100644 --- a/Sources/WalletConnectJWT/JWTDecodable.swift +++ b/Sources/WalletConnectJWT/JWTDecodable.swift @@ -23,7 +23,7 @@ public protocol JWTClaimsCodable { extension JWTClaimsCodable { - public static func decode(from wrapper: Wrapper) throws -> (Self, Claims) { + public static func decodeAndVerify(from wrapper: Wrapper) throws -> (Self, Claims) { let jwt = try JWT(string: wrapper.jwtString) let publicKey = try DIDKey(did: jwt.claims.iss) diff --git a/Sources/WalletConnectKMS/Serialiser/Serializing.swift b/Sources/WalletConnectKMS/Serialiser/Serializing.swift index a5ee59cc9..81d37229f 100644 --- a/Sources/WalletConnectKMS/Serialiser/Serializing.swift +++ b/Sources/WalletConnectKMS/Serialiser/Serializing.swift @@ -2,11 +2,12 @@ import Foundation public protocol Serializing { func serialize(topic: String, encodable: Encodable, envelopeType: Envelope.EnvelopeType) throws -> String - /// - derivedTopic: topic derived from symmetric key as a result of key exchange if peers has sent envelope prefixed with it's public key + /// - derivedTopic: topic derived from symmetric key as a result of key exchange if peers has sent envelope(type1) prefixed with it's public key func deserialize(topic: String, encodedEnvelope: String) throws -> (T, derivedTopic: String?) } public extension Serializing { + /// - derivedTopic: topic derived from symmetric key as a result of key exchange if peers has sent envelope(type1) prefixed with it's public key func tryDeserialize(topic: String, encodedEnvelope: String) -> (T, derivedTopic: String?)? { return try? deserialize(topic: topic, encodedEnvelope: encodedEnvelope) } diff --git a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift index 727bcd171..603b81bbb 100644 --- a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift +++ b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift @@ -52,7 +52,7 @@ class ProposalResponseSubscriber { private func handleResponse(payload: ResponseSubscriptionPayload) async throws -> (PushSubscription, String) { let jwt = payload.response.jwtString - _ = try AcceptSubscriptionJWTPayload.decode(from: payload.response) + _ = try AcceptSubscriptionJWTPayload.decodeAndVerify(from: payload.response) logger.debug("subscriptionAuth JWT validated") guard let subscriptionTopic = payload.derivedTopic else { throw Errors.subscriptionTopicNotDerived } From 07bc5c49f93418c210aa45febe9f89ff3af44b36 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 29 Mar 2023 11:19:27 +0200 Subject: [PATCH 16/18] Fix unit tests --- .../AuthTests/AppRespondSubscriberTests.swift | 2 +- .../WalletRequestSubscriberTests.swift | 2 +- .../NetworkingInteractorMock.swift | 18 +++++++++--------- .../SerialiserTests.swift | 4 ++-- .../AppProposalServiceTests.swift | 6 +++--- .../ApproveEngineTests.swift | 10 +++++----- ...NonControllerSessionStateMachineTests.swift | 14 +++++++------- .../SessionEngineTests.swift | 2 +- 8 files changed, 29 insertions(+), 29 deletions(-) diff --git a/Tests/AuthTests/AppRespondSubscriberTests.swift b/Tests/AuthTests/AppRespondSubscriberTests.swift index cd2fb8f5b..3ec36ed13 100644 --- a/Tests/AuthTests/AppRespondSubscriberTests.swift +++ b/Tests/AuthTests/AppRespondSubscriberTests.swift @@ -65,7 +65,7 @@ class AppRespondSubscriberTests: XCTestCase { let cacao = Cacao(h: cacaoHeader, p: cacaoPayload, s: cacaoSignature) let response = RPCResponse(id: requestId, result: cacao) - networkingInteractor.responsePublisherSubject.send((topic, request, response, Date())) + networkingInteractor.responsePublisherSubject.send((topic, request, response, Date(), nil)) wait(for: [messageExpectation], timeout: defaultTimeout) XCTAssertTrue(pairingRegisterer.isActivateCalled) diff --git a/Tests/AuthTests/WalletRequestSubscriberTests.swift b/Tests/AuthTests/WalletRequestSubscriberTests.swift index 2f504b49d..4cb6e3c68 100644 --- a/Tests/AuthTests/WalletRequestSubscriberTests.swift +++ b/Tests/AuthTests/WalletRequestSubscriberTests.swift @@ -41,7 +41,7 @@ class WalletRequestSubscriberTests: XCTestCase { messageExpectation.fulfill() } - let payload = RequestSubscriptionPayload(id: expectedRequestId, topic: "123", request: AuthRequestParams.stub(id: expectedRequestId, iat: iat), publishedAt: Date()) + let payload = RequestSubscriptionPayload(id: expectedRequestId, topic: "123", request: AuthRequestParams.stub(id: expectedRequestId, iat: iat), publishedAt: Date(), derivedTopic: nil) pairingRegisterer.subject.send(payload) diff --git a/Tests/TestingUtils/NetworkingInteractorMock.swift b/Tests/TestingUtils/NetworkingInteractorMock.swift index 18da4e159..ed6edbd63 100644 --- a/Tests/TestingUtils/NetworkingInteractorMock.swift +++ b/Tests/TestingUtils/NetworkingInteractorMock.swift @@ -30,14 +30,14 @@ public class NetworkingInteractorMock: NetworkInteracting { socketConnectionStatusPublisherSubject.eraseToAnyPublisher() } - public let requestPublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, publishedAt: Date), Never>() - public let responsePublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, response: RPCResponse, publishedAt: Date), Never>() + public let requestPublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, publishedAt: Date, derivedTopic: String?), Never>() + public let responsePublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, response: RPCResponse, publishedAt: Date, derivedTopic: String?), Never>() - public var requestPublisher: AnyPublisher<(topic: String, request: RPCRequest, publishedAt: Date), Never> { + public var requestPublisher: AnyPublisher<(topic: String, request: JSONRPC.RPCRequest, publishedAt: Date, derivedTopic: String?), Never> { requestPublisherSubject.eraseToAnyPublisher() } - private var responsePublisher: AnyPublisher<(topic: String, request: RPCRequest, response: RPCResponse, publishedAt: Date), Never> { + private var responsePublisher: AnyPublisher<(topic: String, request: RPCRequest, response: RPCResponse, publishedAt: Date, derivedTopic: String?), Never> { responsePublisherSubject.eraseToAnyPublisher() } @@ -47,9 +47,9 @@ public class NetworkingInteractorMock: NetworkInteracting { .filter { rpcRequest in return rpcRequest.request.method == request.method } - .compactMap { topic, rpcRequest, publishedAt in + .compactMap { topic, rpcRequest, publishedAt, derivedTopic in guard let id = rpcRequest.id, let request = try? rpcRequest.params?.get(Request.self) else { return nil } - return RequestSubscriptionPayload(id: id, topic: topic, request: request, publishedAt: publishedAt) + return RequestSubscriptionPayload(id: id, topic: topic, request: request, publishedAt: publishedAt, derivedTopic: derivedTopic) } .eraseToAnyPublisher() } @@ -60,12 +60,12 @@ public class NetworkingInteractorMock: NetworkInteracting { .filter { rpcRequest in return rpcRequest.request.method == request.method } - .compactMap { topic, rpcRequest, rpcResponse, publishedAt in + .compactMap { topic, rpcRequest, rpcResponse, publishedAt, derivedTopic in guard let id = rpcRequest.id, let request = try? rpcRequest.params?.get(Request.self), let response = try? rpcResponse.result?.get(Response.self) else { return nil } - return ResponseSubscriptionPayload(id: id, topic: topic, request: request, response: response, publishedAt: publishedAt) + return ResponseSubscriptionPayload(id: id, topic: topic, request: request, response: response, publishedAt: publishedAt, derivedTopic: derivedTopic) } .eraseToAnyPublisher() } @@ -74,7 +74,7 @@ public class NetworkingInteractorMock: NetworkInteracting { public func responseErrorSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { return responsePublisher .filter { $0.request.method == request.method } - .compactMap { (topic, rpcRequest, rpcResponse, publishedAt) in + .compactMap { (topic, rpcRequest, rpcResponse, publishedAt, _) in guard let id = rpcResponse.id, let request = try? rpcRequest.params?.get(Request.self), let error = rpcResponse.error else { return nil } return ResponseSubscriptionErrorPayload(id: id, topic: topic, request: request, error: error) } diff --git a/Tests/WalletConnectKMSTests/SerialiserTests.swift b/Tests/WalletConnectKMSTests/SerialiserTests.swift index 2c6ca0384..02822ff25 100644 --- a/Tests/WalletConnectKMSTests/SerialiserTests.swift +++ b/Tests/WalletConnectKMSTests/SerialiserTests.swift @@ -23,7 +23,7 @@ final class SerializerTests: XCTestCase { _ = try! myKms.createSymmetricKey(topic) let messageToSerialize = "todo - change for request object" let serializedMessage = try! mySerializer.serialize(topic: topic, encodable: messageToSerialize, envelopeType: .type0) - let deserializedMessage: String? = mySerializer.tryDeserialize(topic: topic, encodedEnvelope: serializedMessage) + let (deserializedMessage, _): (String, String?) = mySerializer.tryDeserialize(topic: topic, encodedEnvelope: serializedMessage)! XCTAssertEqual(messageToSerialize, deserializedMessage) } @@ -39,7 +39,7 @@ final class SerializerTests: XCTestCase { let serializedMessage = try! peerSerializer.serialize(topic: topic, encodable: messageToSerialize, envelopeType: .type1(pubKey: peerPubKey.rawRepresentation)) print(agreementKeys.sharedKey.hexRepresentation) // -----------Me Deserialising ------------------- - let deserializedMessage: String? = mySerializer.tryDeserialize(topic: topic, encodedEnvelope: serializedMessage) + let (deserializedMessage, _): (String, String?) = mySerializer.tryDeserialize(topic: topic, encodedEnvelope: serializedMessage)! XCTAssertEqual(messageToSerialize, deserializedMessage) } } diff --git a/Tests/WalletConnectSignTests/AppProposalServiceTests.swift b/Tests/WalletConnectSignTests/AppProposalServiceTests.swift index f516ffad2..27a48066d 100644 --- a/Tests/WalletConnectSignTests/AppProposalServiceTests.swift +++ b/Tests/WalletConnectSignTests/AppProposalServiceTests.swift @@ -114,7 +114,7 @@ final class AppProposalServiceTests: XCTestCase { exp.fulfill() } - networkingInteractor.responsePublisherSubject.send((topicA, request, response, Date())) + networkingInteractor.responsePublisherSubject.send((topicA, request, response, Date(), nil)) let privateKey = try! cryptoMock.getPrivateKey(for: proposal.proposer.publicKey)! let topicB = deriveTopic(publicKey: responder.publicKey, privateKey: privateKey) _ = storageMock.getPairing(forTopic: topicA)! @@ -143,7 +143,7 @@ final class AppProposalServiceTests: XCTestCase { } let response = RPCResponse.stubError(forRequest: request) - networkingInteractor.responsePublisherSubject.send((topicA, request, response, Date())) + networkingInteractor.responsePublisherSubject.send((topicA, request, response, Date(), nil)) XCTAssert(networkingInteractor.didUnsubscribe(to: pairing.topic), "Proposer must unsubscribe if pairing is inactive.") XCTAssertFalse(storageMock.hasPairing(forTopic: pairing.topic), "Proposer must delete an inactive pairing.") @@ -171,7 +171,7 @@ final class AppProposalServiceTests: XCTestCase { storageMock.setPairing(storedPairing) let response = RPCResponse.stubError(forRequest: request) - networkingInteractor.responsePublisherSubject.send((topicA, request, response, Date())) + networkingInteractor.responsePublisherSubject.send((topicA, request, response, Date(), nil)) XCTAssertFalse(networkingInteractor.didUnsubscribe(to: pairing.topic), "Proposer must not unsubscribe if pairing is active.") XCTAssert(storageMock.hasPairing(forTopic: pairing.topic), "Proposer must not delete an active pairing.") diff --git a/Tests/WalletConnectSignTests/ApproveEngineTests.swift b/Tests/WalletConnectSignTests/ApproveEngineTests.swift index e4fd7ec11..3bcc34e01 100644 --- a/Tests/WalletConnectSignTests/ApproveEngineTests.swift +++ b/Tests/WalletConnectSignTests/ApproveEngineTests.swift @@ -60,7 +60,7 @@ final class ApproveEngineTests: XCTestCase { pairingStorageMock.setPairing(pairing) let proposerPubKey = AgreementPrivateKey().publicKey.hexRepresentation let proposal = SessionProposal.stub(proposerPubKey: proposerPubKey) - pairingRegisterer.subject.send(RequestSubscriptionPayload(id: RPCID("id"), topic: topicA, request: proposal, publishedAt: Date())) + pairingRegisterer.subject.send(RequestSubscriptionPayload(id: RPCID("id"), topic: topicA, request: proposal, publishedAt: Date(), derivedTopic: nil)) try await engine.approveProposal(proposerPubKey: proposal.proposer.publicKey, validating: SessionNamespace.stubDictionary()) @@ -84,7 +84,7 @@ final class ApproveEngineTests: XCTestCase { sessionProposed = true } - pairingRegisterer.subject.send(RequestSubscriptionPayload(id: RPCID("id"), topic: topicA, request: proposal, publishedAt: Date())) + pairingRegisterer.subject.send(RequestSubscriptionPayload(id: RPCID("id"), topic: topicA, request: proposal, publishedAt: Date(), derivedTopic: nil)) XCTAssertNotNil(try! proposalPayloadsStore.get(key: proposal.proposer.publicKey), "Proposer must store proposal payload") XCTAssertTrue(sessionProposed) } @@ -108,7 +108,7 @@ final class ApproveEngineTests: XCTestCase { didCallBackOnSessionApproved = true } sessionTopicToProposal.set(SessionProposal.stub().publicRepresentation(pairingTopic: ""), forKey: sessionTopic) - networkingInteractor.requestPublisherSubject.send((sessionTopic, RPCRequest.stubSettle(), Date())) + networkingInteractor.requestPublisherSubject.send((sessionTopic, RPCRequest.stubSettle(), Date(), nil)) usleep(100) @@ -124,7 +124,7 @@ final class ApproveEngineTests: XCTestCase { let request = RPCRequest(method: SessionSettleProtocolMethod().method, params: SessionType.SettleParams.stub()) let response = RPCResponse(matchingRequest: request, result: RPCResult.response(AnyCodable(true))) - networkingInteractor.responsePublisherSubject.send((session.topic, request, response, Date())) + networkingInteractor.responsePublisherSubject.send((session.topic, request, response, Date(), nil)) XCTAssertTrue(sessionStorageMock.getSession(forTopic: session.topic)!.acknowledged, "Responder must acknowledged session") } @@ -139,7 +139,7 @@ final class ApproveEngineTests: XCTestCase { let request = RPCRequest(method: SessionSettleProtocolMethod().method, params: SessionType.SettleParams.stub()) let response = RPCResponse.stubError(forRequest: request) - networkingInteractor.responsePublisherSubject.send((session.topic, request, response, Date())) + networkingInteractor.responsePublisherSubject.send((session.topic, request, response, Date(), nil)) XCTAssertNil(sessionStorageMock.getSession(forTopic: session.topic), "Responder must remove session") XCTAssertTrue(networkingInteractor.didUnsubscribe(to: session.topic), "Responder must unsubscribe topic B") diff --git a/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift b/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift index bc6e0a9eb..ceca38ac3 100644 --- a/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift +++ b/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift @@ -35,7 +35,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { didCallbackUpdatMethods = true XCTAssertEqual(topic, session.topic) } - networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateNamespaces(), Date())) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateNamespaces(), Date(), nil)) XCTAssertTrue(didCallbackUpdatMethods) usleep(100) XCTAssertTrue(networkingInteractor.didRespondSuccess) @@ -51,7 +51,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { // } func testUpdateMethodPeerErrorSessionNotFound() { - networkingInteractor.requestPublisherSubject.send(("", RPCRequest.stubUpdateNamespaces(), Date())) + networkingInteractor.requestPublisherSubject.send(("", RPCRequest.stubUpdateNamespaces(), Date(), nil)) usleep(100) XCTAssertFalse(networkingInteractor.didRespondSuccess) XCTAssertEqual(networkingInteractor.lastErrorCode, 7001) @@ -60,7 +60,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { func testUpdateMethodPeerErrorUnauthorized() { let session = WCSession.stub(isSelfController: true) // Peer is not a controller storageMock.setSession(session) - networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateNamespaces(), Date())) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateNamespaces(), Date(), nil)) usleep(100) XCTAssertFalse(networkingInteractor.didRespondSuccess) XCTAssertEqual(networkingInteractor.lastErrorCode, 3003) @@ -74,7 +74,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { storageMock.setSession(session) let twoDaysFromNowTimestamp = Int64(TimeTraveler.dateByAdding(days: 2).timeIntervalSince1970) - networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: twoDaysFromNowTimestamp), Date())) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: twoDaysFromNowTimestamp), Date(), nil)) let extendedSession = storageMock.getAll().first {$0.topic == session.topic}! print(extendedSession.expiryDate) @@ -87,7 +87,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { storageMock.setSession(session) let twoDaysFromNowTimestamp = Int64(TimeTraveler.dateByAdding(days: 2).timeIntervalSince1970) - networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: twoDaysFromNowTimestamp), Date())) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: twoDaysFromNowTimestamp), Date(), nil)) let potentiallyExtendedSession = storageMock.getAll().first {$0.topic == session.topic}! XCTAssertEqual(potentiallyExtendedSession.expiryDate.timeIntervalSinceReferenceDate, tomorrow.timeIntervalSinceReferenceDate, accuracy: 1, "expiry date has been extended for peer non controller request ") @@ -98,7 +98,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { let session = WCSession.stub(isSelfController: false, expiryDate: tomorrow) storageMock.setSession(session) let tenDaysFromNowTimestamp = Int64(TimeTraveler.dateByAdding(days: 10).timeIntervalSince1970) - networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: tenDaysFromNowTimestamp), Date())) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: tenDaysFromNowTimestamp), Date(), nil)) let potentaillyExtendedSession = storageMock.getAll().first {$0.topic == session.topic}! XCTAssertEqual(potentaillyExtendedSession.expiryDate.timeIntervalSinceReferenceDate, tomorrow.timeIntervalSinceReferenceDate, accuracy: 1, "expiry date has been extended despite ttl to high") @@ -110,7 +110,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { storageMock.setSession(session) let oneDayFromNowTimestamp = Int64(TimeTraveler.dateByAdding(days: 10).timeIntervalSince1970) - networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: oneDayFromNowTimestamp), Date())) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: oneDayFromNowTimestamp), Date(), nil)) let potentaillyExtendedSession = storageMock.getAll().first {$0.topic == session.topic}! XCTAssertEqual(potentaillyExtendedSession.expiryDate.timeIntervalSinceReferenceDate, tomorrow.timeIntervalSinceReferenceDate, accuracy: 1, "expiry date has been extended despite ttl to low") } diff --git a/Tests/WalletConnectSignTests/SessionEngineTests.swift b/Tests/WalletConnectSignTests/SessionEngineTests.swift index 74edb5418..c5a2d36e6 100644 --- a/Tests/WalletConnectSignTests/SessionEngineTests.swift +++ b/Tests/WalletConnectSignTests/SessionEngineTests.swift @@ -47,7 +47,7 @@ final class SessionEngineTests: XCTestCase { expiry: UInt64(Date().timeIntervalSince1970) ) - networkingInteractor.requestPublisherSubject.send(("topic", request, Date())) + networkingInteractor.requestPublisherSubject.send(("topic", request, Date(), nil)) wait(for: [expectation], timeout: 0.5) } From 814599430c4b51bb39c62140bbd628e274f8170e Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 29 Mar 2023 19:09:08 +0500 Subject: [PATCH 17/18] JWT Validator fix --- Sources/WalletConnectJWT/JWT.swift | 15 -------------- Sources/WalletConnectJWT/JWTDecodable.swift | 5 ++--- Sources/WalletConnectJWT/JWTValidator.swift | 22 +++++++++++++++++++++ Tests/RelayerTests/AuthTests/JWTTests.swift | 9 +++++++++ 4 files changed, 33 insertions(+), 18 deletions(-) create mode 100644 Sources/WalletConnectJWT/JWTValidator.swift diff --git a/Sources/WalletConnectJWT/JWT.swift b/Sources/WalletConnectJWT/JWT.swift index 0e6185469..f1eab051c 100644 --- a/Sources/WalletConnectJWT/JWT.swift +++ b/Sources/WalletConnectJWT/JWT.swift @@ -28,21 +28,6 @@ struct JWT: Codable, Equatable { self.signature = try jwtSigner.sign(header: headerString, claims: claimsString) } - func isValid(publicKey: SigningPublicKey) throws -> Bool { - guard let signature else { throw JWTError.noSignature } - - let headerString = try header.encode() - let claimsString = try claims.encode() - let unsignedJWT = [headerString, claimsString].joined(separator: ".") - - guard let unsignedData = unsignedJWT.data(using: .utf8) else { - throw JWTError.invalidJWTString - } - - let signatureData = try JWTEncoder.base64urlDecodedData(string: signature) - return publicKey.isValid(signature: signatureData, for: unsignedData) - } - func encoded() throws -> String { guard let signature = signature else { throw JWTError.jwtNotSigned } let headerString = try header.encode() diff --git a/Sources/WalletConnectJWT/JWTDecodable.swift b/Sources/WalletConnectJWT/JWTDecodable.swift index f28493016..59b1871b1 100644 --- a/Sources/WalletConnectJWT/JWTDecodable.swift +++ b/Sources/WalletConnectJWT/JWTDecodable.swift @@ -29,9 +29,8 @@ extension JWTClaimsCodable { let publicKey = try DIDKey(did: jwt.claims.iss) let signingPublicKey = try SigningPublicKey(rawRepresentation: publicKey.rawData) - guard try jwt.isValid(publicKey: signingPublicKey) else { - throw JWTError.signatureVerificationFailed - } + guard try JWTValidator(jwtString: wrapper.jwtString).isValid(publicKey: signingPublicKey) + else { throw JWTError.signatureVerificationFailed } return (try Self.init(claims: jwt.claims), jwt.claims) } diff --git a/Sources/WalletConnectJWT/JWTValidator.swift b/Sources/WalletConnectJWT/JWTValidator.swift new file mode 100644 index 000000000..e94a72bdf --- /dev/null +++ b/Sources/WalletConnectJWT/JWTValidator.swift @@ -0,0 +1,22 @@ +import Foundation + +struct JWTValidator { + + let jwtString: String + + func isValid(publicKey: SigningPublicKey) throws -> Bool { + var components = jwtString.components(separatedBy: ".") + + guard components.count == 3 else { throw JWTError.undefinedFormat } + + let signature = components.removeLast() + + guard let unsignedData = components + .joined(separator: ".") + .data(using: .utf8) + else { throw JWTError.invalidJWTString } + + let signatureData = try JWTEncoder.base64urlDecodedData(string: signature) + return publicKey.isValid(signature: signatureData, for: unsignedData) + } +} diff --git a/Tests/RelayerTests/AuthTests/JWTTests.swift b/Tests/RelayerTests/AuthTests/JWTTests.swift index e6fdbf0fc..0034c970c 100644 --- a/Tests/RelayerTests/AuthTests/JWTTests.swift +++ b/Tests/RelayerTests/AuthTests/JWTTests.swift @@ -14,6 +14,15 @@ final class JWTTests: XCTestCase { let encoded = try! jwt.encoded() XCTAssertEqual(expectedJWT, encoded) } + + func testBase64Encoding() throws { + let signature = "gf8ZZb04-6DeqhboeA-I7EucdSVuLCJmKcPSTHJqG0CBfVKn0YihgosaD9-6gXED8Itrx5EsyEi49kLmTvS8DA" + + let data = try JWTEncoder.base64urlDecodedData(string: signature) + let string = JWTEncoder.base64urlEncodedString(data: data) + + XCTAssertEqual(signature, string) + } } extension AuthPayload.Claims { From d18b51428d2a62e559a680a1ffbd5a06027a6717 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 4 Apr 2023 10:45:48 +0200 Subject: [PATCH 18/18] update logging level in integration tests --- Example/IntegrationTests/Push/PushTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Example/IntegrationTests/Push/PushTests.swift b/Example/IntegrationTests/Push/PushTests.swift index 2ba203c8b..bf7b0d59e 100644 --- a/Example/IntegrationTests/Push/PushTests.swift +++ b/Example/IntegrationTests/Push/PushTests.swift @@ -27,8 +27,8 @@ final class PushTests: XCTestCase { let keychain = KeychainStorageMock() let keyValueStorage = RuntimeKeyValueStorage() - let relayLogger = ConsoleLogger(suffix: prefix + " [Relay]", loggingLevel: .off) - let pairingLogger = ConsoleLogger(suffix: prefix + " [Pairing]", loggingLevel: .off) + let relayLogger = ConsoleLogger(suffix: prefix + " [Relay]", loggingLevel: .debug) + let pairingLogger = ConsoleLogger(suffix: prefix + " [Pairing]", loggingLevel: .debug) let networkingLogger = ConsoleLogger(suffix: prefix + " [Networking]", loggingLevel: .debug) let relayClient = RelayClient(