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 15 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
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
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
2 changes: 1 addition & 1 deletion Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class WalletRequestSubscriber {
Task(priority: .high) {
let assertionId = payload.decryptedPayload.sha256().toHexString()
do {
let response = try await verifyClient.verifyOrigin(assertionId: assertionId)
let response = try await verifyClient.verify(.v1(assertionId: assertionId))
let verifyContext = verifyClient.createVerifyContext(origin: response.origin, domain: payload.request.payloadParams.domain, isScam: response.isScam)
verifyContextStore.set(verifyContext, forKey: request.id.string)
onRequest?((request, verifyContext))
Expand Down
38 changes: 36 additions & 2 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

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 {

let jwtString: String
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this be private?

Suggested change
let jwtString: String
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
13 changes: 9 additions & 4 deletions Sources/WalletConnectRelay/RPC/Methods/Subscription.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,24 @@ struct Subscription: RelayRPC {
let topic: String
let message: String
let publishedAt: Date
let attestation: String?

enum CodingKeys: String, CodingKey {
case topic, message, publishedAt
case topic, message, publishedAt, attestation
}

internal init(topic: String, message: String, publishedAt: Date) {
internal init(topic: String, message: String, publishedAt: Date, attestation: String?) {
self.topic = topic
self.message = message
self.publishedAt = publishedAt
self.attestation = attestation
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
topic = try container.decode(String.self, forKey: .topic)
message = try container.decode(String.self, forKey: .message)
attestation = try? container.decode(String.self, forKey: .attestation)
let publishedAtMiliseconds = try container.decode(UInt64.self, forKey: .publishedAt)
publishedAt = Date(milliseconds: publishedAtMiliseconds)
}
Expand All @@ -45,7 +48,9 @@ struct Subscription: RelayRPC {
"irn_subscription"
}

init(id: String, topic: String, message: String) {
self.params = Params(id: id, data: Params.Contents(topic: topic, message: message, publishedAt: Date()))
#if DEBUG
init(id: String, topic: String, message: String, attestation: String? = nil) {
self.params = Params(id: id, data: Params.Contents(topic: topic, message: message, publishedAt: Date(), attestation: attestation))
}
#endif
}
6 changes: 3 additions & 3 deletions Sources/WalletConnectRelay/RelayClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public final class RelayClient {
return dispatcher.isSocketConnected
}

public var messagePublisher: AnyPublisher<(topic: String, message: String, publishedAt: Date), Never> {
public var messagePublisher: AnyPublisher<(topic: String, message: String, publishedAt: Date, attestation: String?), Never> {
messagePublisherSubject.eraseToAnyPublisher()
}

Expand All @@ -38,7 +38,7 @@ public final class RelayClient {
dispatcher.networkConnectionStatusPublisher
}

private let messagePublisherSubject = PassthroughSubject<(topic: String, message: String, publishedAt: Date), Never>()
private let messagePublisherSubject = PassthroughSubject<(topic: String, message: String, publishedAt: Date, attestation: String?), Never>()

private let subscriptionResponsePublisherSubject = PassthroughSubject<(RPCID?, [String]), Never>()
private var subscriptionResponsePublisher: AnyPublisher<(RPCID?, [String]), Never> {
Expand Down Expand Up @@ -238,7 +238,7 @@ public final class RelayClient {
try acknowledgeRequest(request)
try rpcHistory.set(request, forTopic: params.data.topic, emmitedBy: .remote, transportType: .relay)
logger.debug("received message: \(params.data.message) on topic: \(params.data.topic)")
messagePublisherSubject.send((params.data.topic, params.data.message, params.data.publishedAt))
messagePublisherSubject.send((params.data.topic, params.data.message, params.data.publishedAt, params.data.attestation))
} catch {
logger.error("RPC History 'set()' error: \(error)")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ class ApproveSessionAuthenticateUtil {
private let logger: ConsoleLogging
private let sessionStore: WCSessionStorage
private let sessionNamespaceBuilder: SessionNamespaceBuilder
private let verifyContextStore: CodableStore<VerifyContext>
private let verifyClient: VerifyClientProtocol

init(
logger: ConsoleLogging,
Expand All @@ -23,7 +25,9 @@ class ApproveSessionAuthenticateUtil {
messageFormatter: SIWEFromCacaoFormatting,
sessionStore: WCSessionStorage,
sessionNamespaceBuilder: SessionNamespaceBuilder,
networkingInteractor: NetworkInteracting
networkingInteractor: NetworkInteracting,
verifyContextStore: CodableStore<VerifyContext>,
verifyClient: VerifyClientProtocol
) {
self.logger = logger
self.kms = kms
Expand All @@ -33,6 +37,8 @@ class ApproveSessionAuthenticateUtil {
self.signatureVerifier = signatureVerifier
self.messageFormatter = messageFormatter
self.networkingInteractor = networkingInteractor
self.verifyContextStore = verifyContextStore
self.verifyClient = verifyClient
}

func getsessionAuthenticateRequestParams(requestId: RPCID) throws -> (request: SessionAuthenticateRequestParams, topic: String) {
Expand Down Expand Up @@ -60,13 +66,13 @@ class ApproveSessionAuthenticateUtil {
return (topic, keys)
}


func createSession(
response: SessionAuthenticateResponseParams,
pairingTopic: String,
request: SessionAuthenticateRequestParams,
sessionTopic: String,
transportType: WCSession.TransportType
transportType: WCSession.TransportType,
verifyContext: VerifyContext
) throws -> Session? {


Expand Down Expand Up @@ -101,7 +107,8 @@ class ApproveSessionAuthenticateUtil {
settleParams: settleParams,
requiredNamespaces: [:],
acknowledged: true,
transportType: transportType
transportType: transportType,
verifyContext: verifyContext
)
logger.debug("created a session with topic: \(sessionTopic)")

Expand All @@ -114,6 +121,12 @@ class ApproveSessionAuthenticateUtil {
return session.publicRepresentation()
}


func getVerifyContext(requestId: RPCID, domain: String) -> VerifyContext {
(try? verifyContextStore.get(key: requestId.string)) ?? verifyClient.createVerifyContext(origin: nil, domain: domain, isScam: false)
}
Copy link
Contributor

@jackpooleywc jackpooleywc Jul 30, 2024

Choose a reason for hiding this comment

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

I find this a bit easier to read.

Suggested change
func getVerifyContext(requestId: RPCID, domain: String) -> VerifyContext {
(try? verifyContextStore.get(key: requestId.string)) ?? verifyClient.createVerifyContext(origin: nil, domain: domain, isScam: false)
}
func getVerifyContext(requestId: RPCID, domain: String) -> VerifyContext {
guard let context = try? verifyContextStore.get(key: requestId.string) else {
return verifyClient.createVerifyContext(origin: nil, domain: domain, isScam: false)
}
return context
}



func recoverAndVerifySignature(cacaos: [Cacao]) async throws {
try await cacaos.asyncForEach { [unowned self] cacao in
guard
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ final class LinkEnvelopesDispatcher {
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: Data(), publishedAt: Date(), derivedTopic: nil)
return RequestSubscriptionPayload(id: id, topic: topic, request: request, decryptedPayload: Data(), publishedAt: Date(), derivedTopic: nil, encryptedMessage: "", attestation: nil)
} catch {
self?.logger.debug(error)
}
Expand Down
Loading
Loading