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

[Auth] Test respond error #461

Merged
merged 5 commits into from
Aug 24, 2022
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
44 changes: 42 additions & 2 deletions Example/IntegrationTests/Auth/AuthTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ final class AuthTests: XCTestCase {
try! await wallet.pair(uri: uri)
wallet.authRequestPublisher.sink { [unowned self] (id, message) in
Task(priority: .high) {
let signature = try! MessageSigner(signer: Signer()).sign(message: message, privateKey: prvKey)
let signature = try! MessageSigner().sign(message: message, privateKey: prvKey)
let cacaoSignature = CacaoSignature(t: "eip191", s: signature)
try! await wallet.respond(requestId: id, result: .success(cacaoSignature))
try! await wallet.respond(requestId: id, signature: cacaoSignature)
}
}
.store(in: &publishers)
Expand All @@ -80,4 +80,44 @@ final class AuthTests: XCTestCase {
.store(in: &publishers)
wait(for: [responseExpectation], timeout: 5)
}

func testUserRespondError() async {
let responseExpectation = expectation(description: "error response delivered")
let uri = try! await app.request(RequestParams.stub())
try! await wallet.pair(uri: uri)
wallet.authRequestPublisher.sink { [unowned self] (id, message) in
Task(priority: .high) {
try! await wallet.reject(requestId: id)
}
}
.store(in: &publishers)
app.authResponsePublisher.sink { (id, result) in
guard case .failure(let error) = result else { XCTFail(); return }
XCTAssertEqual(error, .userRejeted)
responseExpectation.fulfill()
}
.store(in: &publishers)
wait(for: [responseExpectation], timeout: 5)
}

func testRespondSignatureVerificationFailed() async {
let responseExpectation = expectation(description: "invalid signature response delivered")
let uri = try! await app.request(RequestParams.stub())
try! await wallet.pair(uri: uri)
wallet.authRequestPublisher.sink { [unowned self] (id, message) in
Task(priority: .high) {
let invalidSignature = "438effc459956b57fcd9f3dac6c675f9cee88abf21acab7305e8e32aa0303a883b06dcbd956279a7a2ca21ffa882ff55cc22e8ab8ec0f3fe90ab45f306938cfa1b"
let cacaoSignature = CacaoSignature(t: "eip191", s: invalidSignature)
try! await wallet.respond(requestId: id, signature: cacaoSignature)
}
}
.store(in: &publishers)
app.authResponsePublisher.sink { (id, result) in
guard case .failure(let error) = result else { XCTFail(); return }
XCTAssertEqual(error, .signatureVerificationFailed)
responseExpectation.fulfill()
}
.store(in: &publishers)
wait(for: [responseExpectation], timeout: 2)
}
}
9 changes: 7 additions & 2 deletions Sources/Auth/AuthClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import WalletConnectUtils
import WalletConnectPairing
import WalletConnectRelay


public class AuthClient {
enum Errors: Error {
case malformedPairingURI
Expand Down Expand Up @@ -87,9 +88,13 @@ public class AuthClient {
try await appRequestService.request(params: params, topic: topic)
}

public func respond(requestId: RPCID, result: Result<CacaoSignature, Never>) async throws {
public func respond(requestId: RPCID, signature: CacaoSignature) async throws {
guard let account = account else { throw Errors.unknownWalletAddress }
try await walletRespondService.respond(requestId: requestId, result: result, account: account)
try await walletRespondService.respond(requestId: requestId, signature: signature, account: account)
}

public func reject(requestId: RPCID) async throws {
Copy link
Contributor

Choose a reason for hiding this comment

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

Good job!

try await walletRespondService.respondError(requestId: requestId)
}

public func getPendingRequests() throws -> [AuthRequest] {
Expand Down
13 changes: 10 additions & 3 deletions Sources/Auth/Services/App/AppRespondSubscriber.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,23 @@ class AppRespondSubscriber {

private func subscribeForResponse() {
networkingInteractor.responsePublisher.sink { [unowned self] subscriptionPayload in
let response = subscriptionPayload.response
guard
let requestId = subscriptionPayload.response.id,
let requestId = response.id,
let request = rpcHistory.get(recordId: requestId)?.request,
let requestParams = request.params, request.method == "wc_authRequest"
else { return }

networkingInteractor.unsubscribe(topic: subscriptionPayload.topic)

if let errorResponse = response.error,
llbartekll marked this conversation as resolved.
Show resolved Hide resolved
let error = AuthError(code: errorResponse.code) {
onResponse?(requestId, .failure(error))
return
}

guard
let cacao = try? subscriptionPayload.response.result?.get(Cacao.self),
let cacao = try? response.result?.get(Cacao.self),
let address = try? DIDPKH(iss: cacao.payload.iss).account.address,
let message = try? messageFormatter.formatMessage(from: cacao.payload)
else { self.onResponse?(requestId, .failure(.malformedResponseParams)); return }
Expand All @@ -49,7 +56,7 @@ class AppRespondSubscriber {
else { self.onResponse?(requestId, .failure(.messageCompromised)); return }

guard let _ = try? signatureVerifier.verify(signature: cacao.signature.s, message: message, address: address)
else { self.onResponse?(requestId, .failure(.messageVerificationFailed)); return }
else { self.onResponse?(requestId, .failure(.signatureVerificationFailed)); return }

onResponse?(requestId, .success(cacao))

Expand Down
8 changes: 4 additions & 4 deletions Sources/Auth/Services/Signer/MessageSigner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ protocol MessageSigning {
func sign(message: String, privateKey: Data) throws -> String
}

struct MessageSigner: MessageSignatureVerifying, MessageSigning {
public struct MessageSigner: MessageSignatureVerifying, MessageSigning {

enum Errors: Error {
case signatureValidationFailed
Expand All @@ -17,17 +17,17 @@ struct MessageSigner: MessageSignatureVerifying, MessageSigning {

private let signer: Signer

init(signer: Signer) {
public init(signer: Signer = Signer()) {
self.signer = signer
}

func sign(message: String, privateKey: Data) throws -> String {
public func sign(message: String, privateKey: Data) throws -> String {
guard let messageData = message.data(using: .utf8) else { throw Errors.utf8EncodingFailed }
let signature = try signer.sign(message: messageData, with: privateKey)
return signature.toHexString()
}

func verify(signature: String, message: String, address: String) throws {
public func verify(signature: String, message: String, address: String) throws {
guard let messageData = message.data(using: .utf8) else { throw Errors.utf8EncodingFailed }
let signatureData = Data(hex: signature)
guard try signer.isValid(signature: signatureData, message: messageData, address: address)
Expand Down
4 changes: 3 additions & 1 deletion Sources/Auth/Services/Signer/Signer.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import Foundation
import Web3

struct Signer {
public struct Signer {

typealias Signature = (v: UInt, r: [UInt8], s: [UInt8])

public init() {}

func sign(message: Data, with key: Data) throws -> Data {
let prefixed = prefixed(message: message)
let privateKey = try EthereumPrivateKey(privateKey: key.bytes)
Expand Down
13 changes: 2 additions & 11 deletions Sources/Auth/Services/Wallet/WalletRespondService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,7 @@ actor WalletRespondService {
self.rpcHistory = rpcHistory
}

func respond(requestId: RPCID, result: Result<CacaoSignature, Never>, account: Account) async throws {
switch result {
case .success(let signature):
try await respond(requestId: requestId, signature: signature, account: account)
case .failure:
try await respondError(requestId: requestId)
}
}

private func respond(requestId: RPCID, signature: CacaoSignature, account: Account) async throws {
func respond(requestId: RPCID, signature: CacaoSignature, account: Account) async throws {
let authRequestParams = try getAuthRequestParams(requestId: requestId)
let (topic, keys) = try generateAgreementKeys(requestParams: authRequestParams)

Expand All @@ -44,7 +35,7 @@ actor WalletRespondService {
try await networkingInteractor.respond(topic: topic, response: response, tag: AuthResponseParams.tag, envelopeType: .type1(pubKey: keys.publicKey.rawRepresentation))
}

private func respondError(requestId: RPCID) async throws {
func respondError(requestId: RPCID) async throws {
let authRequestParams = try getAuthRequestParams(requestId: requestId)
let (topic, keys) = try generateAgreementKeys(requestParams: authRequestParams)

Expand Down
12 changes: 9 additions & 3 deletions Sources/Auth/Types/Cacao/CacaoSignature.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import Foundation

public struct CacaoSignature: Codable, Equatable {
public let t: String
public let s: String
public let m: String? = nil
let t: String
let s: String
let m: String?

public init(t: String, s: String, m: String? = nil) {
self.t = t
self.s = s
self.m = m
}
}
23 changes: 20 additions & 3 deletions Sources/Auth/Types/Errors/AuthError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,28 @@ public enum AuthError: Codable, Equatable, Error {
case malformedResponseParams
case malformedRequestParams
case messageCompromised
case messageVerificationFailed
case signatureVerificationFailed
}

extension AuthError: Reason {

init?(code: Int) {
switch code {
case Self.userRejeted.code:
self = .userRejeted
case Self.malformedResponseParams.code:
self = .malformedResponseParams
case Self.malformedRequestParams.code:
self = .malformedRequestParams
case Self.messageCompromised.code:
self = .messageCompromised
case Self.signatureVerificationFailed.code:
self = .signatureVerificationFailed
default:
return nil
}
}

public var code: Int {
switch self {
case .userRejeted:
Expand All @@ -20,7 +37,7 @@ extension AuthError: Reason {
return 12002
case .messageCompromised:
return 12003
case .messageVerificationFailed:
case .signatureVerificationFailed:
return 12004
}
}
Expand All @@ -35,7 +52,7 @@ extension AuthError: Reason {
return "Request params malformed"
case .messageCompromised:
return "Original message compromised"
case .messageVerificationFailed:
case .signatureVerificationFailed:
return "Message verification failed"
}
}
Expand Down
7 changes: 6 additions & 1 deletion Sources/Auth/Types/RespondParams.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import Foundation

public struct RespondParams {
public struct RespondParams: Equatable {
let id: RPCID
let signature: CacaoSignature

public init(id: RPCID, signature: CacaoSignature) {
self.id = id
self.signature = signature
}
}