diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index ea92b71d8..90639185b 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -18,6 +18,7 @@ 84474A0129B9EB74005F520B /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = 84474A0029B9EB74005F520B /* Starscream */; }; 84474A0229B9ECA2005F520B /* DefaultSocketFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5629AEF2877F73000094373 /* DefaultSocketFactory.swift */; }; 8448F1D427E4726F0000B866 /* WalletConnect in Frameworks */ = {isa = PBXBuildFile; productRef = 8448F1D327E4726F0000B866 /* WalletConnect */; }; + 844AD55F2B1497270062E123 /* Web3Wallet in Frameworks */ = {isa = PBXBuildFile; productRef = 844AD55E2B1497270062E123 /* Web3Wallet */; }; 845B8D8C2934B36C0084A966 /* Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845B8D8B2934B36C0084A966 /* Account.swift */; }; 847BD1D62989492500076C90 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847BD1D12989492500076C90 /* MainViewController.swift */; }; 847BD1D82989492500076C90 /* MainModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847BD1D32989492500076C90 /* MainModule.swift */; }; @@ -731,6 +732,7 @@ buildActionMask = 2147483647; files = ( A5A650CA2B062A1400F9AD4B /* Mixpanel in Frameworks */, + 844AD55F2B1497270062E123 /* Web3Wallet in Frameworks */, A5B6C0F72A6EAB3200927332 /* WalletConnectNotify in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2030,6 +2032,7 @@ packageProductDependencies = ( A5B6C0F62A6EAB3200927332 /* WalletConnectNotify */, A5A650C92B062A1400F9AD4B /* Mixpanel */, + 844AD55E2B1497270062E123 /* Web3Wallet */, ); productName = PNDecryptionService; productReference = 84E6B84729787A8000428BAF /* PNDecryptionService.appex */; @@ -3409,6 +3412,10 @@ isa = XCSwiftPackageProductDependency; productName = WalletConnect; }; + 844AD55E2B1497270062E123 /* Web3Wallet */ = { + isa = XCSwiftPackageProductDependency; + productName = Web3Wallet; + }; 8487A9432A836C2A0003D5AF /* Sentry */ = { isa = XCSwiftPackageProductDependency; package = 8487A9422A836C2A0003D5AF /* XCRemoteSwiftPackageReference "sentry-cocoa" */; diff --git a/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index dceba93f5..465b92c16 100644 --- a/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -168,8 +168,8 @@ "repositoryURL": "https://github.com/WalletConnect/web3modal-swift", "state": { "branch": null, - "revision": "831410cfd6e68afa7212a5547483fb2d180f0fa7", - "version": "1.0.10" + "revision": "3295d69d1b12df29a5040578d107f56986b1b399", + "version": "1.0.13" } } ] diff --git a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/PushDecryptionService.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/PNDecryptionService.xcscheme similarity index 57% rename from Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/PushDecryptionService.xcscheme rename to Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/PNDecryptionService.xcscheme index 07e071581..026a57bb3 100644 --- a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/PushDecryptionService.xcscheme +++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/PNDecryptionService.xcscheme @@ -1,8 +1,8 @@ + version = "2.0"> @@ -15,9 +15,9 @@ buildForAnalyzing = "YES"> @@ -29,9 +29,9 @@ buildForAnalyzing = "YES"> @@ -41,31 +41,21 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES"> - - - - - - + shouldUseLaunchSchemeArgsEnv = "YES" + shouldAutocreateTestPlan = "YES"> + allowLocationSimulation = "YES" + launchAutomaticallySubstyle = "2"> - - - - - - - - diff --git a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletApp.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletApp.xcscheme index 036e15875..6d01f5811 100644 --- a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletApp.xcscheme +++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletApp.xcscheme @@ -20,6 +20,20 @@ ReferencedContainer = "container:ExampleApp.xcodeproj"> + + + + UNMutableNotificationContent { - let updatedContent = handle(content: content, pushMessage: pushMessage, topic: topic) + do { + let web3WalletDecryptionService = try Web3WalletDecryptionService(groupIdentifier: "group.com.walletconnect.sdk") - let mutableContent = updatedContent.mutableCopy() as! UNMutableNotificationContent - mutableContent.title = pushMessage.title - mutableContent.subtitle = pushMessage.url - mutableContent.body = pushMessage.body + let decryptedPayload = try web3WalletDecryptionService.decryptMessage(topic: topic, ciphertext: ciphertext, tag: tag) - log("message handled", account: account, topic: topic, message: pushMessage) + let mutableContent = content.mutableCopy() as! UNMutableNotificationContent - contentHandler(mutableContent) + guard let metadata = web3WalletDecryptionService.getMetadata(topic: topic) else { + mutableContent.title = "Error: Cannot get peer's metadata" + return mutableContent + } - log("content handled", account: account, topic: topic, message: pushMessage) + switch decryptedPayload.requestMethod { + case .sessionProposal: + mutableContent.title = "New session proposal!" + mutableContent.body = "A new session proposal arrived from \(metadata.name), please check your wallet" + case .sessionRequest: + if let payload = decryptedPayload as? RequestPayload { + mutableContent.title = "New session request!" + mutableContent.body = "A new session request \(payload.request.method) arrived from \(metadata.name), please check your wallet" + } + case .authRequest: + mutableContent.title = "New authentication request!" + mutableContent.body = "A new authentication request arrived from \(metadata.name), please check your wallet" } - catch { - log("error: \(error.localizedDescription)") - let mutableContent = content.mutableCopy() as! UNMutableNotificationContent - mutableContent.title = "Error" - mutableContent.body = error.localizedDescription + return mutableContent + } catch { + let mutableContent = content.mutableCopy() as! UNMutableNotificationContent + mutableContent.title = "Error" + mutableContent.body = error.localizedDescription - contentHandler(mutableContent) - } + return mutableContent + } + } + + + private func handleNotifyNotification(content: UNNotificationContent, topic: String, ciphertext: String) -> UNMutableNotificationContent { + do { + let service = NotifyDecryptionService(groupIdentifier: "group.com.walletconnect.sdk") + let (pushMessage, account) = try service.decryptMessage(topic: topic, ciphertext: ciphertext) + + log("message decrypted", account: account, topic: topic, message: pushMessage) + + let updatedContent = handle(content: content, pushMessage: pushMessage, topic: topic) + + let mutableContent = updatedContent.mutableCopy() as! UNMutableNotificationContent + mutableContent.title = pushMessage.title + mutableContent.subtitle = pushMessage.url + mutableContent.body = pushMessage.body + + log("message handled", account: account, topic: topic, message: pushMessage) + + return mutableContent + } catch { + log("error: \(error.localizedDescription)") + + let mutableContent = content.mutableCopy() as! UNMutableNotificationContent + mutableContent.title = "Error" + mutableContent.body = error.localizedDescription + + return mutableContent } } + override func serviceExtensionTimeWillExpire() { // Called just before the extension will be terminated by the system. // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used. @@ -58,6 +108,8 @@ class NotificationService: UNNotificationServiceExtension { contentHandler(bestAttemptContent) } } + + } private extension NotificationService { diff --git a/Example/WalletApp/ApplicationLayer/AppDelegate.swift b/Example/WalletApp/ApplicationLayer/AppDelegate.swift index 1bef78e52..f2f961b36 100644 --- a/Example/WalletApp/ApplicationLayer/AppDelegate.swift +++ b/Example/WalletApp/ApplicationLayer/AppDelegate.swift @@ -29,7 +29,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { UserDefaults.standard.set(deviceTokenString.joined(), forKey: "deviceToken") Task(priority: .high) { - try await Notify.instance.register(deviceToken: deviceToken) + try await Notify.instance.register(deviceToken: deviceToken, enableEncrypted: true) } } diff --git a/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsView.swift b/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsView.swift index 2daa40746..a3146cbd8 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsView.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsView.swift @@ -191,7 +191,7 @@ struct NotificationsView: View { AsyncButton("Subscribed") { try await presenter.unsubscribe(subscription: subscription) } - .buttonStyle(W3MButtonStyle(size: .m, variant: .accent, rightIcon: .Checkmark)) + .buttonStyle(W3MButtonStyle(size: .m, variant: .accent, rightIcon: Image.Medium.checkmark)) .disabled(true) } else { AsyncButton("Subscribe") { diff --git a/Sources/Auth/AuthDecryptionService.swift b/Sources/Auth/AuthDecryptionService.swift new file mode 100644 index 000000000..9c2c55d61 --- /dev/null +++ b/Sources/Auth/AuthDecryptionService.swift @@ -0,0 +1,47 @@ +import Foundation + +public class AuthDecryptionService { + enum Errors: Error { + case couldNotInitialiseDefaults + case couldNotDecodeTypeFromCiphertext + } + private let serializer: Serializing + private let pairingStorage: PairingStorage + + public init(groupIdentifier: String) throws { + let keychainStorage = GroupKeychainStorage(serviceIdentifier: groupIdentifier) + let kms = KeyManagementService(keychain: keychainStorage) + self.serializer = Serializer(kms: kms, logger: ConsoleLogger(prefix: "🔐", loggingLevel: .off)) + guard let defaults = UserDefaults(suiteName: groupIdentifier) else { + throw Errors.couldNotInitialiseDefaults + } + pairingStorage = PairingStorage(storage: SequenceStore(store: .init(defaults: defaults, identifier: PairStorageIdentifiers.pairings.rawValue))) + } + + public func decryptAuthRequest(topic: String, ciphertext: String) throws -> AuthRequest { + let (rpcRequest, _, _): (RPCRequest, String?, Data) = try serializer.deserialize(topic: topic, encodedEnvelope: ciphertext) + setPairingMetadata(rpcRequest: rpcRequest, topic: topic) + if let params = try rpcRequest.params?.get(AuthRequestParams.self), + let id = rpcRequest.id { + let authRequest = AuthRequest(id: id, topic: topic, payload: params.payloadParams, requester: params.requester.metadata) + return authRequest + } else { + throw Errors.couldNotDecodeTypeFromCiphertext + } + } + + public func getMetadata(topic: String) -> AppMetadata? { + pairingStorage.getPairing(forTopic: topic)?.peerMetadata + } + + private func setPairingMetadata(rpcRequest: RPCRequest, topic: String) { + guard var pairing = pairingStorage.getPairing(forTopic: topic), + pairing.peerMetadata == nil, + let peerMetadata = try? rpcRequest.params?.get(AuthRequestParams.self).requester.metadata + else { return } + + pairing.updatePeerMetadata(peerMetadata) + pairingStorage.setPairing(pairing) + } +} + diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift index 41db7b97e..6a3ea77e5 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift @@ -117,8 +117,8 @@ public class NotifyClient { try? notifyStorage.deleteMessage(id: id) } - public func register(deviceToken: Data) async throws { - try await pushClient.register(deviceToken: deviceToken) + public func register(deviceToken: Data, enableEncrypted: Bool = false) async throws { + try await pushClient.register(deviceToken: deviceToken, enableEncrypted: enableEncrypted) } public func isIdentityRegistered(account: Account) -> Bool { diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyDecryptionService.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyDecryptionService.swift index 296e515e9..272a37cea 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyDecryptionService.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyDecryptionService.swift @@ -5,6 +5,7 @@ public class NotifyDecryptionService { case malformedNotifyMessage } private let serializer: Serializing + private static let notifyTags: [UInt] = [4002] init(serializer: Serializing) { self.serializer = serializer @@ -16,6 +17,10 @@ public class NotifyDecryptionService { self.serializer = Serializer(kms: kms, logger: ConsoleLogger(prefix: "🔐", loggingLevel: .off)) } + public static func canHandle(tag: UInt) -> Bool { + return notifyTags.contains(tag) + } + public func decryptMessage(topic: String, ciphertext: String) throws -> (NotifyMessage, Account) { let (rpcRequest, _, _): (RPCRequest, String?, Data) = try serializer.deserialize(topic: topic, encodedEnvelope: ciphertext) guard let params = rpcRequest.params else { throw Errors.malformedNotifyMessage } diff --git a/Sources/WalletConnectPairing/PairStorageIdentifiers.swift b/Sources/WalletConnectPairing/PairStorageIdentifiers.swift index f287e1aad..0728ec2c5 100644 --- a/Sources/WalletConnectPairing/PairStorageIdentifiers.swift +++ b/Sources/WalletConnectPairing/PairStorageIdentifiers.swift @@ -1,5 +1,5 @@ import Foundation -enum PairStorageIdentifiers: String { +public enum PairStorageIdentifiers: String { case pairings = "com.walletconnect.sdk.pairingSequences" } diff --git a/Sources/WalletConnectPush/PushClient.swift b/Sources/WalletConnectPush/PushClient.swift index 30f87f9f3..2c378b3e6 100644 --- a/Sources/WalletConnectPush/PushClient.swift +++ b/Sources/WalletConnectPush/PushClient.swift @@ -14,13 +14,13 @@ public class PushClient: PushClientProtocol { self.logger = logger } - public func register(deviceToken: Data) async throws { - try await registerService.register(deviceToken: deviceToken) + public func register(deviceToken: Data, enableEncrypted: Bool = false) async throws { + try await registerService.register(deviceToken: deviceToken, alwaysRaw: enableEncrypted) } #if DEBUG public func register(deviceToken: String) async throws { - try await registerService.register(deviceToken: deviceToken) + try await registerService.register(deviceToken: deviceToken, alwaysRaw: true) } #endif } diff --git a/Sources/WalletConnectPush/PushClientFactory.swift b/Sources/WalletConnectPush/PushClientFactory.swift index be255383b..8a7a871cb 100644 --- a/Sources/WalletConnectPush/PushClientFactory.swift +++ b/Sources/WalletConnectPush/PushClientFactory.swift @@ -30,8 +30,8 @@ public struct PushClientFactory { environment: APNSEnvironment ) -> PushClient { let sessionConfiguration = URLSessionConfiguration.default - sessionConfiguration.timeoutIntervalForRequest = 5.0 - sessionConfiguration.timeoutIntervalForResource = 5.0 + sessionConfiguration.timeoutIntervalForRequest = 10.0 + sessionConfiguration.timeoutIntervalForResource = 10.0 let session = URLSession(configuration: sessionConfiguration) let logger = ConsoleLogger(prefix: "👂🏻", loggingLevel: .off) diff --git a/Sources/WalletConnectPush/PushClientProtocol.swift b/Sources/WalletConnectPush/PushClientProtocol.swift index 35eb8f3fa..93c9a5fe6 100644 --- a/Sources/WalletConnectPush/PushClientProtocol.swift +++ b/Sources/WalletConnectPush/PushClientProtocol.swift @@ -1,8 +1,14 @@ import Foundation public protocol PushClientProtocol { - func register(deviceToken: Data) async throws + func register(deviceToken: Data, enableEncrypted: Bool) async throws #if DEBUG func register(deviceToken: String) async throws #endif } + +public extension PushClientProtocol { + func register(deviceToken: Data, enableEncrypted: Bool = false) async throws { + try await register(deviceToken: deviceToken, enableEncrypted: enableEncrypted) + } +} diff --git a/Sources/WalletConnectPush/Register/PushRegisterService.swift b/Sources/WalletConnectPush/Register/PushRegisterService.swift index 3c06ce281..193c173c7 100644 --- a/Sources/WalletConnectPush/Register/PushRegisterService.swift +++ b/Sources/WalletConnectPush/Register/PushRegisterService.swift @@ -29,7 +29,7 @@ actor PushRegisterService { self.environment = environment } - func register(deviceToken: Data) async throws { + func register(deviceToken: Data, alwaysRaw: Bool) async throws { let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) } let token = tokenParts.joined() let pushAuthToken = try pushAuthenticator.createAuthToken() @@ -40,7 +40,7 @@ actor PushRegisterService { do { let response = try await httpClient.request( PushResponse.self, - at: PushAPI.register(clientId: clientIdMutlibase, token: token, projectId: projectId, environment: environment, auth: pushAuthToken) + at: PushAPI.register(clientId: clientIdMutlibase, token: token, projectId: projectId, environment: environment, auth: pushAuthToken, alwaysRaw: alwaysRaw) ) guard response.status == .success else { throw Errors.registrationFailed @@ -51,7 +51,7 @@ actor PushRegisterService { logger.debug("Trying fallback") fallback = true await echoHostFallback() - try await register(deviceToken: deviceToken) + try await register(deviceToken: deviceToken, alwaysRaw: alwaysRaw) } logger.debug("Push Server registration error: \(error.localizedDescription)") throw error @@ -63,7 +63,7 @@ actor PushRegisterService { } #if DEBUG - public func register(deviceToken: String) async throws { + public func register(deviceToken: String, alwaysRaw: Bool) async throws { let pushAuthToken = try pushAuthenticator.createAuthToken() let clientId = try clientIdStorage.getClientId() let clientIdMutlibase = try DIDKey(did: clientId).multibase(variant: .ED25519) @@ -71,7 +71,7 @@ actor PushRegisterService { do { let response = try await httpClient.request( PushResponse.self, - at: PushAPI.register(clientId: clientIdMutlibase, token: deviceToken, projectId: projectId, environment: environment, auth: pushAuthToken) + at: PushAPI.register(clientId: clientIdMutlibase, token: deviceToken, projectId: projectId, environment: environment, auth: pushAuthToken, alwaysRaw: alwaysRaw) ) guard response.status == .success else { throw Errors.registrationFailed @@ -81,7 +81,7 @@ actor PushRegisterService { if (error as? HTTPError) == .couldNotConnect && !fallback { fallback = true await echoHostFallback() - try await register(deviceToken: deviceToken) + try await register(deviceToken: deviceToken, alwaysRaw: alwaysRaw) } throw error } diff --git a/Sources/WalletConnectPush/Register/PushService.swift b/Sources/WalletConnectPush/Register/PushService.swift index d6ddbc6f3..43062e6c4 100644 --- a/Sources/WalletConnectPush/Register/PushService.swift +++ b/Sources/WalletConnectPush/Register/PushService.swift @@ -1,12 +1,12 @@ import Foundation enum PushAPI: HTTPService { - case register(clientId: String, token: String, projectId: String, environment: APNSEnvironment, auth: String) + case register(clientId: String, token: String, projectId: String, environment: APNSEnvironment, auth: String, alwaysRaw: Bool) case unregister(clientId: String, projectId: String, auth: String) var path: String { switch self { - case .register(_, _, let projectId, _, _): + case .register(_, _, let projectId, _, _, _): return "/\(projectId)/clients" case .unregister(let clientId, let projectId, _): return "/\(projectId)/clients\(clientId)" @@ -24,12 +24,9 @@ enum PushAPI: HTTPService { var body: Data? { switch self { - case .register(let clientId, let token, _, let environment, _): - return try? JSONEncoder().encode([ - "client_id": clientId, - "type": environment.rawValue, - "token": token - ]) + case .register(let clientId, let token, _, let environment, _, let alwaysRaw): + let request = RegisterRequest(clientId: clientId, type: environment.rawValue, token: token, alwaysRaw: alwaysRaw) + return try? JSONEncoder().encode(request) case .unregister: return nil } @@ -45,7 +42,7 @@ enum PushAPI: HTTPService { var additionalHeaderFields: [String : String]? { switch self { - case .register(_, _, _, _, let auth): + case .register(_, _, _, _, let auth, _): return ["Authorization": auth] case .unregister(_, _, let auth): return ["Authorization": auth] @@ -54,3 +51,16 @@ enum PushAPI: HTTPService { } +struct RegisterRequest: Codable { + let clientId: String + let type: String + let token: String + let alwaysRaw: Bool + + enum CodingKeys: String, CodingKey { + case clientId = "client_id" + case type + case token + case alwaysRaw = "always_raw" + } +} diff --git a/Sources/WalletConnectSign/SignDecryptionService.swift b/Sources/WalletConnectSign/SignDecryptionService.swift new file mode 100644 index 000000000..8b0e82125 --- /dev/null +++ b/Sources/WalletConnectSign/SignDecryptionService.swift @@ -0,0 +1,52 @@ +import Foundation + +public class SignDecryptionService { + enum Errors: Error { + case couldNotInitialiseDefaults + case couldNotDecodeTypeFromCiphertext + } + private let serializer: Serializing + private let sessionStorage: WCSessionStorage + + public init(groupIdentifier: String) throws { + let keychainStorage = GroupKeychainStorage(serviceIdentifier: groupIdentifier) + let kms = KeyManagementService(keychain: keychainStorage) + self.serializer = Serializer(kms: kms, logger: ConsoleLogger(prefix: "🔐", loggingLevel: .off)) + guard let defaults = UserDefaults(suiteName: groupIdentifier) else { + throw Errors.couldNotInitialiseDefaults + } + sessionStorage = SessionStorage(storage: SequenceStore(store: .init(defaults: defaults, identifier: SignStorageIdentifiers.sessions.rawValue))) + } + + public func decryptProposal(topic: String, ciphertext: String) throws -> Session.Proposal { + let (rpcRequest, _, _): (RPCRequest, String?, Data) = try serializer.deserialize(topic: topic, encodedEnvelope: ciphertext) + if let proposal = try rpcRequest.params?.get(SessionType.ProposeParams.self) { + return proposal.publicRepresentation(pairingTopic: topic) + } else { + throw Errors.couldNotDecodeTypeFromCiphertext + } + } + + public func decryptRequest(topic: String, ciphertext: String) throws -> Request { + let (rpcRequest, _, _): (RPCRequest, String?, Data) = try serializer.deserialize(topic: topic, encodedEnvelope: ciphertext) + if let request = try rpcRequest.params?.get(SessionType.RequestParams.self), + let rpcId = rpcRequest.id{ + let request = Request( + id: rpcId, + topic: topic, + method: request.request.method, + params: request.request.params, + chainId: request.chainId, + expiry: request.request.expiry + ) + + return request + } else { + throw Errors.couldNotDecodeTypeFromCiphertext + } + } + + public func getMetadata(topic: String) -> AppMetadata? { + sessionStorage.getSession(forTopic: topic)?.peerParticipant.metadata + } +} diff --git a/Sources/Web3Wallet/Web3WalletClient.swift b/Sources/Web3Wallet/Web3WalletClient.swift index 19e27790f..38fc6d213 100644 --- a/Sources/Web3Wallet/Web3WalletClient.swift +++ b/Sources/Web3Wallet/Web3WalletClient.swift @@ -218,8 +218,8 @@ public class Web3WalletClient { try authClient.getPendingRequests() } - public func registerPushClient(deviceToken: Data) async throws { - try await pushClient.register(deviceToken: deviceToken) + public func registerPushClient(deviceToken: Data, enableEncrypted: Bool = false) async throws { + try await pushClient.register(deviceToken: deviceToken, enableEncrypted: enableEncrypted) } /// Delete all stored data such as: pairings, sessions, keys diff --git a/Sources/Web3Wallet/Web3WalletDecryptionService.swift b/Sources/Web3Wallet/Web3WalletDecryptionService.swift new file mode 100644 index 000000000..e4786129f --- /dev/null +++ b/Sources/Web3Wallet/Web3WalletDecryptionService.swift @@ -0,0 +1,70 @@ +import Foundation +import WalletConnectSign + +public final class Web3WalletDecryptionService { + enum Errors: Error { + case unknownTag + } + public enum RequestMethod: UInt { + case sessionRequest = 1108 + case sessionProposal = 1100 + case authRequest = 3000 + } + + private let signDecryptionService: SignDecryptionService + private let authDecryptionService: AuthDecryptionService + private static let w3wTags: [UInt] = [1108, 1100, 3000] + + public init(groupIdentifier: String) throws { + self.authDecryptionService = try AuthDecryptionService(groupIdentifier: groupIdentifier) + self.signDecryptionService = try SignDecryptionService(groupIdentifier: groupIdentifier) + } + + public static func canHandle(tag: UInt) -> Bool { + return w3wTags.contains(tag) + } + + public func getMetadata(topic: String) -> AppMetadata? { + if let metadata = signDecryptionService.getMetadata(topic: topic) { + return metadata + } else { + return authDecryptionService.getMetadata(topic: topic) + } + } + + public func decryptMessage(topic: String, ciphertext: String, tag: UInt) throws -> DecryptedPayloadProtocol { + guard let requestMethod = RequestMethod(rawValue: tag) else { throw Errors.unknownTag } + switch requestMethod { + case .sessionProposal: + let proposal = try signDecryptionService.decryptProposal(topic: topic, ciphertext: ciphertext) + return ProposalPayload(proposal: proposal) + case .sessionRequest: + let request = try signDecryptionService.decryptRequest(topic: topic, ciphertext: ciphertext) + return RequestPayload(request: request) + case .authRequest: + let request = try authDecryptionService.decryptAuthRequest(topic: topic, ciphertext: ciphertext) + return AuthRequestPayload(authRequest: request) + } + } + + +} + +public protocol DecryptedPayloadProtocol { + var requestMethod: Web3WalletDecryptionService.RequestMethod { get } +} + +public struct RequestPayload: DecryptedPayloadProtocol { + public var requestMethod: Web3WalletDecryptionService.RequestMethod { .sessionRequest } + public var request: Request +} + +public struct ProposalPayload: DecryptedPayloadProtocol { + public var requestMethod: Web3WalletDecryptionService.RequestMethod { .sessionProposal } + public var proposal: Session.Proposal +} + +public struct AuthRequestPayload: DecryptedPayloadProtocol { + public var requestMethod: Web3WalletDecryptionService.RequestMethod { .authRequest } + public var authRequest: AuthRequest +}