diff --git a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectJWT.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectJWT.xcscheme
new file mode 100644
index 000000000..0bfec5ddc
--- /dev/null
+++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectJWT.xcscheme
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectSigner.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectSigner.xcscheme
new file mode 100644
index 000000000..7d2425bad
--- /dev/null
+++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/WalletConnectSigner.xcscheme
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift
index 8bede13a1..436091772 100644
--- a/Example/IntegrationTests/Auth/AuthTests.swift
+++ b/Example/IntegrationTests/Auth/AuthTests.swift
@@ -83,7 +83,8 @@ final class AuthTests: XCTestCase {
Task(priority: .high) {
let signerFactory = DefaultSignerFactory()
let signer = MessageSignerFactory(signerFactory: signerFactory).create(projectId: InputConfig.projectId)
- let signature = try! signer.sign(payload: request.payload, address: walletAccount.address, privateKey: prvKey, type: .eip191)
+ let payload = try! request.payload.cacaoPayload(address: walletAccount.address)
+ let signature = try! signer.sign(payload: payload, privateKey: prvKey, type: .eip191)
try! await walletAuthClient.respond(requestId: request.id, signature: signature, from: walletAccount)
}
}
diff --git a/Example/IntegrationTests/Auth/Signer/CacaoSignerTests.swift b/Example/IntegrationTests/Auth/Signer/CacaoSignerTests.swift
index c6481015b..8066c147d 100644
--- a/Example/IntegrationTests/Auth/Signer/CacaoSignerTests.swift
+++ b/Example/IntegrationTests/Auth/Signer/CacaoSignerTests.swift
@@ -45,12 +45,10 @@ class CacaoSignerTest: XCTestCase {
func testCacaoSign() throws {
let address = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
- let formatted = try SIWEMessageFormatter().formatMessage(
- from: payload,
- address: address
- )
+ let cacaoPayload = try payload.cacaoPayload(address: address)
+ let formatted = try SIWECacaoFormatter().formatMessage(from: cacaoPayload)
XCTAssertEqual(formatted, message)
- XCTAssertEqual(try signer.sign(payload: payload, address: address, privateKey: privateKey, type: .eip191), signature)
+ XCTAssertEqual(try signer.sign(payload: cacaoPayload, privateKey: privateKey, type: .eip191), signature)
}
func testCacaoVerify() async throws {
diff --git a/Example/IntegrationTests/Auth/Signer/EIP1271VerifierTests.swift b/Example/IntegrationTests/Auth/Signer/EIP1271VerifierTests.swift
index 45c5c1bdd..40b66bcb4 100644
--- a/Example/IntegrationTests/Auth/Signer/EIP1271VerifierTests.swift
+++ b/Example/IntegrationTests/Auth/Signer/EIP1271VerifierTests.swift
@@ -1,6 +1,7 @@
import Foundation
import XCTest
@testable import Auth
+@testable import WalletConnectSigner
import JSONRPC
class EIP1271VerifierTests: XCTestCase {
diff --git a/Example/IntegrationTests/Auth/Signer/EIP191VerifierTests.swift b/Example/IntegrationTests/Auth/Signer/EIP191VerifierTests.swift
index 2e5c9c1f3..5d5aa2c25 100644
--- a/Example/IntegrationTests/Auth/Signer/EIP191VerifierTests.swift
+++ b/Example/IntegrationTests/Auth/Signer/EIP191VerifierTests.swift
@@ -1,6 +1,7 @@
import Foundation
import XCTest
@testable import Auth
+@testable import WalletConnectSigner
class EIP191VerifierTests: XCTestCase {
diff --git a/Example/IntegrationTests/Chat/RegistryTests.swift b/Example/IntegrationTests/Chat/RegistryTests.swift
index ea21c9375..e2f58588c 100644
--- a/Example/IntegrationTests/Chat/RegistryTests.swift
+++ b/Example/IntegrationTests/Chat/RegistryTests.swift
@@ -6,4 +6,50 @@ import WalletConnectUtils
final class RegistryTests: XCTestCase {
+ let account = Account("eip155:1:0x15bca56b6e2728aec2532df9d436bd1600e86688")!
+ let privateKey = Data(hex: "305c6cde3846927892cd32762f6120539f3ec74c9e3a16b9b798b1e85351ae2a")
+
+ var sut: IdentityRegisterService!
+ var storage: IdentityStorage!
+ var signer: CacaoMessageSigner!
+
+ override func setUp() {
+ let keyserverURL = URL(string: "https://staging.keys.walletconnect.com")!
+ let httpService = HTTPNetworkClient(host: keyserverURL.host!)
+ let accountService = AccountService(currentAccount: account)
+ let identityNetworkService = IdentityNetworkService(accountService: accountService, httpService: httpService)
+ storage = IdentityStorage(keychain: KeychainStorageMock())
+ sut = IdentityRegisterService(
+ keyserverURL: keyserverURL,
+ identityStorage: storage,
+ identityNetworkService: identityNetworkService,
+ iatProvader: DefaultIATProvider(),
+ messageFormatter: SIWECacaoFormatter()
+ )
+ signer = MessageSignerFactory(signerFactory: DefaultSignerFactory()).create(projectId: InputConfig.projectId)
+ }
+
+ func testRegisterIdentityAndInviteKey() async throws {
+ var message: String!
+ let publicKey = try await sut.registerIdentity(account: account, isPrivate: false) { msg in
+ message = msg
+ return try! signer.sign(message: msg, privateKey: privateKey, type: .eip191)
+ }
+
+ let cacao = try await sut.resolveIdentity(publicKey: publicKey)
+ XCTAssertEqual(try SIWECacaoFormatter().formatMessage(from: cacao.p), message)
+
+ let recovered = storage.getIdentityKey(for: account)!.publicKey.hexRepresentation
+ XCTAssertEqual(publicKey, recovered)
+
+ let inviteKey = try await sut.registerInvite(account: account, isPrivate: false, onSign: { msg in
+ return try! signer.sign(message: msg, privateKey: privateKey, type: .eip191)
+ })
+
+ let recoveredKey = storage.getInviteKey(for: account)!
+ XCTAssertEqual(inviteKey, recoveredKey.publicKey.hexRepresentation)
+
+ let resolvedKey = try await sut.resolveInvite(account: account)
+ XCTAssertEqual(inviteKey, resolvedKey)
+ }
}
diff --git a/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestInteractor.swift b/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestInteractor.swift
index 361d0b8fe..90f554f02 100644
--- a/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestInteractor.swift
+++ b/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestInteractor.swift
@@ -8,8 +8,7 @@ final class AuthRequestInteractor {
func approve(request: AuthRequest) async throws {
let privateKey = Data(hex: "e56da0e170b5e09a8bb8f1b693392c7d56c3739a9c75740fbc558a2877868540")
let signature = try signer.sign(
- payload: request.payload,
- address: account.address,
+ payload: request.payload.cacaoPayload(address: account.address),
privateKey: privateKey,
type: .eip191)
try await Web3Wallet.instance.respond(requestId: request.id, signature: signature, from: account)
diff --git a/Package.swift b/Package.swift
index c633746d6..9a457fd6e 100644
--- a/Package.swift
+++ b/Package.swift
@@ -56,7 +56,7 @@ let package = Package(
path: "Sources/Chat"),
.target(
name: "Auth",
- dependencies: ["WalletConnectPairing"],
+ dependencies: ["WalletConnectPairing", "WalletConnectSigner"],
path: "Sources/Auth"),
.target(
name: "Web3Wallet",
@@ -72,7 +72,7 @@ let package = Package(
path: "Sources/WalletConnectEcho"),
.target(
name: "WalletConnectRelay",
- dependencies: ["WalletConnectKMS"],
+ dependencies: ["WalletConnectJWT"],
path: "Sources/WalletConnectRelay",
resources: [.copy("PackageConfig.json")]),
.target(
@@ -85,6 +85,12 @@ let package = Package(
.target(
name: "Web3Inbox",
dependencies: ["WalletConnectChat"]),
+ .target(
+ name: "WalletConnectSigner",
+ dependencies: ["WalletConnectNetworking"]),
+ .target(
+ name: "WalletConnectJWT",
+ dependencies: ["WalletConnectKMS"]),
.target(
name: "WalletConnectUtils",
dependencies: ["JSONRPC"]),
diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift
index 9ea611488..2ab5e242a 100644
--- a/Sources/Auth/AuthClient.swift
+++ b/Sources/Auth/AuthClient.swift
@@ -89,12 +89,12 @@ public class AuthClient: AuthClientProtocol {
/// Query pending authentication requests
/// - Returns: Pending authentication requests
- public func getPendingRequests(account: Account) throws -> [AuthRequest] {
- return try pendingRequestsProvider.getPendingRequests(account: account)
+ public func getPendingRequests() throws -> [AuthRequest] {
+ return try pendingRequestsProvider.getPendingRequests()
}
public func formatMessage(payload: AuthPayload, address: String) throws -> String {
- return try SIWEMessageFormatter().formatMessage(from: payload, address: address)
+ return try SIWECacaoFormatter().formatMessage(from: payload.cacaoPayload(address: address))
}
private func setUpPublishers() {
diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift
index 5fc6cb188..f9688eaf6 100644
--- a/Sources/Auth/AuthClientFactory.swift
+++ b/Sources/Auth/AuthClientFactory.swift
@@ -42,7 +42,7 @@ public struct AuthClientFactory {
let kms = KeyManagementService(keychain: keychainStorage)
let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage)
- let messageFormatter = SIWEMessageFormatter()
+ let messageFormatter = SIWECacaoFormatter()
let appRequestService = AppRequestService(networkingInteractor: networkingClient, kms: kms, appMetadata: metadata, logger: logger, iatProvader: iatProvider)
let messageSignerFactory = MessageSignerFactory(signerFactory: signerFactory)
let messageSigner = messageSignerFactory.create(projectId: projectId)
diff --git a/Sources/Auth/AuthClientProtocol.swift b/Sources/Auth/AuthClientProtocol.swift
index 9e1a1fb62..31413cb76 100644
--- a/Sources/Auth/AuthClientProtocol.swift
+++ b/Sources/Auth/AuthClientProtocol.swift
@@ -7,5 +7,5 @@ public protocol AuthClientProtocol {
func formatMessage(payload: AuthPayload, address: String) throws -> String
func respond(requestId: RPCID, signature: CacaoSignature, from account: Account) async throws
func reject(requestId: RPCID) async throws
- func getPendingRequests(account: Account) throws -> [AuthRequest]
+ func getPendingRequests() throws -> [AuthRequest]
}
diff --git a/Sources/Auth/AuthImports.swift b/Sources/Auth/AuthImports.swift
index 27245bda6..f27efa95c 100644
--- a/Sources/Auth/AuthImports.swift
+++ b/Sources/Auth/AuthImports.swift
@@ -1,3 +1,4 @@
#if !CocoaPods
@_exported import WalletConnectPairing
+@_exported import WalletConnectSigner
#endif
diff --git a/Sources/Auth/Services/App/AppRespondSubscriber.swift b/Sources/Auth/Services/App/AppRespondSubscriber.swift
index d070880f5..70b48f95a 100644
--- a/Sources/Auth/Services/App/AppRespondSubscriber.swift
+++ b/Sources/Auth/Services/App/AppRespondSubscriber.swift
@@ -6,7 +6,7 @@ class AppRespondSubscriber {
private let logger: ConsoleLogging
private let rpcHistory: RPCHistory
private let signatureVerifier: MessageSignatureVerifying
- private let messageFormatter: SIWEMessageFormatting
+ private let messageFormatter: SIWECacaoFormatting
private let pairingRegisterer: PairingRegisterer
private var publishers = [AnyCancellable]()
@@ -17,7 +17,7 @@ class AppRespondSubscriber {
rpcHistory: RPCHistory,
signatureVerifier: MessageSignatureVerifying,
pairingRegisterer: PairingRegisterer,
- messageFormatter: SIWEMessageFormatting) {
+ messageFormatter: SIWECacaoFormatting) {
self.networkingInteractor = networkingInteractor
self.logger = logger
self.rpcHistory = rpcHistory
@@ -52,8 +52,7 @@ class AppRespondSubscriber {
guard
let recovered = try? messageFormatter.formatMessage(
- from: requestPayload.payloadParams,
- address: address
+ from: requestPayload.payloadParams.cacaoPayload(address: address)
), recovered == message
else { self.onResponse?(requestId, .failure(.messageCompromised)); return }
diff --git a/Sources/Auth/Services/Common/IATProvider.swift b/Sources/Auth/Services/Common/IATProvider.swift
deleted file mode 100644
index 1d386c11e..000000000
--- a/Sources/Auth/Services/Common/IATProvider.swift
+++ /dev/null
@@ -1,11 +0,0 @@
-import Foundation
-
-protocol IATProvider {
- var iat: String { get }
-}
-
-struct DefaultIATProvider: IATProvider {
- var iat: String {
- return ISO8601DateFormatter().string(from: Date())
- }
-}
diff --git a/Sources/Auth/Services/Common/SIWEMessageFormatter.swift b/Sources/Auth/Services/Common/SIWEMessageFormatter.swift
deleted file mode 100644
index 5e8c82d57..000000000
--- a/Sources/Auth/Services/Common/SIWEMessageFormatter.swift
+++ /dev/null
@@ -1,109 +0,0 @@
-import Foundation
-
-protocol SIWEMessageFormatting {
- func formatMessage(from payload: AuthPayload, address: String) throws -> String
- func formatMessage(from payload: CacaoPayload) throws -> String
-}
-
-public struct SIWEMessageFormatter: SIWEMessageFormatting {
-
- enum Errors: Error {
- case invalidChainID
- }
-
- public init() { }
-
- public func formatMessage(from payload: AuthPayload, address: String) throws -> String {
- guard let chain = Blockchain(payload.chainId) else {
- throw Errors.invalidChainID
- }
- let message = SIWEMessage(
- domain: payload.domain,
- uri: payload.aud,
- address: address,
- version: payload.version,
- nonce: payload.nonce,
- chainId: chain.reference,
- iat: payload.iat,
- nbf: payload.nbf,
- exp: payload.exp,
- statement: payload.statement,
- requestId: payload.requestId,
- resources: payload.resources
- )
- return message.formatted
- }
-
- func formatMessage(from payload: CacaoPayload) throws -> String {
- let address = try DIDPKH(iss: payload.iss).account.address
- let iss = try DIDPKH(iss: payload.iss)
- let message = SIWEMessage(
- domain: payload.domain,
- uri: payload.aud,
- address: address,
- version: payload.version,
- nonce: payload.nonce,
- chainId: iss.account.reference,
- iat: payload.iat,
- nbf: payload.nbf,
- exp: payload.exp,
- statement: payload.statement,
- requestId: payload.requestId,
- resources: payload.resources
- )
- return message.formatted
- }
-}
-
-private struct SIWEMessage: Equatable {
- let domain: String
- let uri: String // aud
- let address: String
- let version: String
- let nonce: String
- let chainId: String
- let iat: String
- let nbf: String?
- let exp: String?
- let statement: String?
- let requestId: String?
- let resources: [String]?
-
- var formatted: String {
- return """
- \(domain) wants you to sign in with your Ethereum account:
- \(address)
- \(statementLine)
- URI: \(uri)
- Version: \(version)
- Chain ID: \(chainId)
- Nonce: \(nonce)
- Issued At: \(iat)\(expLine)\(nbfLine)\(requestIdLine)\(resourcesSection)
- """
- }
-
- var expLine: String {
- guard let exp = exp else { return "" }
- return "\nExpiration Time: \(exp)"
- }
-
- var statementLine: String {
- guard let statement = statement else { return "" }
- return "\n\(statement)\n"
- }
-
- var nbfLine: String {
- guard let nbf = nbf else { return "" }
- return "\nNot Before: \(nbf)"
- }
-
- var requestIdLine: String {
- guard let requestId = requestId else { return "" }
- return "\nRequest ID: \(requestId)"
- }
-
- var resourcesSection: String {
- guard let resources = resources else { return "" }
- return resources.reduce("\nResources:") { $0 + "\n- \($1)" }
- }
-}
diff --git a/Sources/Auth/Services/Wallet/PendingRequestsProvider.swift b/Sources/Auth/Services/Wallet/PendingRequestsProvider.swift
index 4aa8c04c0..524c029d5 100644
--- a/Sources/Auth/Services/Wallet/PendingRequestsProvider.swift
+++ b/Sources/Auth/Services/Wallet/PendingRequestsProvider.swift
@@ -7,7 +7,7 @@ class PendingRequestsProvider {
self.rpcHistory = rpcHistory
}
- public func getPendingRequests(account: Account) throws -> [AuthRequest] {
+ public func getPendingRequests() throws -> [AuthRequest] {
let pendingRequests: [AuthRequest] = rpcHistory.getPending()
.filter {$0.request.method == "wc_authRequest"}
.compactMap {
diff --git a/Sources/Auth/Services/Wallet/WalletRespondService.swift b/Sources/Auth/Services/Wallet/WalletRespondService.swift
index 595489aa3..62bb3427a 100644
--- a/Sources/Auth/Services/Wallet/WalletRespondService.swift
+++ b/Sources/Auth/Services/Wallet/WalletRespondService.swift
@@ -29,9 +29,8 @@ actor WalletRespondService {
try kms.setAgreementSecret(keys, topic: topic)
- let didpkh = DIDPKH(account: account)
let header = CacaoHeader(t: "eip4361")
- let payload = CacaoPayload(params: authRequestParams.payloadParams, didpkh: didpkh)
+ let payload = try authRequestParams.payloadParams.cacaoPayload(address: account.address)
let responseParams = AuthResponseParams(h: header, p: payload, s: signature)
let response = RPCResponse(id: requestId, result: responseParams)
diff --git a/Sources/Auth/Types/AuthPayload.swift b/Sources/Auth/Types/AuthPayload.swift
index 621df1ea3..0e6f7fd1d 100644
--- a/Sources/Auth/Types/AuthPayload.swift
+++ b/Sources/Auth/Types/AuthPayload.swift
@@ -28,4 +28,32 @@ public struct AuthPayload: Codable, Equatable {
self.requestId = requestParams.requestId
self.resources = requestParams.resources
}
+
+ public func cacaoPayload(address: String) throws -> CacaoPayload {
+ guard
+ let blockchain = Blockchain(chainId),
+ let account = Account(blockchain: blockchain, address: address) else {
+ throw Errors.invalidChainID
+ }
+ return CacaoPayload(
+ iss: DIDPKH(account: account).iss,
+ domain: domain,
+ aud: aud,
+ version: version,
+ nonce: nonce,
+ iat: iat,
+ nbf: nbf,
+ exp: exp,
+ statement: statement,
+ requestId: requestId,
+ resources: resources
+ )
+ }
+}
+
+private extension AuthPayload {
+
+ enum Errors: Error {
+ case invalidChainID
+ }
}
diff --git a/Sources/Auth/Types/Cacao/Cacao.swift b/Sources/Auth/Types/Cacao/Cacao.swift
deleted file mode 100644
index a82da2ab4..000000000
--- a/Sources/Auth/Types/Cacao/Cacao.swift
+++ /dev/null
@@ -1,10 +0,0 @@
-import Foundation
-
-/// CAIP-74 Cacao object
-///
-/// specs at: https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-74.md
-public struct Cacao: Codable, Equatable {
- let h: CacaoHeader
- let p: CacaoPayload
- let s: CacaoSignature
-}
diff --git a/Sources/Auth/Types/Cacao/CacaoHeader.swift b/Sources/Auth/Types/Cacao/CacaoHeader.swift
deleted file mode 100644
index 1461f3ae4..000000000
--- a/Sources/Auth/Types/Cacao/CacaoHeader.swift
+++ /dev/null
@@ -1,5 +0,0 @@
-import Foundation
-
-struct CacaoHeader: Codable, Equatable {
- let t: String
-}
diff --git a/Sources/Auth/Types/Cacao/CacaoPayload.swift b/Sources/Auth/Types/Cacao/CacaoPayload.swift
deleted file mode 100644
index 41f66e736..000000000
--- a/Sources/Auth/Types/Cacao/CacaoPayload.swift
+++ /dev/null
@@ -1,29 +0,0 @@
-import Foundation
-
-struct CacaoPayload: Codable, Equatable {
- let iss: String
- let domain: String
- let aud: String
- let version: String
- let nonce: String
- let iat: String
- let nbf: String?
- let exp: String?
- let statement: String?
- let requestId: String?
- let resources: [String]?
-
- init(params: AuthPayload, didpkh: DIDPKH) {
- self.iss = didpkh.iss
- self.domain = params.domain
- self.aud = params.aud
- self.version = "1"
- self.nonce = params.nonce
- self.iat = params.iat
- self.nbf = params.nbf
- self.exp = params.exp
- self.statement = params.statement
- self.requestId = params.requestId
- self.resources = params.resources
- }
-}
diff --git a/Sources/Chat/ChatImports.swift b/Sources/Chat/ChatImports.swift
index 23c1738ef..338babe27 100644
--- a/Sources/Chat/ChatImports.swift
+++ b/Sources/Chat/ChatImports.swift
@@ -1,3 +1,3 @@
#if !CocoaPods
-@_exported import WalletConnectNetworking
+@_exported import WalletConnectSigner
#endif
diff --git a/Sources/Chat/ProtocolServices/Identity/IdentityKey.swift b/Sources/Chat/ProtocolServices/Identity/IdentityKey.swift
new file mode 100644
index 000000000..b172afbdb
--- /dev/null
+++ b/Sources/Chat/ProtocolServices/Identity/IdentityKey.swift
@@ -0,0 +1,3 @@
+import Foundation
+
+typealias IdentityKey = SigningPrivateKey
diff --git a/Sources/Chat/ProtocolServices/Identity/IdentityKeyAPI.swift b/Sources/Chat/ProtocolServices/Identity/IdentityKeyAPI.swift
new file mode 100644
index 000000000..1346d40ac
--- /dev/null
+++ b/Sources/Chat/ProtocolServices/Identity/IdentityKeyAPI.swift
@@ -0,0 +1,64 @@
+import Foundation
+
+enum IdentityKeyAPI: HTTPService {
+
+ case registerIdentity(cacao: Cacao)
+ case resolveIdentity(publicKey: String)
+ case removeIdentity(cacao: Cacao)
+ case registerInvite(idAuth: String)
+ case resolveInvite(account: String)
+ case removeInvite(idAuth: String)
+
+ var path: String {
+ switch self {
+ case .registerIdentity, .resolveIdentity, .removeIdentity:
+ return "/identity"
+ case .registerInvite, .resolveInvite, .removeInvite:
+ return "/invite"
+ }
+ }
+
+ var method: WalletConnectNetworking.HTTPMethod {
+ switch self {
+ case .registerIdentity, .registerInvite:
+ return .post
+ case .resolveIdentity, .resolveInvite:
+ return .get
+ case .removeInvite, .removeIdentity:
+ return .delete
+ }
+ }
+
+ var body: Data? {
+ switch self {
+ case .registerIdentity(let cacao), .removeIdentity(let cacao):
+ return try? JSONEncoder().encode(RegisterIdentityRequest(cacao: cacao))
+ case .registerInvite(let idAuth), .removeInvite(let idAuth):
+ return try? JSONEncoder().encode(RegisterInviteRequest(idAuth: idAuth))
+ case .resolveIdentity, .resolveInvite:
+ return nil
+ }
+ }
+
+ var queryParameters: [String : String]? {
+ switch self {
+ case .resolveIdentity(let publicKey):
+ return ["publicKey": publicKey]
+ case .resolveInvite(let account):
+ return ["account": account]
+ case .registerIdentity, .registerInvite, .removeInvite, .removeIdentity:
+ return nil
+ }
+ }
+}
+
+private extension IdentityKeyAPI {
+
+ struct RegisterIdentityRequest: Codable {
+ let cacao: Cacao
+ }
+
+ struct RegisterInviteRequest: Codable {
+ let idAuth: String
+ }
+}
diff --git a/Sources/Chat/ProtocolServices/Identity/IdentityNetworkService.swift b/Sources/Chat/ProtocolServices/Identity/IdentityNetworkService.swift
new file mode 100644
index 000000000..ca460fe7e
--- /dev/null
+++ b/Sources/Chat/ProtocolServices/Identity/IdentityNetworkService.swift
@@ -0,0 +1,65 @@
+import Foundation
+
+actor IdentityNetworkService {
+
+ private let accountService: AccountService
+ private let httpService: HTTPClient
+
+ init(accountService: AccountService, httpService: HTTPClient) {
+ self.accountService = accountService
+ self.httpService = httpService
+ }
+
+ // MARK: - IdentityKey
+
+ func registerIdentity(cacao: Cacao) async throws {
+ let api = IdentityKeyAPI.registerIdentity(cacao: cacao)
+ try await httpService.request(service: api)
+ }
+
+ func resolveIdentity(publicKey: String) async throws -> Cacao {
+ let api = IdentityKeyAPI.resolveIdentity(publicKey: publicKey)
+ let response = try await httpService.request(ResolveIdentityResponse.self, at: api)
+ return response.value.cacao
+ }
+
+ func removeIdentity(cacao: Cacao) async throws {
+ let api = IdentityKeyAPI.removeIdentity(cacao: cacao)
+ try await httpService.request(service: api)
+ }
+
+ // MARK: - InviteKey
+
+ func registerInvite(idAuth: String) async throws {
+ let api = IdentityKeyAPI.registerInvite(idAuth: idAuth)
+ try await httpService.request(service: api)
+ }
+
+ func resolveInvite(account: String) async throws -> String {
+ let api = IdentityKeyAPI.resolveInvite(account: account)
+ let response = try await httpService.request(ResolveInviteResponse.self, at: api)
+ return response.value.inviteKey
+ }
+
+ func removeInvite(idAuth: String) async throws {
+ let api = IdentityKeyAPI.removeInvite(idAuth: idAuth)
+ try await httpService.request(service: api)
+ }
+}
+
+private extension IdentityNetworkService {
+
+ struct ResolveIdentityResponse: Codable {
+ struct Value: Codable {
+ let cacao: Cacao
+ }
+ let value: Value
+ }
+
+ struct ResolveInviteResponse: Codable {
+ struct Value: Codable {
+ let inviteKey: String
+ }
+ let value: Value
+ }
+}
diff --git a/Sources/Chat/ProtocolServices/Identity/IdentityRegisterService.swift b/Sources/Chat/ProtocolServices/Identity/IdentityRegisterService.swift
new file mode 100644
index 000000000..963290a9e
--- /dev/null
+++ b/Sources/Chat/ProtocolServices/Identity/IdentityRegisterService.swift
@@ -0,0 +1,131 @@
+import Foundation
+
+actor IdentityRegisterService {
+
+ private let keyserverURL: URL
+ private let identityStorage: IdentityStorage
+ private let identityNetworkService: IdentityNetworkService
+ private let iatProvader: IATProvider
+ private let messageFormatter: SIWECacaoFormatting
+
+ init(
+ keyserverURL: URL,
+ identityStorage: IdentityStorage,
+ identityNetworkService: IdentityNetworkService,
+ iatProvader: IATProvider,
+ messageFormatter: SIWECacaoFormatting
+ ) {
+ self.keyserverURL = keyserverURL
+ self.identityStorage = identityStorage
+ self.identityNetworkService = identityNetworkService
+ self.iatProvader = iatProvader
+ self.messageFormatter = messageFormatter
+ }
+
+ func registerIdentity(account: Account,
+ isPrivate: Bool,
+ onSign: (String) -> CacaoSignature
+ ) async throws -> String {
+
+ if let identityKey = identityStorage.getIdentityKey(for: account) {
+ return identityKey.publicKey.hexRepresentation
+ }
+
+ let identityKey = IdentityKey()
+ let cacao = try makeCacao(DIDKey: identityKey.DIDKey, account: account, onSign: onSign)
+ try await identityNetworkService.registerIdentity(cacao: cacao)
+
+ // TODO: Handle private mode
+
+ try identityStorage.saveIdentityKey(identityKey, for: account)
+ return identityKey.publicKey.hexRepresentation
+ }
+
+ func registerInvite(account: Account,
+ isPrivate: Bool,
+ onSign: (String) -> CacaoSignature
+ ) async throws -> String {
+
+ if let inviteKey = identityStorage.getInviteKey(for: account) {
+ return inviteKey.publicKey.hexRepresentation
+ }
+
+ let inviteKey = IdentityKey()
+ let invitePublicKey = inviteKey.publicKey.hexRepresentation
+ let idAuth = try makeIDAuth(account: account, invitePublicKey: invitePublicKey)
+ try await identityNetworkService.registerInvite(idAuth: idAuth)
+
+ // TODO: Handle private mode
+
+ try identityStorage.saveInviteKey(inviteKey, for: account)
+ return inviteKey.publicKey.hexRepresentation
+ }
+
+ func resolveIdentity(publicKey: String) async throws -> Cacao {
+ let data = Data(hex: publicKey)
+ let did = ED25519DIDKeyFactory().make(pubKey: data, prefix: false)
+ return try await identityNetworkService.resolveIdentity(publicKey: did)
+ }
+
+ func resolveInvite(account: Account) async throws -> String {
+ return try await identityNetworkService.resolveInvite(account: account.absoluteString)
+ }
+}
+
+private extension IdentityRegisterService {
+
+ enum Errors: Error {
+ case identityKeyNotFound
+ }
+
+ func makeCacao(
+ DIDKey: String,
+ account: Account,
+ onSign: (String) -> CacaoSignature
+ ) throws -> Cacao {
+ let cacaoHeader = CacaoHeader(t: "eip4361")
+ let cacaoPayload = CacaoPayload(
+ iss: account.iss,
+ domain: keyserverURL.host!,
+ aud: getAudience(),
+ version: getVersion(),
+ nonce: getNonce(),
+ iat: iatProvader.iat,
+ nbf: nil, exp: nil, statement: nil, requestId: nil,
+ resources: [DIDKey]
+ )
+ let cacaoSignature = onSign(try messageFormatter.formatMessage(from: cacaoPayload))
+ return Cacao(h: cacaoHeader, p: cacaoPayload, s: cacaoSignature)
+ }
+
+ func makeIDAuth(account: Account, invitePublicKey: String) throws -> String {
+ guard let identityKey = identityStorage.getIdentityKey(for: account)
+ else { throw Errors.identityKeyNotFound }
+ return try JWTFactory().createAndSignJWT(
+ keyPair: identityKey,
+ sub: invitePublicKey,
+ aud: getAudience(),
+ exp: getExpiry(),
+ pkh: account.iss
+ )
+ }
+
+ private func getNonce() -> String {
+ return Data.randomBytes(count: 32).toHexString()
+ }
+
+ private func getVersion() -> String {
+ return "1"
+ }
+
+ private func getExpiry() -> Int {
+ var components = DateComponents()
+ components.setValue(1, for: .hour)
+ let date = Calendar.current.date(byAdding: components, to: Date())!
+ return Int(date.timeIntervalSince1970)
+ }
+
+ private func getAudience() -> String {
+ return keyserverURL.absoluteString
+ }
+}
diff --git a/Sources/Chat/ProtocolServices/Identity/IdentityStorage.swift b/Sources/Chat/ProtocolServices/Identity/IdentityStorage.swift
new file mode 100644
index 000000000..7f93bb075
--- /dev/null
+++ b/Sources/Chat/ProtocolServices/Identity/IdentityStorage.swift
@@ -0,0 +1,37 @@
+import Foundation
+
+final class IdentityStorage {
+
+ private let keychain: KeychainStorageProtocol
+
+ init(keychain: KeychainStorageProtocol) {
+ self.keychain = keychain
+ }
+
+ func saveIdentityKey(_ key: IdentityKey, for account: Account) throws {
+ try keychain.add(key, forKey: identityKeyIdentifier(for: account))
+ }
+
+ func saveInviteKey(_ key: IdentityKey, for account: Account) throws {
+ try keychain.add(key, forKey: inviteKeyIdentifier(for: account))
+ }
+
+ func getIdentityKey(for account: Account) -> IdentityKey? {
+ return try? keychain.read(key: identityKeyIdentifier(for: account))
+ }
+
+ func getInviteKey(for account: Account) -> IdentityKey? {
+ return try? keychain.read(key: inviteKeyIdentifier(for: account))
+ }
+}
+
+private extension IdentityStorage {
+
+ func identityKeyIdentifier(for account: Account) -> String {
+ return "com.walletconnect.chat.identity.\(account.absoluteString)"
+ }
+
+ func inviteKeyIdentifier(for account: Account) -> String {
+ return "com.walletconnect.chat.invite.\(account.absoluteString)"
+ }
+}
diff --git a/Sources/WalletConnectRelay/ClientAuth/JWT/JWT+Claims.swift b/Sources/WalletConnectJWT/JWT+Claims.swift
similarity index 95%
rename from Sources/WalletConnectRelay/ClientAuth/JWT/JWT+Claims.swift
rename to Sources/WalletConnectJWT/JWT+Claims.swift
index 14ea4cffd..125a2b50a 100644
--- a/Sources/WalletConnectRelay/ClientAuth/JWT/JWT+Claims.swift
+++ b/Sources/WalletConnectJWT/JWT+Claims.swift
@@ -7,6 +7,7 @@ extension JWT {
let aud: String
let iat: Int
let exp: Int
+ let pkh: String?
func encode() throws -> String {
let jsonEncoder = JSONEncoder()
diff --git a/Sources/WalletConnectRelay/ClientAuth/JWT/JWT+Header.swift b/Sources/WalletConnectJWT/JWT+Header.swift
similarity index 100%
rename from Sources/WalletConnectRelay/ClientAuth/JWT/JWT+Header.swift
rename to Sources/WalletConnectJWT/JWT+Header.swift
diff --git a/Sources/WalletConnectRelay/ClientAuth/JWT/JWT.swift b/Sources/WalletConnectJWT/JWT.swift
similarity index 85%
rename from Sources/WalletConnectRelay/ClientAuth/JWT/JWT.swift
rename to Sources/WalletConnectJWT/JWT.swift
index 17663319b..7633fb991 100644
--- a/Sources/WalletConnectRelay/ClientAuth/JWT/JWT.swift
+++ b/Sources/WalletConnectJWT/JWT.swift
@@ -9,12 +9,12 @@ struct JWT: Codable, Equatable {
var claims: Claims
var signature: String?
- public init(header: Header = Header(), claims: Claims) {
+ init(header: Header = Header(), claims: Claims) {
self.header = header
self.claims = claims
}
- public mutating func sign(using jwtSigner: JWTSigning) throws {
+ mutating func sign(using jwtSigner: JWTSigning) throws {
header.alg = jwtSigner.alg
let headerString = try header.encode()
let claimsString = try claims.encode()
diff --git a/Sources/WalletConnectRelay/ClientAuth/JWT/JWTEncoder.swift b/Sources/WalletConnectJWT/JWTEncoder.swift
similarity index 100%
rename from Sources/WalletConnectRelay/ClientAuth/JWT/JWTEncoder.swift
rename to Sources/WalletConnectJWT/JWTEncoder.swift
diff --git a/Sources/WalletConnectJWT/JWTFactory.swift b/Sources/WalletConnectJWT/JWTFactory.swift
new file mode 100644
index 000000000..3b459561a
--- /dev/null
+++ b/Sources/WalletConnectJWT/JWTFactory.swift
@@ -0,0 +1,21 @@
+import Foundation
+
+public struct JWTFactory {
+
+ public init() { }
+
+ public func createAndSignJWT(
+ keyPair: SigningPrivateKey,
+ sub: String,
+ aud: String,
+ exp: Int,
+ pkh: String?
+ ) throws -> String {
+ let now = Int(Date().timeIntervalSince1970)
+ let iss = keyPair.DIDKey
+ let claims = JWT.Claims(iss: iss, sub: sub, aud: aud, iat: now, exp: exp, pkh: pkh)
+ var jwt = JWT(claims: claims)
+ try jwt.sign(using: EdDSASigner(keyPair))
+ return try jwt.encoded()
+ }
+}
diff --git a/Sources/WalletConnectJWT/JWTImports.swift b/Sources/WalletConnectJWT/JWTImports.swift
new file mode 100644
index 000000000..24d4a8cab
--- /dev/null
+++ b/Sources/WalletConnectJWT/JWTImports.swift
@@ -0,0 +1,3 @@
+#if !CocoaPods
+@_exported import WalletConnectKMS
+#endif
diff --git a/Sources/WalletConnectRelay/ClientAuth/JWT/JWTSigning.swift b/Sources/WalletConnectJWT/JWTSigning.swift
similarity index 100%
rename from Sources/WalletConnectRelay/ClientAuth/JWT/JWTSigning.swift
rename to Sources/WalletConnectJWT/JWTSigning.swift
diff --git a/Sources/WalletConnectKMS/Crypto/CryptoKitWrapper/SigningKeyCryptoKit.swift b/Sources/WalletConnectKMS/Crypto/CryptoKitWrapper/SigningKeyCryptoKit.swift
index f02cc72cb..527463fed 100644
--- a/Sources/WalletConnectKMS/Crypto/CryptoKitWrapper/SigningKeyCryptoKit.swift
+++ b/Sources/WalletConnectKMS/Crypto/CryptoKitWrapper/SigningKeyCryptoKit.swift
@@ -79,4 +79,10 @@ public struct SigningPrivateKey: GenericPasswordConvertible, Equatable {
public func signature(_ data: Data) throws -> Data {
return try key.signature(for: data)
}
+
+ public var DIDKey: String {
+ return ED25519DIDKeyFactory().make(
+ pubKey: publicKey.rawRepresentation, prefix: true
+ )
+ }
}
diff --git a/Sources/WalletConnectRelay/ClientAuth/SocketAuthenticator.swift b/Sources/WalletConnectRelay/ClientAuth/SocketAuthenticator.swift
index 6858dcad5..1fbf12bac 100644
--- a/Sources/WalletConnectRelay/ClientAuth/SocketAuthenticator.swift
+++ b/Sources/WalletConnectRelay/ClientAuth/SocketAuthenticator.swift
@@ -17,25 +17,20 @@ struct SocketAuthenticator: SocketAuthenticating {
func createAuthToken() throws -> String {
let clientIdKeyPair = try clientIdStorage.getOrCreateKeyPair()
- let subject = generateSubject()
- return try createAndSignJWT(subject: subject, keyPair: clientIdKeyPair)
+ return try createAndSignJWT(keyPair: clientIdKeyPair)
}
- private func createAndSignJWT(subject: String, keyPair: SigningPrivateKey) throws -> String {
- let issuer = didKeyFactory.make(pubKey: keyPair.publicKey.rawRepresentation, prefix: true)
- let now = Int(Date().timeIntervalSince1970)
- let claims = JWT.Claims(iss: issuer, sub: subject, aud: getAudience(), iat: now, exp: getExpiry())
- var jwt = JWT(claims: claims)
- try jwt.sign(using: EdDSASigner(keyPair))
- return try jwt.encoded()
- }
-
- private func generateSubject() -> String {
- return Data.randomBytes(count: 32).toHexString()
+ private func createAndSignJWT(keyPair: SigningPrivateKey) throws -> String {
+ return try JWTFactory().createAndSignJWT(
+ keyPair: keyPair,
+ sub: getSubject(),
+ aud: getAudience(),
+ exp: getExpiry(),
+ pkh: nil
+ )
}
private func getExpiry() -> Int {
-
var components = DateComponents()
components.setValue(1, for: .day)
// safe to unwrap as the date must be calculated
@@ -46,4 +41,8 @@ struct SocketAuthenticator: SocketAuthenticating {
private func getAudience() -> String {
return "wss://\(relayHost)"
}
+
+ private func getSubject() -> String {
+ return Data.randomBytes(count: 32).toHexString()
+ }
}
diff --git a/Sources/WalletConnectRelay/RelayImports.swift b/Sources/WalletConnectRelay/RelayImports.swift
index 24d4a8cab..5c5e68a01 100644
--- a/Sources/WalletConnectRelay/RelayImports.swift
+++ b/Sources/WalletConnectRelay/RelayImports.swift
@@ -1,3 +1,3 @@
#if !CocoaPods
-@_exported import WalletConnectKMS
+@_exported import WalletConnectJWT
#endif
diff --git a/Sources/Auth/Services/Signer/Ethereum/EIP1271/EIP1271Verifier.swift b/Sources/WalletConnectSigner/Signer/Ethereum/EIP1271/EIP1271Verifier.swift
similarity index 100%
rename from Sources/Auth/Services/Signer/Ethereum/EIP1271/EIP1271Verifier.swift
rename to Sources/WalletConnectSigner/Signer/Ethereum/EIP1271/EIP1271Verifier.swift
diff --git a/Sources/Auth/Services/Signer/Ethereum/EIP1271/RPCService.swift b/Sources/WalletConnectSigner/Signer/Ethereum/EIP1271/RPCService.swift
similarity index 100%
rename from Sources/Auth/Services/Signer/Ethereum/EIP1271/RPCService.swift
rename to Sources/WalletConnectSigner/Signer/Ethereum/EIP1271/RPCService.swift
diff --git a/Sources/Auth/Services/Signer/Ethereum/EIP1271/ValidSignatureMethod.swift b/Sources/WalletConnectSigner/Signer/Ethereum/EIP1271/ValidSignatureMethod.swift
similarity index 100%
rename from Sources/Auth/Services/Signer/Ethereum/EIP1271/ValidSignatureMethod.swift
rename to Sources/WalletConnectSigner/Signer/Ethereum/EIP1271/ValidSignatureMethod.swift
diff --git a/Sources/Auth/Services/Signer/Ethereum/EIP191/EIP191Verifier.swift b/Sources/WalletConnectSigner/Signer/Ethereum/EIP191/EIP191Verifier.swift
similarity index 100%
rename from Sources/Auth/Services/Signer/Ethereum/EIP191/EIP191Verifier.swift
rename to Sources/WalletConnectSigner/Signer/Ethereum/EIP191/EIP191Verifier.swift
diff --git a/Sources/Auth/Services/Signer/Ethereum/EthereumSignature.swift b/Sources/WalletConnectSigner/Signer/EthereumSignature.swift
similarity index 100%
rename from Sources/Auth/Services/Signer/Ethereum/EthereumSignature.swift
rename to Sources/WalletConnectSigner/Signer/EthereumSignature.swift
diff --git a/Sources/Auth/Services/Signer/Ethereum/EthereumSigner.swift b/Sources/WalletConnectSigner/Signer/EthereumSigner.swift
similarity index 100%
rename from Sources/Auth/Services/Signer/Ethereum/EthereumSigner.swift
rename to Sources/WalletConnectSigner/Signer/EthereumSigner.swift
diff --git a/Sources/Auth/Services/Signer/MessageSigner.swift b/Sources/WalletConnectSigner/Signer/MessageSigner.swift
similarity index 76%
rename from Sources/Auth/Services/Signer/MessageSigner.swift
rename to Sources/WalletConnectSigner/Signer/MessageSigner.swift
index 7af8792a2..b4eccf811 100644
--- a/Sources/Auth/Services/Signer/MessageSigner.swift
+++ b/Sources/WalletConnectSigner/Signer/MessageSigner.swift
@@ -9,16 +9,20 @@ public protocol MessageSignatureVerifying {
}
public protocol MessageSigning {
- func sign(payload: AuthPayload,
- address: String,
+ func sign(message: String,
+ privateKey: Data,
+ type: CacaoSignatureType
+ ) throws -> CacaoSignature
+
+ func sign(payload: CacaoPayload,
privateKey: Data,
type: CacaoSignatureType
) throws -> CacaoSignature
}
-public typealias AuthMessageSigner = MessageSignatureVerifying & MessageSigning
+public typealias CacaoMessageSigner = MessageSignatureVerifying & MessageSigning
-struct MessageSigner: AuthMessageSigner {
+struct MessageSigner: CacaoMessageSigner {
enum Errors: Error {
case utf8EncodingFailed
@@ -27,22 +31,28 @@ struct MessageSigner: AuthMessageSigner {
private let signer: EthereumSigner
private let eip191Verifier: EIP191Verifier
private let eip1271Verifier: EIP1271Verifier
- private let messageFormatter: SIWEMessageFormatting
+ private let messageFormatter: SIWECacaoFormatting
- init(signer: EthereumSigner, eip191Verifier: EIP191Verifier, eip1271Verifier: EIP1271Verifier, messageFormatter: SIWEMessageFormatting) {
+ init(signer: EthereumSigner, eip191Verifier: EIP191Verifier, eip1271Verifier: EIP1271Verifier, messageFormatter: SIWECacaoFormatting) {
self.signer = signer
self.eip191Verifier = eip191Verifier
self.eip1271Verifier = eip1271Verifier
self.messageFormatter = messageFormatter
}
- func sign(payload: AuthPayload,
- address: String,
+ func sign(payload: CacaoPayload,
privateKey: Data,
type: CacaoSignatureType
) throws -> CacaoSignature {
- let message = try messageFormatter.formatMessage(from: payload, address: address)
+ let message = try messageFormatter.formatMessage(from: payload)
+ return try sign(message: message, privateKey: privateKey, type: type)
+ }
+
+ func sign(message: String,
+ privateKey: Data,
+ type: CacaoSignatureType
+ ) throws -> CacaoSignature {
guard let messageData = message.data(using: .utf8)else {
throw Errors.utf8EncodingFailed
diff --git a/Sources/Auth/Services/Signer/MessageSignerFactory.swift b/Sources/WalletConnectSigner/Signer/MessageSignerFactory.swift
similarity index 80%
rename from Sources/Auth/Services/Signer/MessageSignerFactory.swift
rename to Sources/WalletConnectSigner/Signer/MessageSignerFactory.swift
index 88d56d374..ef4f53879 100644
--- a/Sources/Auth/Services/Signer/MessageSignerFactory.swift
+++ b/Sources/WalletConnectSigner/Signer/MessageSignerFactory.swift
@@ -8,11 +8,11 @@ public struct MessageSignerFactory {
self.signerFactory = signerFactory
}
- public func create() -> AuthMessageSigner {
+ public func create() -> CacaoMessageSigner {
return create(projectId: Networking.projectId)
}
- func create(projectId: String) -> AuthMessageSigner {
+ public func create(projectId: String) -> CacaoMessageSigner {
return MessageSigner(
signer: signerFactory.createEthereumSigner(),
eip191Verifier: EIP191Verifier(signer: signerFactory.createEthereumSigner()),
@@ -21,7 +21,7 @@ public struct MessageSignerFactory {
httpClient: HTTPNetworkClient(host: "rpc.walletconnect.com"),
signer: signerFactory.createEthereumSigner()
),
- messageFormatter: SIWEMessageFormatter()
+ messageFormatter: SIWECacaoFormatter()
)
}
}
diff --git a/Sources/Auth/Services/Signer/SignerFactory.swift b/Sources/WalletConnectSigner/Signer/SignerFactory.swift
similarity index 100%
rename from Sources/Auth/Services/Signer/SignerFactory.swift
rename to Sources/WalletConnectSigner/Signer/SignerFactory.swift
diff --git a/Sources/WalletConnectSigner/SignerImports.swift b/Sources/WalletConnectSigner/SignerImports.swift
new file mode 100644
index 000000000..23c1738ef
--- /dev/null
+++ b/Sources/WalletConnectSigner/SignerImports.swift
@@ -0,0 +1,3 @@
+#if !CocoaPods
+@_exported import WalletConnectNetworking
+#endif
diff --git a/Sources/WalletConnectUtils/Cacao/Cacao.swift b/Sources/WalletConnectUtils/Cacao/Cacao.swift
new file mode 100644
index 000000000..5c938594b
--- /dev/null
+++ b/Sources/WalletConnectUtils/Cacao/Cacao.swift
@@ -0,0 +1,16 @@
+import Foundation
+
+/// CAIP-74 Cacao object
+///
+/// specs at: https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-74.md
+public struct Cacao: Codable, Equatable {
+ public let h: CacaoHeader
+ public let p: CacaoPayload
+ public let s: CacaoSignature
+
+ public init(h: CacaoHeader, p: CacaoPayload, s: CacaoSignature) {
+ self.h = h
+ self.p = p
+ self.s = s
+ }
+}
diff --git a/Sources/WalletConnectUtils/Cacao/CacaoHeader.swift b/Sources/WalletConnectUtils/Cacao/CacaoHeader.swift
new file mode 100644
index 000000000..897c314d4
--- /dev/null
+++ b/Sources/WalletConnectUtils/Cacao/CacaoHeader.swift
@@ -0,0 +1,9 @@
+import Foundation
+
+public struct CacaoHeader: Codable, Equatable {
+ public let t: String
+
+ public init(t: String) {
+ self.t = t
+ }
+}
diff --git a/Sources/WalletConnectUtils/Cacao/CacaoPayload.swift b/Sources/WalletConnectUtils/Cacao/CacaoPayload.swift
new file mode 100644
index 000000000..3711f3ef0
--- /dev/null
+++ b/Sources/WalletConnectUtils/Cacao/CacaoPayload.swift
@@ -0,0 +1,41 @@
+import Foundation
+
+public struct CacaoPayload: Codable, Equatable {
+ public let iss: String
+ public let domain: String
+ public let aud: String
+ public let version: String
+ public let nonce: String
+ public let iat: String
+ public let nbf: String?
+ public let exp: String?
+ public let statement: String?
+ public let requestId: String?
+ public let resources: [String]?
+
+ public init(
+ iss: String,
+ domain: String,
+ aud: String,
+ version: String,
+ nonce: String,
+ iat: String,
+ nbf: String?,
+ exp: String?,
+ statement: String?,
+ requestId: String?,
+ resources: [String]?
+ ) {
+ self.iss = iss
+ self.domain = domain
+ self.aud = aud
+ self.version = version
+ self.nonce = nonce
+ self.iat = iat
+ self.nbf = nbf
+ self.exp = exp
+ self.statement = statement
+ self.requestId = requestId
+ self.resources = resources
+ }
+}
diff --git a/Sources/Auth/Types/Cacao/CacaoSignature.swift b/Sources/WalletConnectUtils/Cacao/CacaoSignature.swift
similarity index 76%
rename from Sources/Auth/Types/Cacao/CacaoSignature.swift
rename to Sources/WalletConnectUtils/Cacao/CacaoSignature.swift
index 7137f9dea..d272f0279 100644
--- a/Sources/Auth/Types/Cacao/CacaoSignature.swift
+++ b/Sources/WalletConnectUtils/Cacao/CacaoSignature.swift
@@ -6,9 +6,9 @@ public enum CacaoSignatureType: String, Codable {
}
public struct CacaoSignature: Codable, Equatable {
- let t: CacaoSignatureType
- let s: String
- let m: String?
+ public let t: CacaoSignatureType
+ public let s: String
+ public let m: String?
public init(t: CacaoSignatureType, s: String, m: String? = nil) {
self.t = t
diff --git a/Sources/WalletConnectUtils/Cacao/IATProvider.swift b/Sources/WalletConnectUtils/Cacao/IATProvider.swift
new file mode 100644
index 000000000..22aefa45d
--- /dev/null
+++ b/Sources/WalletConnectUtils/Cacao/IATProvider.swift
@@ -0,0 +1,14 @@
+import Foundation
+
+public protocol IATProvider {
+ var iat: String { get }
+}
+
+public struct DefaultIATProvider: IATProvider {
+
+ public init() { }
+
+ public var iat: String {
+ return ISO8601DateFormatter().string(from: Date())
+ }
+}
diff --git a/Sources/WalletConnectRelay/ClientAuth/Base58.swift b/Sources/WalletConnectUtils/DID/Base58.swift
similarity index 100%
rename from Sources/WalletConnectRelay/ClientAuth/Base58.swift
rename to Sources/WalletConnectUtils/DID/Base58.swift
diff --git a/Sources/WalletConnectRelay/ClientAuth/DIDKeyFactory.swift b/Sources/WalletConnectUtils/DID/DIDKeyFactory.swift
similarity index 83%
rename from Sources/WalletConnectRelay/ClientAuth/DIDKeyFactory.swift
rename to Sources/WalletConnectUtils/DID/DIDKeyFactory.swift
index 2a062c090..1d3bb7066 100644
--- a/Sources/WalletConnectRelay/ClientAuth/DIDKeyFactory.swift
+++ b/Sources/WalletConnectUtils/DID/DIDKeyFactory.swift
@@ -1,19 +1,21 @@
import Foundation
-protocol DIDKeyFactory {
+public protocol DIDKeyFactory {
func make(pubKey: Data, prefix: Bool) -> String
}
/// A DID Method for Static Cryptographic Keys
/// did-key-format := did:key:MULTIBASE(base58-btc, MULTICODEC(public-key-type, raw-public-key-bytes))
-struct ED25519DIDKeyFactory: DIDKeyFactory {
+public struct ED25519DIDKeyFactory: DIDKeyFactory {
private let DID_DELIMITER = ":"
private let DID_PREFIX = "did"
private let DID_METHOD = "key"
private let MULTICODEC_ED25519_HEADER: [UInt8] = [0xed, 0x01]
private let MULTICODEC_ED25519_BASE = "z"
- func make(pubKey: Data, prefix: Bool) -> String {
+ public init() { }
+
+ public func make(pubKey: Data, prefix: Bool) -> String {
let multibase = multibase(pubKey: pubKey)
guard prefix else { return multibase }
diff --git a/Sources/Auth/Services/Signer/DIDPKH.swift b/Sources/WalletConnectUtils/DID/DIDPKH.swift
similarity index 65%
rename from Sources/Auth/Services/Signer/DIDPKH.swift
rename to Sources/WalletConnectUtils/DID/DIDPKH.swift
index 1475a6530..065569cc2 100644
--- a/Sources/Auth/Services/Signer/DIDPKH.swift
+++ b/Sources/WalletConnectUtils/DID/DIDPKH.swift
@@ -1,17 +1,18 @@
import Foundation
-struct DIDPKH {
- static let didPrefix: String = "did:pkh"
+public struct DIDPKH {
+
+ private static let didPrefix: String = "did:pkh"
enum Errors: Error {
case invalidDIDPKH
case invalidAccount
}
- let account: Account
- let iss: String
+ public let account: Account
+ public let iss: String
- init(iss: String) throws {
+ public init(iss: String) throws {
guard iss.starts(with: DIDPKH.didPrefix)
else { throw Errors.invalidDIDPKH }
@@ -25,8 +26,15 @@ struct DIDPKH {
self.account = account
}
- init(account: Account) {
+ public init(account: Account) {
self.iss = "\(DIDPKH.didPrefix):\(account.absoluteString)"
self.account = account
}
}
+
+extension Account {
+
+ public var iss: String {
+ return DIDPKH(account: self).iss
+ }
+}
diff --git a/Sources/WalletConnectUtils/SIWE/SIWECacaoFormatter.swift b/Sources/WalletConnectUtils/SIWE/SIWECacaoFormatter.swift
new file mode 100644
index 000000000..c361677a3
--- /dev/null
+++ b/Sources/WalletConnectUtils/SIWE/SIWECacaoFormatter.swift
@@ -0,0 +1,30 @@
+import Foundation
+
+public protocol SIWECacaoFormatting {
+ func formatMessage(from payload: CacaoPayload) throws -> String
+}
+
+public struct SIWECacaoFormatter: SIWECacaoFormatting {
+
+ public init() { }
+
+ public func formatMessage(from payload: CacaoPayload) throws -> String {
+ let address = try DIDPKH(iss: payload.iss).account.address
+ let iss = try DIDPKH(iss: payload.iss)
+ let message = SIWEMessage(
+ domain: payload.domain,
+ uri: payload.aud,
+ address: address,
+ version: payload.version,
+ nonce: payload.nonce,
+ chainId: iss.account.reference,
+ iat: payload.iat,
+ nbf: payload.nbf,
+ exp: payload.exp,
+ statement: payload.statement,
+ requestId: payload.requestId,
+ resources: payload.resources
+ )
+ return message.formatted
+ }
+}
diff --git a/Sources/WalletConnectUtils/SIWE/SIWEMessage.swift b/Sources/WalletConnectUtils/SIWE/SIWEMessage.swift
new file mode 100644
index 000000000..0a7fc18df
--- /dev/null
+++ b/Sources/WalletConnectUtils/SIWE/SIWEMessage.swift
@@ -0,0 +1,72 @@
+import Foundation
+
+public struct SIWEMessage: Equatable {
+ public let domain: String
+ public let uri: String // aud
+ public let address: String
+ public let version: String
+ public let nonce: String
+ public let chainId: String
+ public let iat: String
+ public let nbf: String?
+ public let exp: String?
+ public let statement: String?
+ public let requestId: String?
+ public let resources: [String]?
+
+ public init(domain: String, uri: String, address: String, version: String, nonce: String, chainId: String, iat: String, nbf: String?, exp: String?, statement: String?, requestId: String?, resources: [String]?) {
+ self.domain = domain
+ self.uri = uri
+ self.address = address
+ self.version = version
+ self.nonce = nonce
+ self.chainId = chainId
+ self.iat = iat
+ self.nbf = nbf
+ self.exp = exp
+ self.statement = statement
+ self.requestId = requestId
+ self.resources = resources
+ }
+
+ public var formatted: String {
+ return """
+ \(domain) wants you to sign in with your Ethereum account:
+ \(address)
+ \(statementLine)
+ URI: \(uri)
+ Version: \(version)
+ Chain ID: \(chainId)
+ Nonce: \(nonce)
+ Issued At: \(iat)\(expLine)\(nbfLine)\(requestIdLine)\(resourcesSection)
+ """
+ }
+}
+
+private extension SIWEMessage {
+
+ var expLine: String {
+ guard let exp = exp else { return "" }
+ return "\nExpiration Time: \(exp)"
+ }
+
+ var statementLine: String {
+ guard let statement = statement else { return "" }
+ return "\n\(statement)\n"
+ }
+
+ var nbfLine: String {
+ guard let nbf = nbf else { return "" }
+ return "\nNot Before: \(nbf)"
+ }
+
+ var requestIdLine: String {
+ guard let requestId = requestId else { return "" }
+ return "\nRequest ID: \(requestId)"
+ }
+
+ var resourcesSection: String {
+ guard let resources = resources else { return "" }
+ return resources.reduce("\nResources:") { $0 + "\n- \($1)" }
+ }
+}
diff --git a/Sources/Web3Wallet/Web3WalletClient.swift b/Sources/Web3Wallet/Web3WalletClient.swift
index 43c709b95..d9397de4e 100644
--- a/Sources/Web3Wallet/Web3WalletClient.swift
+++ b/Sources/Web3Wallet/Web3WalletClient.swift
@@ -169,7 +169,7 @@ public class Web3WalletClient {
/// Query pending authentication requests
/// - Returns: Pending authentication requests
- public func getPendingRequests(account: Account) throws -> [AuthRequest] {
- try authClient.getPendingRequests(account: account)
+ public func getPendingRequests() throws -> [AuthRequest] {
+ try authClient.getPendingRequests()
}
}
diff --git a/Tests/AuthTests/AppRespondSubscriberTests.swift b/Tests/AuthTests/AppRespondSubscriberTests.swift
index 72f3f4f51..473ea5d1d 100644
--- a/Tests/AuthTests/AppRespondSubscriberTests.swift
+++ b/Tests/AuthTests/AppRespondSubscriberTests.swift
@@ -11,16 +11,16 @@ class AppRespondSubscriberTests: XCTestCase {
var networkingInteractor: NetworkingInteractorMock!
var sut: AppRespondSubscriber!
- var messageFormatter: SIWEMessageFormatter!
+ var messageFormatter: SIWECacaoFormatter!
var rpcHistory: RPCHistory!
let defaultTimeout: TimeInterval = 0.01
- var messageSigner: AuthMessageSigner!
+ var messageSigner: CacaoMessageSigner!
var pairingStorage: WCPairingStorageMock!
var pairingRegisterer: PairingRegistererMock!
override func setUp() {
networkingInteractor = NetworkingInteractorMock()
- messageFormatter = SIWEMessageFormatter()
+ messageFormatter = SIWECacaoFormatter()
messageSigner = MessageSignerMock()
rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: RuntimeKeyValueStorage())
pairingStorage = WCPairingStorageMock()
@@ -58,9 +58,8 @@ class AppRespondSubscriberTests: XCTestCase {
}
// subscribe on compromised cacao
- let account = Account(chainIdentifier: "eip155:1", address: "0x724d0D2DaD3fbB0C168f947B87Fa5DBe36F1A8bf")!
let cacaoHeader = CacaoHeader(t: "eip4361")
- let cacaoPayload = CacaoPayload(params: compromissedParams.payloadParams, didpkh: DIDPKH(account: account))
+ let cacaoPayload = try! compromissedParams.payloadParams.cacaoPayload(address: "0x724d0D2DaD3fbB0C168f947B87Fa5DBe36F1A8bf")
let cacaoSignature = CacaoSignature(t: .eip191, s: "")
let cacao = Cacao(h: cacaoHeader, p: cacaoPayload, s: cacaoSignature)
diff --git a/Tests/AuthTests/Mocks/SIWEMessageFormatterMock.swift b/Tests/AuthTests/Mocks/SIWEMessageFormatterMock.swift
index 64adc7c96..0796eb6ce 100644
--- a/Tests/AuthTests/Mocks/SIWEMessageFormatterMock.swift
+++ b/Tests/AuthTests/Mocks/SIWEMessageFormatterMock.swift
@@ -1,13 +1,9 @@
import Foundation
@testable import Auth
-class SIWEMessageFormatterMock: SIWEMessageFormatting {
+class SIWEMessageFormatterMock: SIWECacaoFormatting {
var formattedMessage: String!
- func formatMessage(from authPayload: AuthPayload, address: String) throws -> String {
- return formattedMessage
- }
-
func formatMessage(from payload: CacaoPayload) throws -> String {
return formattedMessage
}
diff --git a/Tests/AuthTests/SIWEMessageFormatterTests.swift b/Tests/AuthTests/SIWEMessageFormatterTests.swift
index 859b2cf41..129fe4c52 100644
--- a/Tests/AuthTests/SIWEMessageFormatterTests.swift
+++ b/Tests/AuthTests/SIWEMessageFormatterTests.swift
@@ -3,11 +3,11 @@ import Foundation
import XCTest
class SIWEMessageFormatterTests: XCTestCase {
- var sut: SIWEMessageFormatter!
+ var sut: SIWECacaoFormatter!
let address = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
override func setUp() {
- sut = SIWEMessageFormatter()
+ sut = SIWECacaoFormatter()
}
func testFormatMessage() throws {
@@ -27,7 +27,7 @@ class SIWEMessageFormatterTests: XCTestCase {
- ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/
- https://example.com/my-web2-claim.json
"""
- let message = try sut.formatMessage(from: AuthPayload.stub(), address: address)
+ let message = try sut.formatMessage(from: AuthPayload.stub().cacaoPayload(address: address))
XCTAssertEqual(message, expectedMessage)
}
@@ -48,8 +48,9 @@ class SIWEMessageFormatterTests: XCTestCase {
"""
let message = try sut.formatMessage(
from: AuthPayload.stub(
- requestParams: RequestParams.stub(statement: nil)),
- address: address)
+ requestParams: RequestParams.stub(statement: nil)
+ ).cacaoPayload(address: address)
+ )
XCTAssertEqual(message, expectedMessage)
}
@@ -69,8 +70,8 @@ class SIWEMessageFormatterTests: XCTestCase {
"""
let message = try sut.formatMessage(
from: AuthPayload.stub(
- requestParams: RequestParams.stub(resources: nil)),
- address: address)
+ requestParams: RequestParams.stub(resources: nil)).cacaoPayload(address: address)
+ )
XCTAssertEqual(message, expectedMessage)
}
@@ -88,9 +89,8 @@ class SIWEMessageFormatterTests: XCTestCase {
"""
let message = try sut.formatMessage(
from: AuthPayload.stub(
- requestParams: RequestParams.stub(statement: nil,
- resources: nil)),
- address: address)
+ requestParams: RequestParams.stub(statement: nil, resources: nil)).cacaoPayload(address: address)
+ )
XCTAssertEqual(message, expectedMessage)
}
}
diff --git a/Tests/AuthTests/Stubs/MessageSignerMock.swift b/Tests/AuthTests/Stubs/MessageSignerMock.swift
index 4275cd1b9..869febeeb 100644
--- a/Tests/AuthTests/Stubs/MessageSignerMock.swift
+++ b/Tests/AuthTests/Stubs/MessageSignerMock.swift
@@ -1,7 +1,10 @@
import Foundation
import Auth
-struct MessageSignerMock: AuthMessageSigner {
+struct MessageSignerMock: CacaoMessageSigner {
+ func sign(message: String, privateKey: Data, type: WalletConnectUtils.CacaoSignatureType) throws -> WalletConnectUtils.CacaoSignature {
+ return CacaoSignature(t: .eip191, s: "")
+ }
func verify(signature: CacaoSignature,
message: String,
@@ -11,8 +14,7 @@ struct MessageSignerMock: AuthMessageSigner {
}
- func sign(payload: AuthPayload,
- address: String,
+ func sign(payload: CacaoPayload,
privateKey: Data,
type: CacaoSignatureType
) throws -> CacaoSignature {
diff --git a/Tests/RelayerTests/AuthTests/EdDSASignerTests.swift b/Tests/RelayerTests/AuthTests/EdDSASignerTests.swift
index 2e5643785..913a2bed1 100644
--- a/Tests/RelayerTests/AuthTests/EdDSASignerTests.swift
+++ b/Tests/RelayerTests/AuthTests/EdDSASignerTests.swift
@@ -2,6 +2,7 @@ import Foundation
import XCTest
import WalletConnectKMS
@testable import WalletConnectRelay
+@testable import WalletConnectJWT
final class EdDSASignerTests: XCTestCase {
var sut: EdDSASigner!
diff --git a/Tests/RelayerTests/AuthTests/JWTTests.swift b/Tests/RelayerTests/AuthTests/JWTTests.swift
index 8f20cd961..d2eb4d8b6 100644
--- a/Tests/RelayerTests/AuthTests/JWTTests.swift
+++ b/Tests/RelayerTests/AuthTests/JWTTests.swift
@@ -1,6 +1,7 @@
import Foundation
import XCTest
@testable import WalletConnectRelay
+@testable import WalletConnectJWT
final class JWTTests: XCTestCase {
let expectedJWT = "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NTY5MTAwOTcsImV4cCI6MTY1Njk5NjQ5NywiaXNzIjoiZGlkOmtleTp6Nk1rb2RIWnduZVZSU2h0YUxmOEpLWWt4cERHcDF2R1pucEdtZEJwWDhNMmV4eEgiLCJzdWIiOiJjNDc5ZmU1ZGM0NjRlNzcxZTc4YjE5M2QyMzlhNjViNThkMjc4Y2FkMWMzNGJmYjBiNTcxNmU1YmI1MTQ5MjhlIiwiYXVkIjoid3NzOi8vcmVsYXkud2FsbGV0Y29ubmVjdC5jb20ifQ.0JkxOM-FV21U7Hk-xycargj_qNRaYV2H5HYtE4GzAeVQYiKWj7YySY5AdSqtCgGzX4Gt98XWXn2kSr9rE1qvCA"
@@ -27,6 +28,6 @@ extension JWT.Claims {
let aud = "wss://relay.walletconnect.com"
let expDate = Calendar.current.date(byAdding: components, to: iatDate)!
let exp = Int(expDate.timeIntervalSince1970)
- return JWT.Claims(iss: iss, sub: sub, aud: aud, iat: iat, exp: exp)
+ return JWT.Claims(iss: iss, sub: sub, aud: aud, iat: iat, exp: exp, pkh: nil)
}
}
diff --git a/Tests/RelayerTests/Mocks/EdDSASignerMock.swift b/Tests/RelayerTests/Mocks/EdDSASignerMock.swift
index b7ca50718..dc52ed565 100644
--- a/Tests/RelayerTests/Mocks/EdDSASignerMock.swift
+++ b/Tests/RelayerTests/Mocks/EdDSASignerMock.swift
@@ -1,4 +1,5 @@
import Foundation
+@testable import WalletConnectJWT
@testable import WalletConnectRelay
class EdDSASignerMock: JWTSigning {
diff --git a/Tests/Web3WalletTests/Mocks/AuthClientMock.swift b/Tests/Web3WalletTests/Mocks/AuthClientMock.swift
index 3b1a3a68d..4f3e50c39 100644
--- a/Tests/Web3WalletTests/Mocks/AuthClientMock.swift
+++ b/Tests/Web3WalletTests/Mocks/AuthClientMock.swift
@@ -42,7 +42,7 @@ final class AuthClientMock: AuthClientProtocol {
rejectCalled = true
}
- func getPendingRequests(account: WalletConnectUtils.Account) throws -> [AuthRequest] {
+ func getPendingRequests() throws -> [AuthRequest] {
return [authRequest]
}
}
diff --git a/Tests/Web3WalletTests/Web3WalletTests.swift b/Tests/Web3WalletTests/Web3WalletTests.swift
index 7ce173cc5..8962e88a1 100644
--- a/Tests/Web3WalletTests/Web3WalletTests.swift
+++ b/Tests/Web3WalletTests/Web3WalletTests.swift
@@ -186,8 +186,7 @@ final class Web3WalletTests: XCTestCase {
}
func testAuthPendingRequestsCalledAndNotEmpty() async {
- let account = Account("eip155:56:0xe5EeF1368781911d265fDB6946613dA61915a501")!
- let pendingRequests = try! web3WalletClient.getPendingRequests(account: account)
+ let pendingRequests = try! web3WalletClient.getPendingRequests()
XCTAssertEqual(1, pendingRequests.count)
}
}