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

Verify v2 #1392

Merged
merged 26 commits into from
Aug 13, 2024
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
42 changes: 12 additions & 30 deletions Example/ExampleApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -2783,11 +2783,9 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_ENTITLEMENTS = DApp/DApp.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 173;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5R8AG9K22;
DEVELOPMENT_TEAM = W5R8AG9K22;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = DApp/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = dApp;
Expand All @@ -2807,7 +2805,6 @@
PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.dapp;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.walletconnect.dapp";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
Expand All @@ -2822,11 +2819,9 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_ENTITLEMENTS = DApp/DAppRelease.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 173;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5R8AG9K22;
DEVELOPMENT_TEAM = W5R8AG9K22;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = DApp/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = dApp;
Expand All @@ -2846,7 +2841,6 @@
PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.dapp;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.walletconnect.dapp";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
Expand All @@ -2859,11 +2853,9 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CODE_SIGN_ENTITLEMENTS = PNDecryptionService/PNDecryptionService.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 173;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5R8AG9K22;
DEVELOPMENT_TEAM = W5R8AG9K22;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = PNDecryptionService/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = PNDecryptionService;
Expand All @@ -2878,7 +2870,6 @@
PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.walletapp.PNDecryptionService;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.walletconnect.walletapp.PNDecryptionService";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
Expand All @@ -2892,11 +2883,9 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CODE_SIGN_ENTITLEMENTS = PNDecryptionService/PNDecryptionServiceRelease.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 173;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5R8AG9K22;
DEVELOPMENT_TEAM = W5R8AG9K22;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = PNDecryptionService/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = PNDecryptionService;
Expand All @@ -2911,7 +2900,6 @@
PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.walletapp.PNDecryptionService;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.walletconnect.walletapp.PNDecryptionService";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
Expand Down Expand Up @@ -3075,11 +3063,9 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CODE_SIGN_ENTITLEMENTS = WalletApp/WalletApp.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 173;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5R8AG9K22;
DEVELOPMENT_TEAM = W5R8AG9K22;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = WalletApp/Other/Info.plist;
Expand All @@ -3098,7 +3084,6 @@
PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.walletapp;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.walletconnect.walletapp";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
Expand All @@ -3114,11 +3099,9 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CODE_SIGN_ENTITLEMENTS = WalletApp/WalletAppRelease.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 173;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5R8AG9K22;
DEVELOPMENT_TEAM = W5R8AG9K22;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = WalletApp/Other/Info.plist;
Expand All @@ -3137,7 +3120,6 @@
PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.walletapp;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.walletconnect.walletapp";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
Expand Down
4 changes: 2 additions & 2 deletions Example/RelayIntegrationTests/RelayClientEndToEndTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,12 @@ final class RelayClientEndToEndTests: XCTestCase {
expectationA.assertForOverFulfill = false
expectationB.assertForOverFulfill = false

relayA.messagePublisher.sink { topic, payload, _ in
relayA.messagePublisher.sink { topic, payload, _, _ in
(subscriptionATopic, subscriptionAPayload) = (topic, payload)
expectationA.fulfill()
}.store(in: &publishers)

relayB.messagePublisher.sink { topic, payload, _ in
relayB.messagePublisher.sink { topic, payload, _, _ in
(subscriptionBTopic, subscriptionBPayload) = (topic, payload)
Task(priority: .high) {
sleep(1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ final class AuthRequestPresenter: ObservableObject {

/* Redirect */
if let uri = request.requester.redirect?.native {
// WalletConnectRouter.goBack(uri: uri)
WalletConnectRouter.goBack(uri: uri)
router.dismiss()
} else {
showSignedSheet.toggle()
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ let package = Package(
path: "Sources/WalletConnectRouter/Router"),
.target(
name: "WalletConnectVerify",
dependencies: ["WalletConnectUtils", "WalletConnectNetworking"],
dependencies: ["WalletConnectUtils", "WalletConnectNetworking", "WalletConnectJWT"],
resources: [.process("Resources/PrivacyInfo.xcprivacy")]),
.target(
name: "Database",
Expand Down
6 changes: 3 additions & 3 deletions Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,12 @@ class WalletRequestSubscriber {
Task(priority: .high) {
let assertionId = payload.decryptedPayload.sha256().toHexString()
do {
let response = try await verifyClient.verifyOrigin(assertionId: assertionId)
let verifyContext = verifyClient.createVerifyContext(origin: response.origin, domain: payload.request.payloadParams.domain, isScam: response.isScam)
let response = try await verifyClient.verify(.v1(assertionId: assertionId))
let verifyContext = verifyClient.createVerifyContext(origin: response.origin, domain: payload.request.payloadParams.domain, isScam: response.isScam, isVerified: nil)
verifyContextStore.set(verifyContext, forKey: request.id.string)
onRequest?((request, verifyContext))
} catch {
let verifyContext = verifyClient.createVerifyContext(origin: nil, domain: payload.request.payloadParams.domain, isScam: nil)
let verifyContext = verifyClient.createVerifyContext(origin: nil, domain: payload.request.payloadParams.domain, isScam: nil, isVerified: nil)
verifyContextStore.set(verifyContext, forKey: request.id.string)
onRequest?((request, verifyContext))
return
Expand Down
40 changes: 37 additions & 3 deletions Sources/WalletConnectJWT/JWTValidator.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import Foundation
import CryptoKit

struct JWTValidator {
public struct JWTValidator {

let jwtString: String
private let jwtString: String

func isValid(publicKey: SigningPublicKey) throws -> Bool {
public init(jwtString: String) {
self.jwtString = jwtString
}

public func isValid(publicKey: SigningPublicKey) throws -> Bool {
var components = jwtString.components(separatedBy: ".")

guard components.count == 3 else { throw JWTError.undefinedFormat }
Expand All @@ -20,3 +25,32 @@ struct JWTValidator {
return publicKey.isValid(signature: signatureData, for: unsignedData)
}
}


public struct P256JWTValidator {

private let jwtString: String

public init(jwtString: String) {
self.jwtString = jwtString
}

public func isValid(publicKey: P256.Signing.PublicKey) throws -> Bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this unit tested?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not as a unit here but we have it covered in the AttestationJWTVerifierTests as it depends on P256JWTValidator

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)

let P256Signature = try P256.Signing.ECDSASignature(rawRepresentation: signatureData)

return publicKey.isValidSignature(P256Signature, for: unsignedData)
}
}
2 changes: 1 addition & 1 deletion Sources/WalletConnectNetworking/NetworkInteracting.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ public protocol NetworkInteracting {
var isSocketConnected: Bool { get }
var socketConnectionStatusPublisher: AnyPublisher<SocketConnectionStatus, Never> { get }
var networkConnectionStatusPublisher: AnyPublisher<NetworkConnectionStatus, Never> { get }
var requestPublisher: AnyPublisher<(topic: String, request: RPCRequest, decryptedPayload: Data, publishedAt: Date, derivedTopic: String?), Never> { get }
var requestPublisher: AnyPublisher<(topic: String, request: RPCRequest, decryptedPayload: Data, publishedAt: Date, derivedTopic: String?, encryptedMessage: String, attestation: String?), Never> { get }
func subscribe(topic: String) async throws
func unsubscribe(topic: String)
func batchSubscribe(topics: [String]) async throws
Expand Down
34 changes: 22 additions & 12 deletions Sources/WalletConnectNetworking/NetworkingInteractor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ public class NetworkingInteractor: NetworkInteracting {
private let rpcHistory: RPCHistory
private let logger: ConsoleLogging

private let requestPublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, decryptedPayload: Data, publishedAt: Date, derivedTopic: String?), Never>()
private let requestPublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, decryptedPayload: Data, publishedAt: Date, derivedTopic: String?, encryptedMessage: String, attestation: 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, decryptedPayload: Data, publishedAt: Date, derivedTopic: String?), Never> {
public var requestPublisher: AnyPublisher<(topic: String, request: RPCRequest, decryptedPayload: Data, publishedAt: Date, derivedTopic: String?, encryptedMessage: String, attestation: String?), Never> {
requestPublisherSubject.eraseToAnyPublisher()
}

Expand Down Expand Up @@ -51,8 +51,8 @@ public class NetworkingInteractor: NetworkInteracting {

private func setupRelaySubscribtion() {
relayClient.messagePublisher
.sink { [unowned self] (topic, message, publishedAt) in
manageSubscription(topic, message, publishedAt)
.sink { [unowned self] (topic, message, publishedAt, attestation) in
manageSubscription(topic, message, publishedAt, attestation)
}.store(in: &publishers)
}

Expand Down Expand Up @@ -123,19 +123,29 @@ public class NetworkingInteractor: NetworkInteracting {
}.store(in: &publishers)
}


public func requestSubscription<RequestParams: Codable>(on request: ProtocolMethod) -> AnyPublisher<RequestSubscriptionPayload<RequestParams>, Never> {
return requestPublisher
.filter { rpcRequest in
return rpcRequest.request.method == request.method
}
.compactMap { [weak self] topic, rpcRequest, decryptedPayload, publishedAt, derivedTopic in
.compactMap { [weak self] topic, rpcRequest, decryptedPayload, publishedAt, derivedTopic, encryptedMessage, attestation in
do {
guard let id = rpcRequest.id, let request = try rpcRequest.params?.get(RequestParams.self) else { return nil }
return RequestSubscriptionPayload(id: id, topic: topic, request: request, decryptedPayload: decryptedPayload, publishedAt: publishedAt, derivedTopic: derivedTopic)
return RequestSubscriptionPayload(
id: id,
topic: topic,
request: request,
decryptedPayload: decryptedPayload,
publishedAt: publishedAt,
derivedTopic: derivedTopic,
encryptedMessage: encryptedMessage,
attestation: attestation
)
} catch {
self?.logger.debug("Networking Interactor - \(error)")
return nil
}
return nil
}
.eraseToAnyPublisher()
}
Expand Down Expand Up @@ -245,11 +255,11 @@ public class NetworkingInteractor: NetworkInteracting {
try await respond(topic: topic, response: response, protocolMethod: protocolMethod, envelopeType: envelopeType)
}

private func manageSubscription(_ topic: String, _ encodedEnvelope: String, _ publishedAt: Date) {
private func manageSubscription(_ topic: String, _ encodedEnvelope: String, _ publishedAt: Date, _ attestation: String?) {
if let result = serializer.tryDeserializeRequestOrResponse(topic: topic, codingType: .base64Encoded, envelopeString: encodedEnvelope) {
switch result {
case .left(let result):
handleRequest(topic: topic, request: result.request, decryptedPayload: result.decryptedPayload, publishedAt: publishedAt, derivedTopic: result.derivedTopic)
handleRequest(topic: topic, request: result.request, decryptedPayload: result.decryptedPayload, publishedAt: publishedAt, derivedTopic: result.derivedTopic, encryptedMessage: encodedEnvelope, attestation: attestation)
case .right(let result):
handleResponse(topic: topic, response: result.response, publishedAt: publishedAt, derivedTopic: result.derivedTopic)
}
Expand All @@ -259,13 +269,13 @@ public class NetworkingInteractor: NetworkInteracting {
}

public func handleHistoryRequest(topic: String, request: RPCRequest) {
requestPublisherSubject.send((topic, request, Data(), Date(), nil))
requestPublisherSubject.send((topic, request, Data(), Date(), nil, "", nil ))
}

private func handleRequest(topic: String, request: RPCRequest, decryptedPayload: Data, publishedAt: Date, derivedTopic: String?) {
private func handleRequest(topic: String, request: RPCRequest, decryptedPayload: Data, publishedAt: Date, derivedTopic: String?, encryptedMessage: String, attestation: String?) {
do {
try rpcHistory.set(request, forTopic: topic, emmitedBy: .remote, transportType: .relay)
requestPublisherSubject.send((topic, request, decryptedPayload, publishedAt, derivedTopic))
requestPublisherSubject.send((topic, request, decryptedPayload, publishedAt, derivedTopic, encryptedMessage, attestation))
} catch {
logger.debug(error)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,22 @@ import Foundation

public struct RequestSubscriptionPayload<Request: Codable>: Codable, SubscriptionPayload {
public let id: RPCID
public let encryptedMessage: String
public let attestation: String?
public let topic: String
public let request: Request
public let decryptedPayload: Data
public let publishedAt: Date
public let derivedTopic: String?

public init(id: RPCID, topic: String, request: Request, decryptedPayload: Data, publishedAt: Date, derivedTopic: String?) {
public init(id: RPCID, topic: String, request: Request, decryptedPayload: Data, publishedAt: Date, derivedTopic: String?, encryptedMessage: String, attestation: String?) {
self.id = id
self.topic = topic
self.request = request
self.decryptedPayload = decryptedPayload
self.publishedAt = publishedAt
self.derivedTopic = derivedTopic
self.encryptedMessage = encryptedMessage
self.attestation = attestation
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Loading
Loading