Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Push client encrypted PNs #1245

Merged
merged 19 commits into from
Dec 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Example/ExampleApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand Down Expand Up @@ -731,6 +732,7 @@
buildActionMask = 2147483647;
files = (
A5A650CA2B062A1400F9AD4B /* Mixpanel in Frameworks */,
844AD55F2B1497270062E123 /* Web3Wallet in Frameworks */,
A5B6C0F72A6EAB3200927332 /* WalletConnectNotify in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -2030,6 +2032,7 @@
packageProductDependencies = (
A5B6C0F62A6EAB3200927332 /* WalletConnectNotify */,
A5A650C92B062A1400F9AD4B /* Mixpanel */,
844AD55E2B1497270062E123 /* Web3Wallet */,
);
productName = PNDecryptionService;
productReference = 84E6B84729787A8000428BAF /* PNDecryptionService.appex */;
Expand Down Expand Up @@ -3409,6 +3412,10 @@
isa = XCSwiftPackageProductDependency;
productName = WalletConnect;
};
844AD55E2B1497270062E123 /* Web3Wallet */ = {
isa = XCSwiftPackageProductDependency;
productName = Web3Wallet;
};
8487A9432A836C2A0003D5AF /* Sentry */ = {
isa = XCSwiftPackageProductDependency;
package = 8487A9422A836C2A0003D5AF /* XCRemoteSwiftPackageReference "sentry-cocoa" */;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1410"
LastUpgradeVersion = "1500"
wasCreatedForAppExtension = "YES"
version = "1.3">
version = "2.0">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
Expand All @@ -15,9 +15,9 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "84B767752954554A00E92316"
BuildableName = "PushDecryptionService.appex"
BlueprintName = "PushDecryptionService"
BlueprintIdentifier = "84E6B84629787A8000428BAF"
BuildableName = "PNDecryptionService.appex"
BlueprintName = "PNDecryptionService"
llbartekll marked this conversation as resolved.
Show resolved Hide resolved
ReferencedContainer = "container:ExampleApp.xcodeproj">
</BuildableReference>
</BuildActionEntry>
Expand All @@ -29,9 +29,9 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "764E1D3B26F8D3FC00A1FB15"
BuildableName = "WalletConnect Wallet.app"
BlueprintName = "Wallet"
BlueprintIdentifier = "C56EE21A293F55ED004840D1"
BuildableName = "WalletApp.app"
BlueprintName = "WalletApp"
ReferencedContainer = "container:ExampleApp.xcodeproj">
</BuildableReference>
</BuildActionEntry>
Expand All @@ -41,31 +41,21 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "844749F229B9E5B9005F520B"
BuildableName = "RelayIntegrationTests.xctest"
BlueprintName = "RelayIntegrationTests"
ReferencedContainer = "container:ExampleApp.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
selectedDebuggerIdentifier = ""
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
launchStyle = "0"
askForAppToLaunch = "Yes"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
allowLocationSimulation = "YES"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
Expand All @@ -76,15 +66,6 @@
ReferencedContainer = "container:ExampleApp.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "764E1D3B26F8D3FC00A1FB15"
BuildableName = "WalletConnect Wallet.app"
BlueprintName = "Wallet"
ReferencedContainer = "container:ExampleApp.xcodeproj">
</BuildableReference>
</MacroExpansion>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
Expand All @@ -98,21 +79,12 @@
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "764E1D3B26F8D3FC00A1FB15"
BuildableName = "WalletConnect Wallet.app"
BlueprintName = "Wallet"
BlueprintIdentifier = "C56EE21A293F55ED004840D1"
BuildableName = "WalletApp.app"
BlueprintName = "WalletApp"
ReferencedContainer = "container:ExampleApp.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "764E1D3B26F8D3FC00A1FB15"
BuildableName = "WalletConnect Wallet.app"
BlueprintName = "Wallet"
ReferencedContainer = "container:ExampleApp.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@
ReferencedContainer = "container:ExampleApp.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "84E6B84629787A8000428BAF"
BuildableName = "PNDecryptionService.appex"
BlueprintName = "PNDecryptionService"
ReferencedContainer = "container:ExampleApp.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
Expand Down
96 changes: 74 additions & 22 deletions Example/PNDecryptionService/NotificationService.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import UserNotifications
import Web3Wallet
import WalletConnectNotify
import Intents
import Mixpanel
Expand All @@ -16,48 +17,99 @@ class NotificationService: UNNotificationServiceExtension {

if let content = bestAttemptContent,
let topic = content.userInfo["topic"] as? String,
let ciphertext = content.userInfo["blob"] as? String {
let ciphertext = content.userInfo["message"] as? String,
let tag = content.userInfo["tag"] as? UInt {
llbartekll marked this conversation as resolved.
Show resolved Hide resolved

log("topic and blob found")

do {
let service = NotifyDecryptionService(groupIdentifier: "group.com.walletconnect.sdk")
let (pushMessage, account) = try service.decryptMessage(topic: topic, ciphertext: ciphertext)
if Web3WalletDecryptionService.canHandle(tag: tag) {
let mutableContent = handleWeb3WalletNotification(content: content, topic: topic, tag: tag, ciphertext: ciphertext)
contentHandler(mutableContent)
} else if NotifyDecryptionService.canHandle(tag: tag) {
let mutableContent = handleNotifyNotification(content: content, topic: topic, ciphertext: ciphertext)
contentHandler(mutableContent)
} else {
let mutableContent = content.mutableCopy() as! UNMutableNotificationContent
mutableContent.title = "Error: unknown message tag"
}
}
}

log("message decrypted", account: account, topic: topic, message: pushMessage)
private func handleWeb3WalletNotification(content: UNNotificationContent, topic: String, tag: UInt, ciphertext: String) -> 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.
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
contentHandler(bestAttemptContent)
}
}


}

private extension NotificationService {
Expand Down
2 changes: 1 addition & 1 deletion Example/WalletApp/ApplicationLayer/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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") {
Expand Down
47 changes: 47 additions & 0 deletions Sources/Auth/AuthDecryptionService.swift
Original file line number Diff line number Diff line change
@@ -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<WCPairing>(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)
}
}

4 changes: 2 additions & 2 deletions Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading