diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8ff4a9a95..a71b4ce7e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,7 @@ jobs: - name: Setup Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '13.4.1' + xcode-version: 14.1 - uses: actions/cache@v2 with: diff --git a/.github/workflows/set-user-agent.yml b/.github/workflows/set-user-agent.yml index 1919088d5..a08089f8a 100644 --- a/.github/workflows/set-user-agent.yml +++ b/.github/workflows/set-user-agent.yml @@ -17,6 +17,9 @@ jobs: runs-on: macos-latest steps: - uses: actions/checkout@v2 + with: + token: ${{ secrets.GH_TOKEN }} + - name: Set User Agent shell: bash run: ./.github/scripts/set-user-agent.sh diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectVerify.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectVerify.xcscheme new file mode 100644 index 000000000..b5ef0e1c9 --- /dev/null +++ b/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectVerify.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/DApp/SceneDelegate.swift b/Example/DApp/SceneDelegate.swift index afeca0dce..c1ea35937 100644 --- a/Example/DApp/SceneDelegate.swift +++ b/Example/DApp/SceneDelegate.swift @@ -1,16 +1,8 @@ import UIKit -import Starscream +import Auth import WalletConnectRelay import WalletConnectNetworking -extension WebSocket: WebSocketConnecting { } - -struct SocketFactory: WebSocketFactory { - func create(with url: URL) -> WebSocketConnecting { - return WebSocket(url: url) - } -} - class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? @@ -19,7 +11,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { private let authCoordinator = AuthCoordinator() func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { - Networking.configure(projectId: InputConfig.projectId, socketFactory: SocketFactory()) + Networking.configure(projectId: InputConfig.projectId, socketFactory: DefaultSocketFactory()) + Auth.configure(signerFactory: DefaultSignerFactory()) setupWindow(scene: scene) } diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index fc6357d2b..6e5062005 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -31,6 +31,7 @@ 84494388278D9C1B00CC26BB /* UIAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84494387278D9C1B00CC26BB /* UIAlertController.swift */; }; 8460DCFC274F98A10081F94C /* RequestViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8460DCFB274F98A10081F94C /* RequestViewController.swift */; }; 847CF3AF28E3141700F1D760 /* WalletConnectPush in Frameworks */ = {isa = PBXBuildFile; productRef = 847CF3AE28E3141700F1D760 /* WalletConnectPush */; settings = {ATTRIBUTES = (Required, ); }; }; + 849D7A90292665D3006A2BD4 /* WalletConnectVerify in Frameworks */ = {isa = PBXBuildFile; productRef = 849D7A8F292665D3006A2BD4 /* WalletConnectVerify */; }; 84AA01DB28CF0CD7005D48D8 /* XCTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84AA01DA28CF0CD7005D48D8 /* XCTest.swift */; }; 84CE641F27981DED00142511 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE641E27981DED00142511 /* AppDelegate.swift */; }; 84CE642127981DED00142511 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE642027981DED00142511 /* SceneDelegate.swift */; }; @@ -57,6 +58,11 @@ A51AC0D928E436A3001BACF9 /* InputConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51AC0D828E436A3001BACF9 /* InputConfig.swift */; }; A51AC0DD28E43727001BACF9 /* InputConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51AC0DB28E436E6001BACF9 /* InputConfig.swift */; }; A51AC0DF28E4379F001BACF9 /* InputConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51AC0DE28E4379F001BACF9 /* InputConfig.swift */; }; + A541959E2934BFEF0035AD19 /* CacaoSignerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A541959A2934BFEF0035AD19 /* CacaoSignerTests.swift */; }; + A541959F2934BFEF0035AD19 /* SignerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A541959B2934BFEF0035AD19 /* SignerTests.swift */; }; + A54195A02934BFEF0035AD19 /* EIP1271VerifierTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A541959C2934BFEF0035AD19 /* EIP1271VerifierTests.swift */; }; + A54195A12934BFEF0035AD19 /* EIP191VerifierTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A541959D2934BFEF0035AD19 /* EIP191VerifierTests.swift */; }; + A54195A52934E83F0035AD19 /* Web3 in Frameworks */ = {isa = PBXBuildFile; productRef = A54195A42934E83F0035AD19 /* Web3 */; }; A5434023291E6A270068F706 /* SolanaSwift in Frameworks */ = {isa = PBXBuildFile; productRef = A5434022291E6A270068F706 /* SolanaSwift */; }; A55CAAB028B92AFF00844382 /* ScanModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55CAAAB28B92AFF00844382 /* ScanModule.swift */; }; A55CAAB128B92AFF00844382 /* ScanPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55CAAAC28B92AFF00844382 /* ScanPresenter.swift */; }; @@ -85,15 +91,14 @@ A5629AE42876E6D200094373 /* ThreadViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5629AE32876E6D200094373 /* ThreadViewModel.swift */; }; A5629AE828772A0100094373 /* InviteViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5629AE728772A0100094373 /* InviteViewModel.swift */; }; A5629AEA2877F2D600094373 /* WalletConnectChat in Frameworks */ = {isa = PBXBuildFile; productRef = A5629AE92877F2D600094373 /* WalletConnectChat */; }; - A5629AF02877F73000094373 /* SocketFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5629AEF2877F73000094373 /* SocketFactory.swift */; }; A5629AF22877F75100094373 /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = A5629AF12877F75100094373 /* Starscream */; }; A578FA322873036400AA7720 /* InputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A578FA312873036400AA7720 /* InputView.swift */; }; A578FA35287304A300AA7720 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = A578FA34287304A300AA7720 /* Color.swift */; }; A578FA372873D8EE00AA7720 /* UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = A578FA362873D8EE00AA7720 /* UIColor.swift */; }; A578FA392873FCE000AA7720 /* ChatScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A578FA382873FCE000AA7720 /* ChatScrollView.swift */; }; A578FA3D2874002400AA7720 /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = A578FA3C2874002400AA7720 /* View.swift */; }; - A57E71A6291CF76400325797 /* EthereumSigner.swift in Sources */ = {isa = PBXBuildFile; fileRef = A57E71A5291CF76400325797 /* EthereumSigner.swift */; }; - A57E71A8291CF8A500325797 /* SolanaSigner.swift in Sources */ = {isa = PBXBuildFile; fileRef = A57E71A7291CF8A500325797 /* SolanaSigner.swift */; }; + A57E71A6291CF76400325797 /* ETHSigner.swift in Sources */ = {isa = PBXBuildFile; fileRef = A57E71A5291CF76400325797 /* ETHSigner.swift */; }; + A57E71A8291CF8A500325797 /* SOLSigner.swift in Sources */ = {isa = PBXBuildFile; fileRef = A57E71A7291CF8A500325797 /* SOLSigner.swift */; }; A58E7CEB28729F550082D443 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A58E7CEA28729F550082D443 /* AppDelegate.swift */; }; A58E7CED28729F550082D443 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A58E7CEC28729F550082D443 /* SceneDelegate.swift */; }; A58E7CF428729F550082D443 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A58E7CF328729F550082D443 /* Assets.xcassets */; }; @@ -120,6 +125,7 @@ A58E7D432872EE320082D443 /* MessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A58E7D422872EE320082D443 /* MessageView.swift */; }; A58E7D452872EE570082D443 /* ContentMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A58E7D442872EE570082D443 /* ContentMessageView.swift */; }; A58E7D482872EF610082D443 /* MessageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A58E7D472872EF610082D443 /* MessageViewModel.swift */; }; + A59CF4F6292F83D50031A42F /* DefaultSignerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59CF4F5292F83D50031A42F /* DefaultSignerFactory.swift */; }; A59EBEF828B54A2A003EDAAF /* AuthRequestModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59EBEF328B54A2A003EDAAF /* AuthRequestModule.swift */; }; A59EBEF928B54A2A003EDAAF /* AuthRequestPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59EBEF428B54A2A003EDAAF /* AuthRequestPresenter.swift */; }; A59EBEFA28B54A2A003EDAAF /* AuthRequestRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59EBEF528B54A2A003EDAAF /* AuthRequestRouter.swift */; }; @@ -138,6 +144,12 @@ A5A4FC5C283D1F6700BBEC1E /* SessionDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A4FC5B283D1F6700BBEC1E /* SessionDetailViewController.swift */; }; A5A4FC5E283D23CA00BBEC1E /* Array.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A4FC5D283D23CA00BBEC1E /* Array.swift */; }; A5A4FC772840C12C00BBEC1E /* RegressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A4FC762840C12C00BBEC1E /* RegressionTests.swift */; }; + A5A8E47A293A1C9B00FEB97D /* DefaultSocketFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5629AEF2877F73000094373 /* DefaultSocketFactory.swift */; }; + A5A8E47B293A1CFE00FEB97D /* DefaultSocketFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5629AEF2877F73000094373 /* DefaultSocketFactory.swift */; }; + A5A8E47D293A1CFE00FEB97D /* DefaultSocketFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5629AEF2877F73000094373 /* DefaultSocketFactory.swift */; }; + A5A8E47E293A1CFE00FEB97D /* DefaultSignerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59CF4F5292F83D50031A42F /* DefaultSignerFactory.swift */; }; + A5A8E47F293A1D0000FEB97D /* DefaultSocketFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5629AEF2877F73000094373 /* DefaultSocketFactory.swift */; }; + A5A8E480293A1D0000FEB97D /* DefaultSignerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59CF4F5292F83D50031A42F /* DefaultSignerFactory.swift */; }; A5AE354728A1A2AC0059AE8A /* Web3 in Frameworks */ = {isa = PBXBuildFile; productRef = A5AE354628A1A2AC0059AE8A /* Web3 */; }; A5BB7F9F28B69B7100707FC6 /* SignCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5BB7F9E28B69B7100707FC6 /* SignCoordinator.swift */; }; A5BB7FA128B69F3400707FC6 /* AuthCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5BB7FA028B69F3400707FC6 /* AuthCoordinator.swift */; }; @@ -161,6 +173,7 @@ A5C2022B287EB89A007E3188 /* WelcomeInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5C2022A287EB89A007E3188 /* WelcomeInteractor.swift */; }; A5C2022D287EC3F0007E3188 /* RegisterService.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5C2022C287EC3F0007E3188 /* RegisterService.swift */; }; A5C4DD8728A2DE88006A626D /* WalletConnectRouter in Frameworks */ = {isa = PBXBuildFile; productRef = A5C4DD8628A2DE88006A626D /* WalletConnectRouter */; }; + A5C8BE85292FE20B006CC85C /* Web3 in Frameworks */ = {isa = PBXBuildFile; productRef = A5C8BE84292FE20B006CC85C /* Web3 */; }; A5D85226286333D500DAF5C3 /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = A5D85225286333D500DAF5C3 /* Starscream */; }; A5D85228286333E300DAF5C3 /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = A5D85227286333E300DAF5C3 /* Starscream */; }; A5E03DF52864651200888481 /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = A5E03DF42864651200888481 /* Starscream */; }; @@ -170,7 +183,6 @@ A5E03E01286466EA00888481 /* WalletConnectChat in Frameworks */ = {isa = PBXBuildFile; productRef = A5E03E00286466EA00888481 /* WalletConnectChat */; }; A5E03E03286466F400888481 /* ChatTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E03E02286466F400888481 /* ChatTests.swift */; }; A5E03E0D28646AD200888481 /* RelayClientEndToEndTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E03E0C28646AD200888481 /* RelayClientEndToEndTests.swift */; }; - A5E03E0F28646D8A00888481 /* WebSocketFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E03E0E28646D8A00888481 /* WebSocketFactory.swift */; }; A5E03E1128646F8000888481 /* KeychainStorageMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E03E1028646F8000888481 /* KeychainStorageMock.swift */; }; A5E22D1A2840C62A00E36487 /* Engine.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E22D192840C62A00E36487 /* Engine.swift */; }; A5E22D1C2840C85D00E36487 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E22D1B2840C85D00E36487 /* App.swift */; }; @@ -253,6 +265,10 @@ A51AC0D828E436A3001BACF9 /* InputConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputConfig.swift; sourceTree = ""; }; A51AC0DB28E436E6001BACF9 /* InputConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputConfig.swift; sourceTree = ""; }; A51AC0DE28E4379F001BACF9 /* InputConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputConfig.swift; sourceTree = ""; }; + A541959A2934BFEF0035AD19 /* CacaoSignerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CacaoSignerTests.swift; sourceTree = ""; }; + A541959B2934BFEF0035AD19 /* SignerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignerTests.swift; sourceTree = ""; }; + A541959C2934BFEF0035AD19 /* EIP1271VerifierTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EIP1271VerifierTests.swift; sourceTree = ""; }; + A541959D2934BFEF0035AD19 /* EIP191VerifierTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EIP191VerifierTests.swift; sourceTree = ""; }; A55CAAAB28B92AFF00844382 /* ScanModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanModule.swift; sourceTree = ""; }; A55CAAAC28B92AFF00844382 /* ScanPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanPresenter.swift; sourceTree = ""; }; A55CAAAD28B92AFF00844382 /* ScanRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanRouter.swift; sourceTree = ""; }; @@ -279,14 +295,14 @@ A5629ADD2876CC6E00094373 /* InviteListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteListView.swift; sourceTree = ""; }; A5629AE32876E6D200094373 /* ThreadViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadViewModel.swift; sourceTree = ""; }; A5629AE728772A0100094373 /* InviteViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteViewModel.swift; sourceTree = ""; }; - A5629AEF2877F73000094373 /* SocketFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketFactory.swift; sourceTree = ""; }; + A5629AEF2877F73000094373 /* DefaultSocketFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultSocketFactory.swift; sourceTree = ""; }; A578FA312873036400AA7720 /* InputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputView.swift; sourceTree = ""; }; A578FA34287304A300AA7720 /* Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = ""; }; A578FA362873D8EE00AA7720 /* UIColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColor.swift; sourceTree = ""; }; A578FA382873FCE000AA7720 /* ChatScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatScrollView.swift; sourceTree = ""; }; A578FA3C2874002400AA7720 /* View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = ""; }; - A57E71A5291CF76400325797 /* EthereumSigner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EthereumSigner.swift; sourceTree = ""; }; - A57E71A7291CF8A500325797 /* SolanaSigner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SolanaSigner.swift; sourceTree = ""; }; + A57E71A5291CF76400325797 /* ETHSigner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ETHSigner.swift; sourceTree = ""; }; + A57E71A7291CF8A500325797 /* SOLSigner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SOLSigner.swift; sourceTree = ""; }; A58E7CE828729F550082D443 /* Showcase.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Showcase.app; sourceTree = BUILT_PRODUCTS_DIR; }; A58E7CEA28729F550082D443 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; A58E7CEC28729F550082D443 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -315,6 +331,7 @@ A58E7D422872EE320082D443 /* MessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageView.swift; sourceTree = ""; }; A58E7D442872EE570082D443 /* ContentMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentMessageView.swift; sourceTree = ""; }; A58E7D472872EF610082D443 /* MessageViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageViewModel.swift; sourceTree = ""; }; + A59CF4F5292F83D50031A42F /* DefaultSignerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultSignerFactory.swift; sourceTree = ""; }; A59EBEF328B54A2A003EDAAF /* AuthRequestModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthRequestModule.swift; sourceTree = ""; }; A59EBEF428B54A2A003EDAAF /* AuthRequestPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthRequestPresenter.swift; sourceTree = ""; }; A59EBEF528B54A2A003EDAAF /* AuthRequestRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthRequestRouter.swift; sourceTree = ""; }; @@ -357,7 +374,6 @@ A5E03DFC286465D100888481 /* Stubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stubs.swift; sourceTree = ""; }; A5E03E02286466F400888481 /* ChatTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatTests.swift; sourceTree = ""; }; A5E03E0C28646AD200888481 /* RelayClientEndToEndTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RelayClientEndToEndTests.swift; sourceTree = ""; }; - A5E03E0E28646D8A00888481 /* WebSocketFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebSocketFactory.swift; sourceTree = ""; }; A5E03E1028646F8000888481 /* KeychainStorageMock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainStorageMock.swift; sourceTree = ""; }; A5E22D192840C62A00E36487 /* Engine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Engine.swift; sourceTree = ""; }; A5E22D1B2840C85D00E36487 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = ""; }; @@ -376,6 +392,7 @@ files = ( A5AE354728A1A2AC0059AE8A /* Web3 in Frameworks */, A5434023291E6A270068F706 /* SolanaSwift in Frameworks */, + 849D7A90292665D3006A2BD4 /* WalletConnectVerify in Frameworks */, 764E1D5826F8DBAB00A1FB15 /* WalletConnect in Frameworks */, A5D85226286333D500DAF5C3 /* Starscream in Frameworks */, A5C4DD8728A2DE88006A626D /* WalletConnectRouter in Frameworks */, @@ -387,6 +404,7 @@ buildActionMask = 2147483647; files = ( 8448F1D427E4726F0000B866 /* WalletConnect in Frameworks */, + A54195A52934E83F0035AD19 /* Web3 in Frameworks */, A5D85228286333E300DAF5C3 /* Starscream in Frameworks */, A5BB7FA328B6A50400707FC6 /* WalletConnectAuth in Frameworks */, ); @@ -417,6 +435,7 @@ A5E03DFF2864662500888481 /* WalletConnect in Frameworks */, A5E03DF52864651200888481 /* Starscream in Frameworks */, 847CF3AF28E3141700F1D760 /* WalletConnectPush in Frameworks */, + A5C8BE85292FE20B006CC85C /* Web3 in Frameworks */, 84DDB4ED28ABB663003D66ED /* WalletConnectAuth in Frameworks */, A5E03E01286466EA00888481 /* WalletConnectChat in Frameworks */, ); @@ -465,6 +484,7 @@ 764E1D3326F8D3FC00A1FB15 = { isa = PBXGroup; children = ( + A5A8E479293A1C4400FEB97D /* Shared */, A5F48A0528E43D3F0034CBFB /* Configuration.xcconfig */, 84CE6453279FFE1100142511 /* Wallet.entitlements */, 764E1D3E26F8D3FC00A1FB15 /* ExampleApp */, @@ -609,6 +629,7 @@ 84D2A66728A4F5260088AE09 /* Auth */ = { isa = PBXGroup; children = ( + A54195992934BFDD0035AD19 /* Signer */, 84D2A66528A4F51E0088AE09 /* AuthTests.swift */, ); path = Auth; @@ -630,6 +651,17 @@ path = Common; sourceTree = ""; }; + A54195992934BFDD0035AD19 /* Signer */ = { + isa = PBXGroup; + children = ( + A541959A2934BFEF0035AD19 /* CacaoSignerTests.swift */, + A541959D2934BFEF0035AD19 /* EIP191VerifierTests.swift */, + A541959C2934BFEF0035AD19 /* EIP1271VerifierTests.swift */, + A541959B2934BFEF0035AD19 /* SignerTests.swift */, + ); + path = Signer; + sourceTree = ""; + }; A55CAAAA28B92AF200844382 /* Scan */ = { isa = PBXGroup; children = ( @@ -657,7 +689,6 @@ isa = PBXGroup; children = ( A5C20227287EB342007E3188 /* AccountStorage */, - A5629AEE2877F72B00094373 /* SocketFactory */, A5629AEB2877F69C00094373 /* Chat */, ); path = DomainLayer; @@ -727,14 +758,6 @@ path = Chat; sourceTree = ""; }; - A5629AEE2877F72B00094373 /* SocketFactory */ = { - isa = PBXGroup; - children = ( - A5629AEF2877F73000094373 /* SocketFactory.swift */, - ); - path = SocketFactory; - sourceTree = ""; - }; A578FA332873049400AA7720 /* Style */ = { isa = PBXGroup; children = ( @@ -765,8 +788,8 @@ isa = PBXGroup; children = ( 84F568C1279582D200D0A289 /* Signer.swift */, - A57E71A5291CF76400325797 /* EthereumSigner.swift */, - A57E71A7291CF8A500325797 /* SolanaSigner.swift */, + A57E71A5291CF76400325797 /* ETHSigner.swift */, + A57E71A7291CF8A500325797 /* SOLSigner.swift */, ); path = Signer; sourceTree = ""; @@ -986,6 +1009,15 @@ path = Engine; sourceTree = ""; }; + A5A8E479293A1C4400FEB97D /* Shared */ = { + isa = PBXGroup; + children = ( + A5629AEF2877F73000094373 /* DefaultSocketFactory.swift */, + A59CF4F5292F83D50031A42F /* DefaultSignerFactory.swift */, + ); + path = Shared; + sourceTree = ""; + }; A5BB7FA528B6A5DC00707FC6 /* Auth */ = { isa = PBXGroup; children = ( @@ -1096,7 +1128,6 @@ children = ( A518B31328E33A6500A2CE93 /* InputConfig.swift */, A5E03E1028646F8000888481 /* KeychainStorageMock.swift */, - A5E03E0E28646D8A00888481 /* WebSocketFactory.swift */, A5E03DFC286465D100888481 /* Stubs.swift */, 84FE684528ACDB4700C893FF /* RequestParams.swift */, ); @@ -1149,6 +1180,7 @@ A5AE354628A1A2AC0059AE8A /* Web3 */, A5C4DD8628A2DE88006A626D /* WalletConnectRouter */, A5434022291E6A270068F706 /* SolanaSwift */, + 849D7A8F292665D3006A2BD4 /* WalletConnectVerify */, ); productName = ExampleApp; productReference = 764E1D3C26F8D3FC00A1FB15 /* WalletConnect Wallet.app */; @@ -1171,6 +1203,7 @@ 8448F1D327E4726F0000B866 /* WalletConnect */, A5D85227286333E300DAF5C3 /* Starscream */, A5BB7FA228B6A50400707FC6 /* WalletConnectAuth */, + A54195A42934E83F0035AD19 /* Web3 */, ); productName = DApp; productReference = 84CE641C27981DED00142511 /* DApp.app */; @@ -1237,6 +1270,7 @@ A5E03E00286466EA00888481 /* WalletConnectChat */, 84DDB4EC28ABB663003D66ED /* WalletConnectAuth */, 847CF3AE28E3141700F1D760 /* WalletConnectPush */, + A5C8BE84292FE20B006CC85C /* Web3 */, ); productName = IntegrationTests; productReference = A5E03DED286464DB00888481 /* IntegrationTests.xctest */; @@ -1357,11 +1391,12 @@ 76235E8B28201C9C004ED0AA /* Utilities.swift in Sources */, 76744CF726FE4D5400B77ED9 /* ActiveSessionItem.swift in Sources */, A5A4FC5A283CC08600BBEC1E /* SessionNamespaceViewModel.swift in Sources */, - A57E71A8291CF8A500325797 /* SolanaSigner.swift in Sources */, + A5A8E47B293A1CFE00FEB97D /* DefaultSocketFactory.swift in Sources */, + A57E71A8291CF8A500325797 /* SOLSigner.swift in Sources */, 764E1D4226F8D3FC00A1FB15 /* SceneDelegate.swift in Sources */, 84F568C2279582D200D0A289 /* Signer.swift in Sources */, A5A4FC56283CBB7800BBEC1E /* SessionDetailView.swift in Sources */, - A57E71A6291CF76400325797 /* EthereumSigner.swift in Sources */, + A57E71A6291CF76400325797 /* ETHSigner.swift in Sources */, 7600223B2819FC0B0011DD38 /* ProposalView.swift in Sources */, 761248172819F9E600CB6D48 /* WalletView.swift in Sources */, A5A4FC58283CBB9F00BBEC1E /* SessionDetailViewModel.swift in Sources */, @@ -1383,10 +1418,12 @@ A5BB7FA128B69F3400707FC6 /* AuthCoordinator.swift in Sources */, 84CE645527A29D4D00142511 /* ResponseViewController.swift in Sources */, 84CE641F27981DED00142511 /* AppDelegate.swift in Sources */, + A5A8E47D293A1CFE00FEB97D /* DefaultSocketFactory.swift in Sources */, A5BB7FAD28B6AA7D00707FC6 /* QRCodeGenerator.swift in Sources */, A51AC0D928E436A3001BACF9 /* InputConfig.swift in Sources */, A5BB7FA928B6A5FD00707FC6 /* AuthViewModel.swift in Sources */, 84CE6452279ED42B00142511 /* ConnectView.swift in Sources */, + A5A8E47E293A1CFE00FEB97D /* DefaultSignerFactory.swift in Sources */, 84CE6448279AE68600142511 /* AccountRequestViewController.swift in Sources */, A5BB7F9F28B69B7100707FC6 /* SignCoordinator.swift in Sources */, 84CE642127981DED00142511 /* SceneDelegate.swift in Sources */, @@ -1464,12 +1501,13 @@ A5629AC02876CBC000094373 /* ChatListInteractor.swift in Sources */, A55CAAB428B92AFF00844382 /* ScanView.swift in Sources */, A5629AE22876CC6E00094373 /* InviteListView.swift in Sources */, + A5A8E47F293A1D0000FEB97D /* DefaultSocketFactory.swift in Sources */, A578FA3D2874002400AA7720 /* View.swift in Sources */, A5629AD72876CC5700094373 /* InviteView.swift in Sources */, A59EBEFA28B54A2A003EDAAF /* AuthRequestRouter.swift in Sources */, - A5629AF02877F73000094373 /* SocketFactory.swift in Sources */, A55CAAB228B92AFF00844382 /* ScanRouter.swift in Sources */, A5C20221287EA5B8007E3188 /* TextFieldView.swift in Sources */, + A5A8E480293A1D0000FEB97D /* DefaultSignerFactory.swift in Sources */, A5C2022B287EB89A007E3188 /* WelcomeInteractor.swift in Sources */, A5C2020C287D9DEE007E3188 /* WelcomePresenter.swift in Sources */, A58E7D1D2872A57B0082D443 /* Configurator.swift in Sources */, @@ -1510,17 +1548,22 @@ 84CEC64628D89D6B00D081A8 /* PairingTests.swift in Sources */, 767DC83528997F8E00080FA9 /* EthSendTransaction.swift in Sources */, A518B31428E33A6500A2CE93 /* InputConfig.swift in Sources */, + A541959E2934BFEF0035AD19 /* CacaoSignerTests.swift in Sources */, + A59CF4F6292F83D50031A42F /* DefaultSignerFactory.swift in Sources */, A5E03E03286466F400888481 /* ChatTests.swift in Sources */, 84D2A66628A4F51E0088AE09 /* AuthTests.swift in Sources */, 84FE684628ACDB4700C893FF /* RequestParams.swift in Sources */, 7694A5262874296A0001257E /* RegistryTests.swift in Sources */, + A541959F2934BFEF0035AD19 /* SignerTests.swift in Sources */, A50C036528AAD32200FE72D3 /* ClientDelegate.swift in Sources */, A5E03E0D28646AD200888481 /* RelayClientEndToEndTests.swift in Sources */, A5E03DFA286465C700888481 /* SignClientTests.swift in Sources */, - A5E03E0F28646D8A00888481 /* WebSocketFactory.swift in Sources */, + A54195A02934BFEF0035AD19 /* EIP1271VerifierTests.swift in Sources */, + A54195A12934BFEF0035AD19 /* EIP191VerifierTests.swift in Sources */, 84AA01DB28CF0CD7005D48D8 /* XCTest.swift in Sources */, A5E03E1128646F8000888481 /* KeychainStorageMock.swift in Sources */, A5E03DFD286465D100888481 /* Stubs.swift in Sources */, + A5A8E47A293A1C9B00FEB97D /* DefaultSocketFactory.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2016,7 +2059,7 @@ repositoryURL = "https://github.com/WalletConnect/Web3.swift"; requirement = { kind = exactVersion; - version = 1.0.0; + version = 1.0.2; }; }; A5D85224286333D500DAF5C3 /* XCRemoteSwiftPackageReference "Starscream" */ = { @@ -2042,10 +2085,19 @@ isa = XCSwiftPackageProductDependency; productName = WalletConnectPush; }; + 849D7A8F292665D3006A2BD4 /* WalletConnectVerify */ = { + isa = XCSwiftPackageProductDependency; + productName = WalletConnectVerify; + }; 84DDB4EC28ABB663003D66ED /* WalletConnectAuth */ = { isa = XCSwiftPackageProductDependency; productName = WalletConnectAuth; }; + A54195A42934E83F0035AD19 /* Web3 */ = { + isa = XCSwiftPackageProductDependency; + package = A5AE354528A1A2AC0059AE8A /* XCRemoteSwiftPackageReference "Web3" */; + productName = Web3; + }; A5434022291E6A270068F706 /* SolanaSwift */ = { isa = XCSwiftPackageProductDependency; package = A5434021291E6A270068F706 /* XCRemoteSwiftPackageReference "solana-swift" */; @@ -2082,6 +2134,11 @@ isa = XCSwiftPackageProductDependency; productName = WalletConnectRouter; }; + A5C8BE84292FE20B006CC85C /* Web3 */ = { + isa = XCSwiftPackageProductDependency; + package = A5AE354528A1A2AC0059AE8A /* XCRemoteSwiftPackageReference "Web3" */; + productName = Web3; + }; A5D85225286333D500DAF5C3 /* Starscream */ = { isa = XCSwiftPackageProductDependency; package = A5D85224286333D500DAF5C3 /* XCRemoteSwiftPackageReference "Starscream" */; diff --git a/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 8893d902d..4dc474e71 100644 --- a/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -78,8 +78,8 @@ "repositoryURL": "https://github.com/WalletConnect/Web3.swift", "state": { "branch": null, - "revision": "bdaaed96eee3a9bf7f341165f89a94288961d14c", - "version": "1.0.0" + "revision": "569255adcfff0b37e4cb8004aea29d0e2d6266df", + "version": "1.0.2" } } ] diff --git a/Example/ExampleApp/SceneDelegate.swift b/Example/ExampleApp/SceneDelegate.swift index 0037fb05b..c3ddd5743 100644 --- a/Example/ExampleApp/SceneDelegate.swift +++ b/Example/ExampleApp/SceneDelegate.swift @@ -5,15 +5,6 @@ import WalletConnectSign import WalletConnectNetworking import WalletConnectRelay import WalletConnectPairing -import Starscream - -extension WebSocket: WebSocketConnecting { } - -struct SocketFactory: WebSocketFactory { - func create(with url: URL) -> WebSocketConnecting { - return WebSocket(url: url) - } -} class SceneDelegate: UIResponder, UIWindowSceneDelegate { @@ -27,7 +18,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { url: "example.wallet", icons: ["https://avatars.githubusercontent.com/u/37784886"]) - Networking.configure(projectId: InputConfig.projectId, socketFactory: SocketFactory()) + Networking.configure(projectId: InputConfig.projectId, socketFactory: DefaultSocketFactory()) Pair.configure(metadata: metadata) #if DEBUG if CommandLine.arguments.contains("-cleanInstall") { diff --git a/Example/ExampleApp/Shared/Signer/EthereumSigner.swift b/Example/ExampleApp/Shared/Signer/ETHSigner.swift similarity index 98% rename from Example/ExampleApp/Shared/Signer/EthereumSigner.swift rename to Example/ExampleApp/Shared/Signer/ETHSigner.swift index 6f1178872..3d1e0e743 100644 --- a/Example/ExampleApp/Shared/Signer/EthereumSigner.swift +++ b/Example/ExampleApp/Shared/Signer/ETHSigner.swift @@ -2,7 +2,7 @@ import Foundation import Commons import Web3 -struct EthereumSigner { +struct ETHSigner { private init() {} diff --git a/Example/ExampleApp/Shared/Signer/SolanaSigner.swift b/Example/ExampleApp/Shared/Signer/SOLSigner.swift similarity index 98% rename from Example/ExampleApp/Shared/Signer/SolanaSigner.swift rename to Example/ExampleApp/Shared/Signer/SOLSigner.swift index c65ec83f1..557d0177f 100644 --- a/Example/ExampleApp/Shared/Signer/SolanaSigner.swift +++ b/Example/ExampleApp/Shared/Signer/SOLSigner.swift @@ -3,7 +3,7 @@ import Commons import SolanaSwift import TweetNacl -struct SolanaSigner { +struct SOLSigner { static var address: String { return account.publicKey.base58EncodedString diff --git a/Example/ExampleApp/Shared/Signer/Signer.swift b/Example/ExampleApp/Shared/Signer/Signer.swift index dde8e101f..d22d863da 100644 --- a/Example/ExampleApp/Shared/Signer/Signer.swift +++ b/Example/ExampleApp/Shared/Signer/Signer.swift @@ -9,16 +9,16 @@ class Signer { static func sign(request: Request) -> AnyCodable { switch request.method { case "personal_sign": - return EthereumSigner.personalSign(request.params) + return ETHSigner.personalSign(request.params) case "eth_signTypedData": - return EthereumSigner.signTypedData(request.params) + return ETHSigner.signTypedData(request.params) case "eth_sendTransaction": - return EthereumSigner.sendTransaction(request.params) + return ETHSigner.sendTransaction(request.params) case "solana_signTransaction": - return SolanaSigner.signTransaction(request.params) + return SOLSigner.signTransaction(request.params) default: fatalError("not implemented") } diff --git a/Example/ExampleApp/Wallet/WalletViewController.swift b/Example/ExampleApp/Wallet/WalletViewController.swift index 034998740..b20a21ea0 100644 --- a/Example/ExampleApp/Wallet/WalletViewController.swift +++ b/Example/ExampleApp/Wallet/WalletViewController.swift @@ -9,8 +9,8 @@ import Combine final class WalletViewController: UIViewController { lazy var accounts = [ - "eip155": EthereumSigner.address, - "solana": SolanaSigner.address + "eip155": ETHSigner.address, + "solana": SOLSigner.address ] var sessionItems: [ActiveSessionItem] = [] diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index ea165a98f..8bede13a1 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -14,6 +14,8 @@ final class AuthTests: XCTestCase { var appAuthClient: AuthClient! var walletAuthClient: AuthClient! + + let walletAccount = Account(chainIdentifier: "eip155:1", address: "0x724d0D2DaD3fbB0C168f947B87Fa5DBe36F1A8bf")! let prvKey = Data(hex: "462c1dad6832d7d96ccf87bd6a686a4110e114aaaebd5512e552c0e3a87b480f") let eip1271Signature = "0xc1505719b2504095116db01baaf276361efd3a73c28cf8cc28dabefa945b8d536011289ac0a3b048600c1e692ff173ca944246cf7ceb319ac2262d27b395c82b1c" private var publishers = [AnyCancellable]() @@ -22,16 +24,15 @@ final class AuthTests: XCTestCase { setupClients() } - private func setupClients(address: String = "0x724d0D2DaD3fbB0C168f947B87Fa5DBe36F1A8bf", iatProvider: IATProvider = DefaultIATProvider()) { - let walletAccount = Account(chainIdentifier: "eip155:1", address: address)! + private func setupClients(iatProvider: IATProvider = DefaultIATProvider()) { (appPairingClient, appAuthClient) = makeClients(prefix: "🤖 App", iatProvider: iatProvider) - (walletPairingClient, walletAuthClient) = makeClients(prefix: "🐶 Wallet", account: walletAccount, iatProvider: iatProvider) + (walletPairingClient, walletAuthClient) = makeClients(prefix: "🐶 Wallet", iatProvider: iatProvider) } - func makeClients(prefix: String, account: Account? = nil, iatProvider: IATProvider) -> (PairingClient, AuthClient) { + func makeClients(prefix: String, iatProvider: IATProvider) -> (PairingClient, AuthClient) { let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug) let keychain = KeychainStorageMock() - let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: InputConfig.projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) + let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: InputConfig.projectId, keychainStorage: keychain, socketFactory: DefaultSocketFactory(), logger: logger) let keyValueStorage = RuntimeKeyValueStorage() let networkingClient = NetworkingClientFactory.create( @@ -48,8 +49,8 @@ final class AuthTests: XCTestCase { let authClient = AuthClientFactory.create( metadata: AppMetadata(name: name, description: "", url: "", icons: [""]), - account: account, projectId: InputConfig.projectId, + signerFactory: DefaultSignerFactory(), logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychain, @@ -80,9 +81,10 @@ final class AuthTests: XCTestCase { try! await walletPairingClient.pair(uri: uri) walletAuthClient.authRequestPublisher.sink { [unowned self] request in Task(priority: .high) { - let signer = MessageSignerFactory.create(projectId: InputConfig.projectId) - let signature = try! signer.sign(message: request.message, privateKey: prvKey, type: .eip191) - try! await walletAuthClient.respond(requestId: request.id, signature: signature) + 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) + try! await walletAuthClient.respond(requestId: request.id, signature: signature, from: walletAccount) } } .store(in: &publishers) @@ -95,7 +97,9 @@ final class AuthTests: XCTestCase { } func testEIP1271RespondSuccess() async { - setupClients(address: "0x2faf83c542b68f1b4cdc0e770e8cb9f567b08f71", iatProvider: IATProviderMock()) + setupClients(iatProvider: IATProviderMock()) + + let account = Account(chainIdentifier: "eip155:1", address: "0x2faf83c542b68f1b4cdc0e770e8cb9f567b08f71")! let responseExpectation = expectation(description: "successful response delivered") let uri = try! await appPairingClient.create() @@ -115,7 +119,7 @@ final class AuthTests: XCTestCase { walletAuthClient.authRequestPublisher.sink { [unowned self] request in Task(priority: .high) { let signature = CacaoSignature(t: .eip1271, s: eip1271Signature) - try! await walletAuthClient.respond(requestId: request.id, signature: signature) + try! await walletAuthClient.respond(requestId: request.id, signature: signature, from: account) } } .store(in: &publishers) @@ -136,7 +140,7 @@ final class AuthTests: XCTestCase { walletAuthClient.authRequestPublisher.sink { [unowned self] request in Task(priority: .high) { let signature = CacaoSignature(t: .eip1271, s: eip1271Signature) - try! await walletAuthClient.respond(requestId: request.id, signature: signature) + try! await walletAuthClient.respond(requestId: request.id, signature: signature, from: walletAccount) } } .store(in: &publishers) @@ -179,7 +183,7 @@ final class AuthTests: XCTestCase { Task(priority: .high) { let invalidSignature = "438effc459956b57fcd9f3dac6c675f9cee88abf21acab7305e8e32aa0303a883b06dcbd956279a7a2ca21ffa882ff55cc22e8ab8ec0f3fe90ab45f306938cfa1b" let cacaoSignature = CacaoSignature(t: .eip191, s: invalidSignature) - try! await walletAuthClient.respond(requestId: request.id, signature: cacaoSignature) + try! await walletAuthClient.respond(requestId: request.id, signature: cacaoSignature, from: walletAccount) } } .store(in: &publishers) diff --git a/Tests/AuthTests/CacaoSignerTests.swift b/Example/IntegrationTests/Auth/Signer/CacaoSignerTests.swift similarity index 52% rename from Tests/AuthTests/CacaoSignerTests.swift rename to Example/IntegrationTests/Auth/Signer/CacaoSignerTests.swift index 95a01a538..c6481015b 100644 --- a/Tests/AuthTests/CacaoSignerTests.swift +++ b/Example/IntegrationTests/Auth/Signer/CacaoSignerTests.swift @@ -1,11 +1,11 @@ import Foundation import XCTest @testable import Auth -import TestingUtils class CacaoSignerTest: XCTestCase { - let signer = MessageSignerFactory.create(projectId: "project-id") + let signer = MessageSignerFactory(signerFactory: DefaultSignerFactory()) + .create(projectId: InputConfig.projectId) let privateKey = Data(hex: "305c6cde3846927892cd32762f6120539f3ec74c9e3a16b9b798b1e85351ae2a") @@ -26,10 +26,31 @@ class CacaoSignerTest: XCTestCase { - https://example.com/my-web2-claim.json """ + let payload = AuthPayload(requestParams: RequestParams( + domain: "service.invalid", + chainId: "eip155:1", + nonce: "32891756", + aud: "https://service.invalid/login", + nbf: nil, + exp: nil, + statement: "I accept the ServiceOrg Terms of Service: https://service.invalid/tos", + requestId: nil, + resources: [ + "ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/", + "https://example.com/my-web2-claim.json" + ] + ), iat: "2021-09-30T16:25:24Z") + let signature = CacaoSignature(t: .eip191, s: "0x438effc459956b57fcd9f3dac6c675f9cee88abf21acab7305e8e32aa0303a883b06dcbd956279a7a2ca21ffa882ff55cc22e8ab8ec0f3fe90ab45f306938cfa1b") func testCacaoSign() throws { - XCTAssertEqual(try signer.sign(message: message, privateKey: privateKey, type: .eip191), signature) + let address = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" + let formatted = try SIWEMessageFormatter().formatMessage( + from: payload, + address: address + ) + XCTAssertEqual(formatted, message) + XCTAssertEqual(try signer.sign(payload: payload, address: address, 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 new file mode 100644 index 000000000..45c5c1bdd --- /dev/null +++ b/Example/IntegrationTests/Auth/Signer/EIP1271VerifierTests.swift @@ -0,0 +1,39 @@ +import Foundation +import XCTest +@testable import Auth +import JSONRPC + +class EIP1271VerifierTests: XCTestCase { + + let signature = Data(hex: "c1505719b2504095116db01baaf276361efd3a73c28cf8cc28dabefa945b8d536011289ac0a3b048600c1e692ff173ca944246cf7ceb319ac2262d27b395c82b1c") + + let message = Data(hex: "19457468657265756d205369676e6564204d6573736167653a0a3235326c6f63616c686f73742077616e747320796f7520746f207369676e20696e207769746820796f757220457468657265756d206163636f756e743a0a3078326661663833633534326236386631623463646330653737306538636239663536376230386637310a0a5552493a20687474703a2f2f6c6f63616c686f73743a333030302f0a56657273696f6e3a20310a436861696e2049443a20310a4e6f6e63653a20313636353434333031353730300a4973737565642041743a20323032322d31302d31305432333a30333a33352e3730305a0a45787069726174696f6e2054696d653a20323032322d31302d31315432333a30333a33352e3730305a") + + let address = "0x2faf83c542b68f1b4cdc0e770e8cb9f567b08f71" + let chainId = "eip155:1" + + func testSuccessVerify() async throws { + let httpClient = HTTPNetworkClient(host: "rpc.walletconnect.com") + let signer = DefaultSignerFactory().createEthereumSigner() + let verifier = EIP1271Verifier(projectId: InputConfig.projectId, httpClient: httpClient, signer: signer) + try await verifier.verify( + signature: signature, + message: message, + address: address, + chainId: chainId + ) + } + + func testFailureVerify() async throws { + let httpClient = HTTPNetworkClient(host: "rpc.walletconnect.com") + let signer = DefaultSignerFactory().createEthereumSigner() + let verifier = EIP1271Verifier(projectId: InputConfig.projectId, httpClient: httpClient, signer: signer) + + await XCTAssertThrowsErrorAsync(try await verifier.verify( + signature: Data("deadbeaf"), + message: message, + address: address, + chainId: chainId + )) + } +} diff --git a/Tests/AuthTests/EIP191VerifierTests.swift b/Example/IntegrationTests/Auth/Signer/EIP191VerifierTests.swift similarity index 95% rename from Tests/AuthTests/EIP191VerifierTests.swift rename to Example/IntegrationTests/Auth/Signer/EIP191VerifierTests.swift index 7b683ab14..2e5c9c1f3 100644 --- a/Tests/AuthTests/EIP191VerifierTests.swift +++ b/Example/IntegrationTests/Auth/Signer/EIP191VerifierTests.swift @@ -1,11 +1,10 @@ import Foundation import XCTest -import TestingUtils @testable import Auth class EIP191VerifierTests: XCTestCase { - private let verifier = EIP191Verifier() + private let verifier = EIP191Verifier(signer: DefaultSignerFactory().createEthereumSigner()) private let address = "0x15bca56b6e2728aec2532df9d436bd1600e86688" private let message = "\u{19}Ethereum Signed Message:\n7Message".data(using: .utf8)! diff --git a/Tests/AuthTests/SignerTests.swift b/Example/IntegrationTests/Auth/Signer/SignerTests.swift similarity index 78% rename from Tests/AuthTests/SignerTests.swift rename to Example/IntegrationTests/Auth/Signer/SignerTests.swift index 80b19ecfb..2a8f49b2d 100644 --- a/Tests/AuthTests/SignerTests.swift +++ b/Example/IntegrationTests/Auth/Signer/SignerTests.swift @@ -1,24 +1,21 @@ import Foundation import XCTest @testable import Auth -import secp256k1 -import Web3 -import WalletConnectUtils import WalletConnectRelay class SignerTest: XCTestCase { - private let signer = Signer() + private let signer = DefaultSignerFactory().createEthereumSigner() private let message = "\u{19}Ethereum Signed Message:\n7Message".data(using: .utf8)! private let privateKey = Data(hex: "305c6cde3846927892cd32762f6120539f3ec74c9e3a16b9b798b1e85351ae2a") - private let signature = Data(hex: "66121e60cccc01fbf7fcba694a1e08ac5db35fb4ec6c045bedba7860445b95c021cad2c595f0bf68ff896964c7c02bb2f3a3e9540e8e4595c98b737ce264cc541b") + private let signature = "0x66121e60cccc01fbf7fcba694a1e08ac5db35fb4ec6c045bedba7860445b95c021cad2c595f0bf68ff896964c7c02bb2f3a3e9540e8e4595c98b737ce264cc541b" private var address = "0x15bca56b6e2728aec2532df9d436bd1600e86688" func testValidSignature() throws { let result = try signer.sign(message: message, with: privateKey) - XCTAssertEqual(signature.toHexString(), result.toHexString()) + XCTAssertEqual(signature, result.hex()) } private func prefixed(_ message: Data) -> Data { diff --git a/Example/IntegrationTests/Chat/ChatTests.swift b/Example/IntegrationTests/Chat/ChatTests.swift index 4c305a88f..0f7d770ce 100644 --- a/Example/IntegrationTests/Chat/ChatTests.swift +++ b/Example/IntegrationTests/Chat/ChatTests.swift @@ -21,7 +21,7 @@ final class ChatTests: XCTestCase { func makeClient(prefix: String) -> ChatClient { let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug) let keychain = KeychainStorageMock() - let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: InputConfig.projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) + let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: InputConfig.projectId, keychainStorage: keychain, socketFactory: DefaultSocketFactory(), logger: logger) return ChatClientFactory.create(registry: registry, relayClient: relayClient, kms: KeyManagementService(keychain: keychain), logger: logger, keyValueStorage: RuntimeKeyValueStorage()) } diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift index dce08c134..a3724cb67 100644 --- a/Example/IntegrationTests/Pairing/PairingTests.swift +++ b/Example/IntegrationTests/Pairing/PairingTests.swift @@ -34,7 +34,7 @@ final class PairingTests: XCTestCase { projectId: InputConfig.projectId, keyValueStorage: RuntimeKeyValueStorage(), keychainStorage: keychain, - socketFactory: SocketFactory(), + socketFactory: DefaultSocketFactory(), logger: relayLogger) let networkingClient = NetworkingClientFactory.create( @@ -68,7 +68,7 @@ final class PairingTests: XCTestCase { relayHost: InputConfig.relayHost, projectId: InputConfig.projectId, keychainStorage: keychain, - socketFactory: SocketFactory(), + socketFactory: DefaultSocketFactory(), logger: logger) let networkingClient = NetworkingClientFactory.create( diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift index acbfbb728..a429d0884 100644 --- a/Example/IntegrationTests/Sign/SignClientTests.swift +++ b/Example/IntegrationTests/Sign/SignClientTests.swift @@ -19,7 +19,7 @@ final class SignClientTests: XCTestCase { projectId: InputConfig.projectId, keyValueStorage: RuntimeKeyValueStorage(), keychainStorage: keychain, - socketFactory: SocketFactory(), + socketFactory: DefaultSocketFactory(), socketConnectionType: .automatic, logger: logger ) diff --git a/Example/IntegrationTests/Stubs/WebSocketFactory.swift b/Example/IntegrationTests/Stubs/WebSocketFactory.swift deleted file mode 100644 index 22f45a8cb..000000000 --- a/Example/IntegrationTests/Stubs/WebSocketFactory.swift +++ /dev/null @@ -1,14 +0,0 @@ -import Foundation -import Starscream -import WalletConnectRelay - -extension WebSocket: WebSocketConnecting { } - -public struct SocketFactory: WebSocketFactory { - - public init() { } - - public func create(with url: URL) -> WebSocketConnecting { - return WebSocket(url: url) - } -} diff --git a/Example/Shared/DefaultSignerFactory.swift b/Example/Shared/DefaultSignerFactory.swift new file mode 100644 index 000000000..ac720452d --- /dev/null +++ b/Example/Shared/DefaultSignerFactory.swift @@ -0,0 +1,36 @@ +import Foundation +import CryptoSwift +import Web3 +import Auth + +public struct DefaultSignerFactory: SignerFactory { + + public func createEthereumSigner() -> EthereumSigner { + return Web3Signer() + } +} + +public struct Web3Signer: EthereumSigner { + + public func sign(message: Data, with key: Data) throws -> EthereumSignature { + let privateKey = try EthereumPrivateKey(privateKey: [UInt8](key)) + let signature = try privateKey.sign(message: message.bytes) + return EthereumSignature(v: UInt8(signature.v), r: signature.r, s: signature.s) + } + + public func recoverPubKey(signature: EthereumSignature, message: Data) throws -> Data { + let publicKey = try EthereumPublicKey( + message: message.bytes, + v: EthereumQuantity(quantity: BigUInt(signature.v)), + r: EthereumQuantity(signature.r), + s: EthereumQuantity(signature.s) + ) + return Data(publicKey.rawPublicKey) + } + + public func keccak256(_ data: Data) -> Data { + let digest = SHA3(variant: .keccak256) + let hash = digest.calculate(for: [UInt8](data)) + return Data(hash) + } +} diff --git a/Example/Showcase/Classes/DomainLayer/SocketFactory/SocketFactory.swift b/Example/Shared/DefaultSocketFactory.swift similarity index 81% rename from Example/Showcase/Classes/DomainLayer/SocketFactory/SocketFactory.swift rename to Example/Shared/DefaultSocketFactory.swift index d22a5ef48..14bb1ed98 100644 --- a/Example/Showcase/Classes/DomainLayer/SocketFactory/SocketFactory.swift +++ b/Example/Shared/DefaultSocketFactory.swift @@ -4,7 +4,7 @@ import WalletConnectRelay extension WebSocket: WebSocketConnecting { } -struct SocketFactory: WebSocketFactory { +struct DefaultSocketFactory: WebSocketFactory { func create(with url: URL) -> WebSocketConnecting { return WebSocket(url: url) } diff --git a/Example/Showcase/Classes/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift b/Example/Showcase/Classes/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift index a8936517a..f71b9de3e 100644 --- a/Example/Showcase/Classes/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift +++ b/Example/Showcase/Classes/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift @@ -5,7 +5,7 @@ import Auth struct ThirdPartyConfigurator: Configurator { func configure() { - Networking.configure(projectId: InputConfig.projectId, socketFactory: SocketFactory()) + Networking.configure(projectId: InputConfig.projectId, socketFactory: DefaultSocketFactory()) Pair.configure( metadata: AppMetadata( name: "Showcase App", @@ -14,8 +14,6 @@ struct ThirdPartyConfigurator: Configurator { icons: ["https://avatars.githubusercontent.com/u/37784886"] )) - Auth.configure( - account: Account("eip155:1:0xe5EeF1368781911d265fDB6946613dA61915a501")! - ) + Auth.configure(signerFactory: DefaultSignerFactory()) } } diff --git a/Example/Showcase/Classes/PresentationLayer/Wallet/AuthRequest/AuthRequestInteractor.swift b/Example/Showcase/Classes/PresentationLayer/Wallet/AuthRequest/AuthRequestInteractor.swift index c0c3a8bd3..816d0b398 100644 --- a/Example/Showcase/Classes/PresentationLayer/Wallet/AuthRequest/AuthRequestInteractor.swift +++ b/Example/Showcase/Classes/PresentationLayer/Wallet/AuthRequest/AuthRequestInteractor.swift @@ -4,14 +4,27 @@ import WalletConnectUtils final class AuthRequestInteractor { + private let signer = MessageSignerFactory(signerFactory: DefaultSignerFactory()).create() + private let account = Account("eip155:1:0xe5EeF1368781911d265fDB6946613dA61915a501")! + func approve(request: AuthRequest) async throws { let privateKey = Data(hex: "e56da0e170b5e09a8bb8f1b693392c7d56c3739a9c75740fbc558a2877868540") - let signer = MessageSignerFactory.create() - let signature = try signer.sign(message: request.message, privateKey: privateKey, type: .eip191) - try await Auth.instance.respond(requestId: request.id, signature: signature) + let signature = try signer.sign( + payload: request.payload, + address: account.address, + privateKey: privateKey, + type: .eip191) + try await Auth.instance.respond(requestId: request.id, signature: signature, from: account) } func reject(request: AuthRequest) async throws { try await Auth.instance.reject(requestId: request.id) } + + func formatted(request: AuthRequest) -> String { + return try! Auth.instance.formatMessage( + payload: request.payload, + address: account.address + ) + } } diff --git a/Example/Showcase/Classes/PresentationLayer/Wallet/AuthRequest/AuthRequestPresenter.swift b/Example/Showcase/Classes/PresentationLayer/Wallet/AuthRequest/AuthRequestPresenter.swift index f70700e86..3aece0e97 100644 --- a/Example/Showcase/Classes/PresentationLayer/Wallet/AuthRequest/AuthRequestPresenter.swift +++ b/Example/Showcase/Classes/PresentationLayer/Wallet/AuthRequest/AuthRequestPresenter.swift @@ -17,7 +17,7 @@ final class AuthRequestPresenter: ObservableObject { } var message: String { - return request.message + return interactor.formatted(request: request) } @MainActor diff --git a/Package.swift b/Package.swift index 5ea3fe03c..a9f2af394 100644 --- a/Package.swift +++ b/Package.swift @@ -30,11 +30,12 @@ let package = Package( targets: ["WalletConnectRouter"]), .library( name: "WalletConnectNetworking", - targets: ["WalletConnectNetworking"]) - ], - dependencies: [ - .package(url: "https://github.com/WalletConnect/Web3.swift", .exact("1.0.0")) + targets: ["WalletConnectNetworking"]), + .library( + name: "WalletConnectVerify", + targets: ["WalletConnectVerify"]) ], + dependencies: [], targets: [ .target( name: "WalletConnectSign", @@ -46,7 +47,7 @@ let package = Package( path: "Sources/Chat"), .target( name: "Auth", - dependencies: ["WalletConnectPairing", .product(name: "Web3", package: "Web3.swift")], + dependencies: ["WalletConnectPairing"], path: "Sources/Auth"), .target( name: "WalletConnectPush", @@ -79,6 +80,9 @@ let package = Package( .target( name: "WalletConnectRouter", dependencies: []), + .target( + name: "WalletConnectVerify", + dependencies: ["WalletConnectUtils"]), .testTarget( name: "WalletConnectSignTests", dependencies: ["WalletConnectSign", "TestingUtils"]), @@ -94,6 +98,9 @@ let package = Package( .testTarget( name: "RelayerTests", dependencies: ["WalletConnectRelay", "WalletConnectUtils", "TestingUtils"]), + .testTarget( + name: "VerifyTests", + dependencies: ["WalletConnectVerify", "TestingUtils"]), .testTarget( name: "WalletConnectKMSTests", dependencies: ["WalletConnectKMS", "WalletConnectUtils", "TestingUtils"]), diff --git a/README.md b/README.md index 32b7736dd..3b269c948 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,16 @@ dependencies: [ .package(url: "https://github.com/WalletConnect/WalletConnectSwiftV2", .branch("main")), ], ``` +### Cocoapods +Add pod to your Podfile: + +```Ruby +pod 'WalletConnectSwiftV2' +``` +If you encounter any problems during package installation, you can specify the exact path to the repository +```Ruby +pod 'WalletConnectSwiftV2', :git => 'https://github.com/WalletConnect/WalletConnectSwiftV2.git', :tag => '1.0.5' +``` ## Setting Project ID Follow instructions from *Configuration.xcconfig* and configure PROJECT_ID with your ID from WalletConnect Dashboard ``` diff --git a/Sources/Auth/Auth.swift b/Sources/Auth/Auth.swift index 52bda894a..263e742e6 100644 --- a/Sources/Auth/Auth.swift +++ b/Sources/Auth/Auth.swift @@ -16,10 +16,13 @@ public class Auth { /// Auth client instance public static var instance: AuthClient = { + guard let config = Auth.config else { + fatalError("Error - you must call Auth.configure(_:) before accessing the shared instance.") + } return AuthClientFactory.create( metadata: Pair.metadata, - account: config?.account, projectId: Networking.projectId, + signerFactory: config.signerFactory, networkingClient: Networking.interactor, pairingRegisterer: Pair.registerer ) @@ -29,10 +32,10 @@ public class Auth { private init() { } - /// Auth instance wallet config method + /// Auth instance wallet config method. For DApp usage /// - Parameters: - /// - account: account that wallet will be authenticating with. - static public func configure(account: Account) { - Auth.config = Auth.Config(account: account) + /// - signerFactory: Auth signers factory + static public func configure(signerFactory: SignerFactory) { + Auth.config = Auth.Config(signerFactory: signerFactory) } } diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index 1de34a126..43847d3ef 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -7,9 +7,6 @@ import Combine /// /// Access via `Auth.instance` public class AuthClient { - enum Errors: Error { - case unknownWalletAddress - } // MARK: - Public Properties @@ -46,13 +43,11 @@ public class AuthClient { private let walletRequestSubscriber: WalletRequestSubscriber private let walletRespondService: WalletRespondService private let pendingRequestsProvider: PendingRequestsProvider - private var account: Account? init(appRequestService: AppRequestService, appRespondSubscriber: AppRespondSubscriber, walletRequestSubscriber: WalletRequestSubscriber, walletRespondService: WalletRespondService, - account: Account?, pendingRequestsProvider: PendingRequestsProvider, logger: ConsoleLogging, socketConnectionStatusPublisher: AnyPublisher, @@ -62,7 +57,6 @@ public class AuthClient { self.walletRequestSubscriber = walletRequestSubscriber self.walletRespondService = walletRespondService self.appRespondSubscriber = appRespondSubscriber - self.account = account self.pendingRequestsProvider = pendingRequestsProvider self.logger = logger self.socketConnectionStatusPublisher = socketConnectionStatusPublisher @@ -83,8 +77,7 @@ public class AuthClient { /// - Parameters: /// - requestId: authentication request id /// - signature: CACAO signature of requested message - public func respond(requestId: RPCID, signature: CacaoSignature) async throws { - guard let account = account else { throw Errors.unknownWalletAddress } + public func respond(requestId: RPCID, signature: CacaoSignature, from account: Account) async throws { try await walletRespondService.respond(requestId: requestId, signature: signature, account: account) } @@ -96,11 +89,14 @@ public class AuthClient { /// Query pending authentication requests /// - Returns: Pending authentication requests - public func getPendingRequests() throws -> [AuthRequest] { - guard let account = account else { throw Errors.unknownWalletAddress } + public func getPendingRequests(account: Account) throws -> [AuthRequest] { return try pendingRequestsProvider.getPendingRequests(account: account) } + public func formatMessage(payload: AuthPayload, address: String) throws -> String { + return try SIWEMessageFormatter().formatMessage(from: payload, address: address) + } + private func setUpPublishers() { appRespondSubscriber.onResponse = { [unowned self] (id, result) in authResponsePublisherSubject.send((id, result)) diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift index 89816a70f..5fc6cb188 100644 --- a/Sources/Auth/AuthClientFactory.swift +++ b/Sources/Auth/AuthClientFactory.swift @@ -2,33 +2,65 @@ import Foundation public struct AuthClientFactory { - public static func create(metadata: AppMetadata, account: Account?, projectId: String, networkingClient: NetworkingInteractor, pairingRegisterer: PairingRegisterer) -> AuthClient { + public static func create( + metadata: AppMetadata, + projectId: String, + signerFactory: SignerFactory, + networkingClient: NetworkingInteractor, + pairingRegisterer: PairingRegisterer + ) -> AuthClient { + let logger = ConsoleLogger(loggingLevel: .off) let keyValueStorage = UserDefaults.standard let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") - return AuthClientFactory.create(metadata: metadata, account: account, projectId: projectId, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, networkingClient: networkingClient, pairingRegisterer: pairingRegisterer, iatProvider: DefaultIATProvider()) + let iatProvider = DefaultIATProvider() + + return AuthClientFactory.create( + metadata: metadata, + projectId: projectId, + signerFactory: signerFactory, + logger: logger, + keyValueStorage: keyValueStorage, + keychainStorage: keychainStorage, + networkingClient: networkingClient, + pairingRegisterer: pairingRegisterer, + iatProvider: iatProvider + ) } - static func create(metadata: AppMetadata, account: Account?, projectId: String, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, networkingClient: NetworkingInteractor, pairingRegisterer: PairingRegisterer, iatProvider: IATProvider) -> AuthClient { + static func create( + metadata: AppMetadata, + projectId: String, + signerFactory: SignerFactory, + logger: ConsoleLogging, + keyValueStorage: KeyValueStorage, + keychainStorage: KeychainStorageProtocol, + networkingClient: NetworkingInteractor, + pairingRegisterer: PairingRegisterer, + iatProvider: IATProvider + ) -> AuthClient { + let kms = KeyManagementService(keychain: keychainStorage) let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) let messageFormatter = SIWEMessageFormatter() let appRequestService = AppRequestService(networkingInteractor: networkingClient, kms: kms, appMetadata: metadata, logger: logger, iatProvader: iatProvider) - let messageSigner = MessageSignerFactory.create(projectId: projectId) + let messageSignerFactory = MessageSignerFactory(signerFactory: signerFactory) + let messageSigner = messageSignerFactory.create(projectId: projectId) let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingClient, logger: logger, rpcHistory: history, signatureVerifier: messageSigner, pairingRegisterer: pairingRegisterer, messageFormatter: messageFormatter) let walletErrorResponder = WalletErrorResponder(networkingInteractor: networkingClient, logger: logger, kms: kms, rpcHistory: history) - let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingClient, logger: logger, kms: kms, messageFormatter: messageFormatter, address: account?.address, walletErrorResponder: walletErrorResponder, pairingRegisterer: pairingRegisterer) + let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingClient, logger: logger, kms: kms, walletErrorResponder: walletErrorResponder, pairingRegisterer: pairingRegisterer) let walletRespondService = WalletRespondService(networkingInteractor: networkingClient, logger: logger, kms: kms, rpcHistory: history, walletErrorResponder: walletErrorResponder) let pendingRequestsProvider = PendingRequestsProvider(rpcHistory: history) - return AuthClient(appRequestService: appRequestService, - appRespondSubscriber: appRespondSubscriber, - walletRequestSubscriber: walletRequestSubscriber, - walletRespondService: walletRespondService, - account: account, - pendingRequestsProvider: pendingRequestsProvider, - logger: logger, - socketConnectionStatusPublisher: networkingClient.socketConnectionStatusPublisher, - pairingRegisterer: pairingRegisterer) + return AuthClient( + appRequestService: appRequestService, + appRespondSubscriber: appRespondSubscriber, + walletRequestSubscriber: walletRequestSubscriber, + walletRespondService: walletRespondService, + pendingRequestsProvider: pendingRequestsProvider, + logger: logger, + socketConnectionStatusPublisher: networkingClient.socketConnectionStatusPublisher, + pairingRegisterer: pairingRegisterer + ) } } diff --git a/Sources/Auth/AuthConfig.swift b/Sources/Auth/AuthConfig.swift index 00a98faed..bf5c09c24 100644 --- a/Sources/Auth/AuthConfig.swift +++ b/Sources/Auth/AuthConfig.swift @@ -2,6 +2,6 @@ import Foundation extension Auth { struct Config { - let account: Account? + let signerFactory: SignerFactory } } diff --git a/Sources/Auth/Extensions/Data+Keccak256.swift b/Sources/Auth/Extensions/Data+Keccak256.swift deleted file mode 100644 index 1bfbc1b8b..000000000 --- a/Sources/Auth/Extensions/Data+Keccak256.swift +++ /dev/null @@ -1,9 +0,0 @@ -import Foundation -import CryptoSwift - -extension Data { - - var keccak256: Data { - return sha3(.keccak256) - } -} diff --git a/Sources/Auth/Services/App/AppRespondSubscriber.swift b/Sources/Auth/Services/App/AppRespondSubscriber.swift index 99b8410b8..2aaaba9e9 100644 --- a/Sources/Auth/Services/App/AppRespondSubscriber.swift +++ b/Sources/Auth/Services/App/AppRespondSubscriber.swift @@ -49,7 +49,11 @@ class AppRespondSubscriber { let message = try? messageFormatter.formatMessage(from: cacao.p) else { self.onResponse?(requestId, .failure(.malformedResponseParams)); return } - guard messageFormatter.formatMessage(from: requestPayload.payloadParams, address: address) == message + guard + let recovered = try? messageFormatter.formatMessage( + from: requestPayload.payloadParams, + address: address + ), recovered == message else { self.onResponse?(requestId, .failure(.messageCompromised)); return } Task(priority: .high) { diff --git a/Sources/Auth/Services/Common/SIWEMessageFormatter.swift b/Sources/Auth/Services/Common/SIWEMessageFormatter.swift index 5c69caa90..5e8c82d57 100644 --- a/Sources/Auth/Services/Common/SIWEMessageFormatter.swift +++ b/Sources/Auth/Services/Common/SIWEMessageFormatter.swift @@ -1,25 +1,35 @@ import Foundation protocol SIWEMessageFormatting { - func formatMessage(from authPayload: AuthPayload, address: String) -> String? + func formatMessage(from payload: AuthPayload, address: String) throws -> String func formatMessage(from payload: CacaoPayload) throws -> String } -struct SIWEMessageFormatter: SIWEMessageFormatting { - func formatMessage(from payload: AuthPayload, address: String) -> String? { - guard let chain = Blockchain(payload.chainId) else {return nil} - 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 +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 } diff --git a/Sources/Auth/Services/Signer/EIP191/EIP191Verifier.swift b/Sources/Auth/Services/Signer/EIP191/EIP191Verifier.swift deleted file mode 100644 index 7622cae14..000000000 --- a/Sources/Auth/Services/Signer/EIP191/EIP191Verifier.swift +++ /dev/null @@ -1,36 +0,0 @@ -import Foundation -import Web3 - -actor EIP191Verifier { - - func verify(signature: Data, message: Data, address: String) async throws { - let sig = decompose(signature: signature) - let publicKey = try EthereumPublicKey.init( - message: message.bytes, - v: EthereumQuantity(quantity: BigUInt(sig.v)), - r: EthereumQuantity(sig.r), - s: EthereumQuantity(sig.s) - ) - try verifyPublicKey(publicKey, address: address) - } - - private func decompose(signature: Data) -> Signer.Signature { - let v = signature.bytes[signature.count-1] - let r = signature.bytes[0..<32] - let s = signature.bytes[32..<64] - return (UInt(v), [UInt8](r), [UInt8](s)) - } - - private func verifyPublicKey(_ publicKey: EthereumPublicKey, address: String) throws { - guard publicKey.address.hex(eip55: false) == address.lowercased() else { - throw Errors.invalidSignature - } - } -} - -extension EIP191Verifier { - - enum Errors: Error { - case invalidSignature - } -} diff --git a/Sources/Auth/Services/Signer/EIP1271/EIP1271Verifier.swift b/Sources/Auth/Services/Signer/Ethereum/EIP1271/EIP1271Verifier.swift similarity index 85% rename from Sources/Auth/Services/Signer/EIP1271/EIP1271Verifier.swift rename to Sources/Auth/Services/Signer/Ethereum/EIP1271/EIP1271Verifier.swift index a3e6d527c..a3b70ae8b 100644 --- a/Sources/Auth/Services/Signer/EIP1271/EIP1271Verifier.swift +++ b/Sources/Auth/Services/Signer/Ethereum/EIP1271/EIP1271Verifier.swift @@ -3,14 +3,17 @@ import Foundation actor EIP1271Verifier { private let projectId: String private let httpClient: HTTPClient + private let signer: EthereumSigner - init(projectId: String, httpClient: HTTPClient) { + init(projectId: String, httpClient: HTTPClient, signer: EthereumSigner) { self.projectId = projectId self.httpClient = httpClient + self.signer = signer } func verify(signature: Data, message: Data, address: String, chainId: String) async throws { - let encoder = ValidSignatureMethod(signature: signature, messageHash: message.keccak256) + let messageHash = signer.keccak256(message) + let encoder = ValidSignatureMethod(signature: signature, messageHash: messageHash) let call = EthCall(to: address, data: encoder.encode()) let params = AnyCodable([AnyCodable(call), AnyCodable("latest")]) let request = RPCRequest(method: "eth_call", params: params) diff --git a/Sources/Auth/Services/Signer/EIP1271/RPCService.swift b/Sources/Auth/Services/Signer/Ethereum/EIP1271/RPCService.swift similarity index 100% rename from Sources/Auth/Services/Signer/EIP1271/RPCService.swift rename to Sources/Auth/Services/Signer/Ethereum/EIP1271/RPCService.swift diff --git a/Sources/Auth/Services/Signer/EIP1271/ValidSignatureMethod.swift b/Sources/Auth/Services/Signer/Ethereum/EIP1271/ValidSignatureMethod.swift similarity index 100% rename from Sources/Auth/Services/Signer/EIP1271/ValidSignatureMethod.swift rename to Sources/Auth/Services/Signer/Ethereum/EIP1271/ValidSignatureMethod.swift diff --git a/Sources/Auth/Services/Signer/Ethereum/EIP191/EIP191Verifier.swift b/Sources/Auth/Services/Signer/Ethereum/EIP191/EIP191Verifier.swift new file mode 100644 index 000000000..9517e0831 --- /dev/null +++ b/Sources/Auth/Services/Signer/Ethereum/EIP191/EIP191Verifier.swift @@ -0,0 +1,32 @@ +import Foundation + +actor EIP191Verifier { + + let signer: EthereumSigner + + init(signer: EthereumSigner) { + self.signer = signer + } + + func verify(signature: Data, message: Data, address: String) async throws { + let sig = EthereumSignature(serialized: signature) + let publicKey = try signer.recoverPubKey(signature: sig, message: message) + try verifyPublicKey(publicKey, address: address) + } + + private func verifyPublicKey(_ publicKey: Data, address: String) throws { + let recovered = "0x" + signer.keccak256(publicKey) + .suffix(20) + .toHexString() + + guard recovered == address.lowercased() else { + throw Errors.invalidSignature + } + } +} + +extension EIP191Verifier { + enum Errors: Error { + case invalidSignature + } +} diff --git a/Sources/Auth/Services/Signer/Ethereum/EthereumSignature.swift b/Sources/Auth/Services/Signer/Ethereum/EthereumSignature.swift new file mode 100644 index 000000000..e992023ff --- /dev/null +++ b/Sources/Auth/Services/Signer/Ethereum/EthereumSignature.swift @@ -0,0 +1,38 @@ +import Foundation + +public struct EthereumSignature { + public let v: UInt8 + public let r: [UInt8] + public let s: [UInt8] + + public init(v: UInt8, r: [UInt8], s: [UInt8]) { + self.v = v + self.r = r + self.s = s + } + + public init(serialized: Data) { + let bytes = [UInt8](serialized) + + var v = bytes[bytes.count-1] + if v >= 27 && v <= 30 { + v -= 27 + } else if v >= 31 && v <= 34 { + v -= 31 + } else if v >= 35 && v <= 38 { + v -= 35 + } + + self.v = v + self.r = [UInt8](bytes[0..<32]) + self.s = [UInt8](bytes[32..<64]) + } + + public var serialized: Data { + return Data(r + s + [UInt8(v + 27)]) + } + + public func hex() -> String { + return "0x" + serialized.toHexString() + } +} diff --git a/Sources/Auth/Services/Signer/Ethereum/EthereumSigner.swift b/Sources/Auth/Services/Signer/Ethereum/EthereumSigner.swift new file mode 100644 index 000000000..9865b04c3 --- /dev/null +++ b/Sources/Auth/Services/Signer/Ethereum/EthereumSigner.swift @@ -0,0 +1,7 @@ +import Foundation + +public protocol EthereumSigner { + func sign(message: Data, with key: Data) throws -> EthereumSignature + func recoverPubKey(signature: EthereumSignature, message: Data) throws -> Data + func keccak256(_ data: Data) -> Data +} diff --git a/Sources/Auth/Services/Signer/MessageSigner.swift b/Sources/Auth/Services/Signer/MessageSigner.swift index fd4ffffbb..7af8792a2 100644 --- a/Sources/Auth/Services/Signer/MessageSigner.swift +++ b/Sources/Auth/Services/Signer/MessageSigner.swift @@ -1,37 +1,63 @@ import Foundation public protocol MessageSignatureVerifying { - func verify(signature: CacaoSignature, message: String, address: String, chainId: String) async throws + func verify(signature: CacaoSignature, + message: String, + address: String, + chainId: String + ) async throws } public protocol MessageSigning { - func sign(message: String, privateKey: Data, type: CacaoSignatureType) throws -> CacaoSignature + func sign(payload: AuthPayload, + address: String, + privateKey: Data, + type: CacaoSignatureType + ) throws -> CacaoSignature } -struct MessageSigner: MessageSignatureVerifying, MessageSigning { +public typealias AuthMessageSigner = MessageSignatureVerifying & MessageSigning + +struct MessageSigner: AuthMessageSigner { enum Errors: Error { case utf8EncodingFailed } - private let signer: Signer + private let signer: EthereumSigner private let eip191Verifier: EIP191Verifier private let eip1271Verifier: EIP1271Verifier + private let messageFormatter: SIWEMessageFormatting - init(signer: Signer, eip191Verifier: EIP191Verifier, eip1271Verifier: EIP1271Verifier) { + init(signer: EthereumSigner, eip191Verifier: EIP191Verifier, eip1271Verifier: EIP1271Verifier, messageFormatter: SIWEMessageFormatting) { self.signer = signer self.eip191Verifier = eip191Verifier self.eip1271Verifier = eip1271Verifier + self.messageFormatter = messageFormatter } - func sign(message: String, privateKey: Data, type: CacaoSignatureType) throws -> CacaoSignature { - guard let messageData = message.data(using: .utf8) else { throw Errors.utf8EncodingFailed } + func sign(payload: AuthPayload, + address: String, + privateKey: Data, + type: CacaoSignatureType + ) throws -> CacaoSignature { + + let message = try messageFormatter.formatMessage(from: payload, address: address) + + guard let messageData = message.data(using: .utf8)else { + throw Errors.utf8EncodingFailed + } + let signature = try signer.sign(message: prefixed(messageData), with: privateKey) - let prefixedHexSignature = "0x" + signature.toHexString() - return CacaoSignature(t: type, s: prefixedHexSignature) + return CacaoSignature(t: type, s: signature.hex()) } - func verify(signature: CacaoSignature, message: String, address: String, chainId: String) async throws { + func verify(signature: CacaoSignature, + message: String, + address: String, + chainId: String + ) async throws { + guard let messageData = message.data(using: .utf8) else { throw Errors.utf8EncodingFailed } diff --git a/Sources/Auth/Services/Signer/MessageSignerFactory.swift b/Sources/Auth/Services/Signer/MessageSignerFactory.swift index 85dd7d780..88d56d374 100644 --- a/Sources/Auth/Services/Signer/MessageSignerFactory.swift +++ b/Sources/Auth/Services/Signer/MessageSignerFactory.swift @@ -2,18 +2,26 @@ import Foundation public struct MessageSignerFactory { - public static func create() -> MessageSigning & MessageSignatureVerifying { + public let signerFactory: SignerFactory + + public init(signerFactory: SignerFactory) { + self.signerFactory = signerFactory + } + + public func create() -> AuthMessageSigner { return create(projectId: Networking.projectId) } - static func create(projectId: String) -> MessageSigning & MessageSignatureVerifying { + func create(projectId: String) -> AuthMessageSigner { return MessageSigner( - signer: Signer(), - eip191Verifier: EIP191Verifier(), + signer: signerFactory.createEthereumSigner(), + eip191Verifier: EIP191Verifier(signer: signerFactory.createEthereumSigner()), eip1271Verifier: EIP1271Verifier( projectId: projectId, - httpClient: HTTPNetworkClient(host: "rpc.walletconnect.com") - ) + httpClient: HTTPNetworkClient(host: "rpc.walletconnect.com"), + signer: signerFactory.createEthereumSigner() + ), + messageFormatter: SIWEMessageFormatter() ) } } diff --git a/Sources/Auth/Services/Signer/Signer.swift b/Sources/Auth/Services/Signer/Signer.swift deleted file mode 100644 index cbd56ca89..000000000 --- a/Sources/Auth/Services/Signer/Signer.swift +++ /dev/null @@ -1,19 +0,0 @@ -import Foundation -import Web3 - -struct Signer { - - typealias Signature = (v: UInt, r: [UInt8], s: [UInt8]) - - init() {} - - func sign(message: Data, with key: Data) throws -> Data { - let privateKey = try EthereumPrivateKey(privateKey: key.bytes) - let signature = try privateKey.sign(message: message.bytes) - return serialized(signature: signature) - } - - private func serialized(signature: Signature) -> Data { - return Data(signature.r + signature.s + [UInt8(signature.v)]) - } -} diff --git a/Sources/Auth/Services/Signer/SignerFactory.swift b/Sources/Auth/Services/Signer/SignerFactory.swift new file mode 100644 index 000000000..8f26baf15 --- /dev/null +++ b/Sources/Auth/Services/Signer/SignerFactory.swift @@ -0,0 +1,5 @@ +import Foundation + +public protocol SignerFactory { + func createEthereumSigner() -> EthereumSigner +} diff --git a/Sources/Auth/Services/Wallet/PendingRequestsProvider.swift b/Sources/Auth/Services/Wallet/PendingRequestsProvider.swift index 35bb47265..4aa8c04c0 100644 --- a/Sources/Auth/Services/Wallet/PendingRequestsProvider.swift +++ b/Sources/Auth/Services/Wallet/PendingRequestsProvider.swift @@ -11,9 +11,8 @@ class PendingRequestsProvider { let pendingRequests: [AuthRequest] = rpcHistory.getPending() .filter {$0.request.method == "wc_authRequest"} .compactMap { - guard let params = try? $0.request.params?.get(AuthRequestParams.self), - let message = SIWEMessageFormatter().formatMessage(from: params.payloadParams, address: account.address) else {return nil} - return AuthRequest(id: $0.request.id!, message: message) + guard let params = try? $0.request.params?.get(AuthRequestParams.self) else { return nil } + return AuthRequest(id: $0.request.id!, payload: params.payloadParams) } return pendingRequests } diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index d2bb83ef2..b47f1cbf2 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -5,44 +5,34 @@ class WalletRequestSubscriber { private let networkingInteractor: NetworkInteracting private let logger: ConsoleLogging private let kms: KeyManagementServiceProtocol - private let address: String? private var publishers = [AnyCancellable]() - private let messageFormatter: SIWEMessageFormatting private let walletErrorResponder: WalletErrorResponder private let pairingRegisterer: PairingRegisterer var onRequest: ((AuthRequest) -> Void)? - init(networkingInteractor: NetworkInteracting, - logger: ConsoleLogging, - kms: KeyManagementServiceProtocol, - messageFormatter: SIWEMessageFormatting, - address: String?, - walletErrorResponder: WalletErrorResponder, - pairingRegisterer: PairingRegisterer) { + init( + networkingInteractor: NetworkInteracting, + logger: ConsoleLogging, + kms: KeyManagementServiceProtocol, + walletErrorResponder: WalletErrorResponder, + pairingRegisterer: PairingRegisterer) + { self.networkingInteractor = networkingInteractor self.logger = logger self.kms = kms - self.address = address - self.messageFormatter = messageFormatter self.walletErrorResponder = walletErrorResponder self.pairingRegisterer = pairingRegisterer subscribeForRequest() } private func subscribeForRequest() { - guard let address = address else { return } - pairingRegisterer.register(method: AuthRequestProtocolMethod()) .sink { [unowned self] (payload: RequestSubscriptionPayload) in logger.debug("WalletRequestSubscriber: Received request") - guard let message = messageFormatter.formatMessage(from: payload.request.payloadParams, address: address) else { - Task(priority: .high) { - try? await walletErrorResponder.respondError(AuthError.malformedRequestParams, requestId: payload.id) - } - return - } pairingRegisterer.activate(pairingTopic: payload.topic) - onRequest?(.init(id: payload.id, message: message)) + + let request = AuthRequest(id: payload.id, payload: payload.request.payloadParams) + onRequest?(request) }.store(in: &publishers) } } diff --git a/Sources/Auth/Types/AuthPayload.swift b/Sources/Auth/Types/AuthPayload.swift index 1329f5265..621df1ea3 100644 --- a/Sources/Auth/Types/AuthPayload.swift +++ b/Sources/Auth/Types/AuthPayload.swift @@ -1,18 +1,18 @@ import Foundation -struct AuthPayload: Codable, Equatable { - let domain: String - let aud: String - let version: String - let nonce: String - let chainId: String - let type: String - let iat: String - let nbf: String? - let exp: String? - let statement: String? - let requestId: String? - let resources: [String]? +public struct AuthPayload: Codable, Equatable { + public let domain: String + public let aud: String + public let version: String + public let nonce: String + public let chainId: String + public let type: 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]? init(requestParams: RequestParams, iat: String) { self.type = "eip4361" diff --git a/Sources/Auth/Types/Public/AuthRequest.swift b/Sources/Auth/Types/Public/AuthRequest.swift index f431ccb4d..b5095751c 100644 --- a/Sources/Auth/Types/Public/AuthRequest.swift +++ b/Sources/Auth/Types/Public/AuthRequest.swift @@ -2,6 +2,5 @@ import Foundation public struct AuthRequest: Equatable, Codable { public let id: RPCID - /// EIP-4361: Sign-In with Ethereum message - public let message: String + public let payload: AuthPayload } diff --git a/Sources/WalletConnectRelay/PackageConfig.json b/Sources/WalletConnectRelay/PackageConfig.json index 4a5b368a0..48f893bf2 100644 --- a/Sources/WalletConnectRelay/PackageConfig.json +++ b/Sources/WalletConnectRelay/PackageConfig.json @@ -1 +1 @@ -{"version": "1.0.6"} +{"version": "1.1.0"} diff --git a/Sources/WalletConnectUtils/Data+Extension.swift b/Sources/WalletConnectUtils/Data+Extension.swift deleted file mode 100644 index 88624109f..000000000 --- a/Sources/WalletConnectUtils/Data+Extension.swift +++ /dev/null @@ -1,56 +0,0 @@ -import Foundation - -// MARK: - Random data generation - -extension Data { - - public static func randomBytes(count: Int) -> Data { - var buffer = [UInt8](repeating: 0, count: count) - let status = SecRandomCopyBytes(kSecRandomDefault, count, &buffer) - guard status == errSecSuccess else { - fatalError("Failed to generate secure random data of size \(count).") - } - return Data(buffer) - } -} - -// MARK: - Hexadecimal string conversion - -extension Data { - - static func value(of nibble: UInt8) -> UInt8? { - guard let letter = String(bytes: [nibble], encoding: .ascii) else { return nil } - return UInt8(letter, radix: 16) - } - - public init(hex: String) { - var data = Data() - let string = hex.hasPrefix("0x") ? String(hex.dropFirst(2)) : hex - - // Convert the string to bytes for better performance - guard - let stringData = string.data(using: .ascii, allowLossyConversion: true) - else { - self = data - return - } - - let stringBytes = Array(stringData) - for idx in stride(from: 0, to: stringBytes.count, by: 2) { - guard let high = Data.value(of: stringBytes[idx]) else { - data.removeAll() - break - } - if idx < stringBytes.count - 1, let low = Data.value(of: stringBytes[idx + 1]) { - data.append((high << 4) | low) - } else { - data.append(high) - } - } - self = data - } - - public func toHexString() -> String { - return map({ String(format: "%02x", $0) }).joined() - } -} diff --git a/Sources/WalletConnectUtils/Extensions/Data.swift b/Sources/WalletConnectUtils/Extensions/Data.swift index 90236854f..88624109f 100644 --- a/Sources/WalletConnectUtils/Extensions/Data.swift +++ b/Sources/WalletConnectUtils/Extensions/Data.swift @@ -1,8 +1,56 @@ import Foundation -public extension UInt8 { - var data: Data { - var int = self - return Data(bytes: &int, count: MemoryLayout.size) +// MARK: - Random data generation + +extension Data { + + public static func randomBytes(count: Int) -> Data { + var buffer = [UInt8](repeating: 0, count: count) + let status = SecRandomCopyBytes(kSecRandomDefault, count, &buffer) + guard status == errSecSuccess else { + fatalError("Failed to generate secure random data of size \(count).") + } + return Data(buffer) + } +} + +// MARK: - Hexadecimal string conversion + +extension Data { + + static func value(of nibble: UInt8) -> UInt8? { + guard let letter = String(bytes: [nibble], encoding: .ascii) else { return nil } + return UInt8(letter, radix: 16) + } + + public init(hex: String) { + var data = Data() + let string = hex.hasPrefix("0x") ? String(hex.dropFirst(2)) : hex + + // Convert the string to bytes for better performance + guard + let stringData = string.data(using: .ascii, allowLossyConversion: true) + else { + self = data + return + } + + let stringBytes = Array(stringData) + for idx in stride(from: 0, to: stringBytes.count, by: 2) { + guard let high = Data.value(of: stringBytes[idx]) else { + data.removeAll() + break + } + if idx < stringBytes.count - 1, let low = Data.value(of: stringBytes[idx + 1]) { + data.append((high << 4) | low) + } else { + data.append(high) + } + } + self = data + } + + public func toHexString() -> String { + return map({ String(format: "%02x", $0) }).joined() } } diff --git a/Sources/WalletConnectUtils/Extensions/String+Extension.swift b/Sources/WalletConnectUtils/Extensions/String+CAIPs.swift similarity index 100% rename from Sources/WalletConnectUtils/Extensions/String+Extension.swift rename to Sources/WalletConnectUtils/Extensions/String+CAIPs.swift diff --git a/Sources/WalletConnectUtils/Extensions/UInt8.swift b/Sources/WalletConnectUtils/Extensions/UInt8.swift new file mode 100644 index 000000000..c147c544f --- /dev/null +++ b/Sources/WalletConnectUtils/Extensions/UInt8.swift @@ -0,0 +1,15 @@ +import Foundation + +public extension UInt8 { + var data: Data { + var int = self + return Data(bytes: &int, count: MemoryLayout.size) + } +} + +public extension Array where Element == UInt8 { + + func toHexString() -> String { + return Data(self).toHexString() + } +} diff --git a/Sources/WalletConnectVerify/AppAttestationRegistrer.swift b/Sources/WalletConnectVerify/AppAttestationRegistrer.swift new file mode 100644 index 000000000..04e7cd819 --- /dev/null +++ b/Sources/WalletConnectVerify/AppAttestationRegistrer.swift @@ -0,0 +1,49 @@ +import Foundation +import DeviceCheck +import WalletConnectUtils +import CryptoKit + + +@available(iOS 14.0, *) +@available(macOS 11.0, *) +class AppAttestationRegistrer { + private let logger: ConsoleLogging + private let keyIdStorage: CodableStore + + let attestKeyGenerator: AttestKeyGenerating + let attestChallengeProvider: AttestChallengeProviding + let keyAttestationService: KeyAttesting + + init(logger: ConsoleLogging, + keyIdStorage: CodableStore, + attestKeyGenerator: AttestKeyGenerating, + attestChallengeProvider: AttestChallengeProviding, + keyAttestationService: KeyAttesting + ) { + self.logger = logger + self.keyIdStorage = keyIdStorage + self.attestKeyGenerator = attestKeyGenerator + self.attestChallengeProvider = attestChallengeProvider + self.keyAttestationService = keyAttestationService + } + + func registerAttestationIfNeeded() async throws { + if let _ = try? keyIdStorage.get(key: Constants.keyIdStorageKey) { return } + let keyId = try await generateKeys() + let challenge = try await getChallenge() + let hash = Data(SHA256.hash(data: challenge)) + try await attestKey(keyId: keyId, clientDataHash: hash) + } + + private func generateKeys() async throws -> String { + try await attestKeyGenerator.generateKeys() + } + + private func getChallenge() async throws -> Data { + try await attestChallengeProvider.getChallenge() + } + + private func attestKey(keyId: String, clientDataHash: Data) async throws { + try await keyAttestationService.attestKey(keyId: keyId, clientDataHash: clientDataHash) + } +} diff --git a/Sources/WalletConnectVerify/AssertionRegistrer.swift b/Sources/WalletConnectVerify/AssertionRegistrer.swift new file mode 100644 index 000000000..2cf2289ad --- /dev/null +++ b/Sources/WalletConnectVerify/AssertionRegistrer.swift @@ -0,0 +1,6 @@ + +import Foundation + +class AssertionRegistrer { + func registerAssertion() async throws {} +} diff --git a/Sources/WalletConnectVerify/AttestChallengeProvider.swift b/Sources/WalletConnectVerify/AttestChallengeProvider.swift new file mode 100644 index 000000000..5d2891e67 --- /dev/null +++ b/Sources/WalletConnectVerify/AttestChallengeProvider.swift @@ -0,0 +1,12 @@ +import Foundation + +protocol AttestChallengeProviding { + func getChallenge() async throws -> Data +} + +class AttestChallengeProvider: AttestChallengeProviding { + func getChallenge() async throws -> Data { + return Data() + fatalError("not implemented") + } +} diff --git a/Sources/WalletConnectVerify/AttestKeyGenerator.swift b/Sources/WalletConnectVerify/AttestKeyGenerator.swift new file mode 100644 index 000000000..166d84a01 --- /dev/null +++ b/Sources/WalletConnectVerify/AttestKeyGenerator.swift @@ -0,0 +1,29 @@ +import DeviceCheck +import Foundation +import WalletConnectUtils + +protocol AttestKeyGenerating { + func generateKeys() async throws -> String +} + +@available(iOS 14.0, *) +@available(macOS 11.0, *) +class AttestKeyGenerator: AttestKeyGenerating { + private let service = DCAppAttestService.shared + private let logger: ConsoleLogging + private let keyIdStorage: CodableStore + + + init(logger: ConsoleLogging, + keyIdStorage: CodableStore + ) { + self.logger = logger + self.keyIdStorage = keyIdStorage + } + + func generateKeys() async throws -> String { + let keyId = try await service.generateKey() + keyIdStorage.set(keyId, forKey: Constants.keyIdStorageKey) + return keyId + } +} diff --git a/Sources/WalletConnectVerify/Constants.swift b/Sources/WalletConnectVerify/Constants.swift new file mode 100644 index 000000000..47e31f2ab --- /dev/null +++ b/Sources/WalletConnectVerify/Constants.swift @@ -0,0 +1,5 @@ +import Foundation + +enum Constants { + static let keyIdStorageKey = "attested_key_id" +} diff --git a/Sources/WalletConnectVerify/KeyAttestationService.swift b/Sources/WalletConnectVerify/KeyAttestationService.swift new file mode 100644 index 000000000..ce0833d81 --- /dev/null +++ b/Sources/WalletConnectVerify/KeyAttestationService.swift @@ -0,0 +1,26 @@ +import Foundation +import DeviceCheck + +protocol KeyAttesting { + func attestKey(keyId: String, clientDataHash: Data) async throws +} + +@available(iOS 14.0, *) +@available(macOS 11.0, *) +class KeyAttestationService: KeyAttesting { + private let service = DCAppAttestService.shared + // If the method, which accesses a remote Apple server, returns the serverUnavailable error, + // try attestation again later with the same key. For any other error, + //discard the key identifier and create a new key when you want to try again. + //Otherwise, send the completion handler’s attestation object and the keyId to your server for processing. + func attestKey(keyId: String, clientDataHash: Data) async throws { + + try await service.attestKey(keyId, clientDataHash: clientDataHash) + //TODO - Send the attestation object to your server for verification. handle errors + + } + + private func sendAttestationToVerifyServer() async throws { + + } +} diff --git a/Sources/WalletConnectVerify/OriginVerifier.swift b/Sources/WalletConnectVerify/OriginVerifier.swift new file mode 100644 index 000000000..de33c5fff --- /dev/null +++ b/Sources/WalletConnectVerify/OriginVerifier.swift @@ -0,0 +1,7 @@ + +import Foundation + +class OriginVerifier { + func verifyOrigin() async throws {} +} + diff --git a/Sources/WalletConnectVerify/VerifyClient.swift b/Sources/WalletConnectVerify/VerifyClient.swift new file mode 100644 index 000000000..0936d9da0 --- /dev/null +++ b/Sources/WalletConnectVerify/VerifyClient.swift @@ -0,0 +1,38 @@ +import DeviceCheck +import Foundation +import WalletConnectUtils + +@available(iOS 14.0, *) +@available(macOS 11.0, *) +public actor VerifyClient { + enum Errors: Error { + case attestationNotSupported + } + let originVerifier: OriginVerifier + let assertionRegistrer: AssertionRegistrer + let appAttestationRegistrer: AppAttestationRegistrer + + init(originVerifier: OriginVerifier, + assertionRegistrer: AssertionRegistrer, + appAttestationRegistrer: AppAttestationRegistrer) throws { + if !DCAppAttestService.shared.isSupported { + throw Errors.attestationNotSupported + } + self.originVerifier = originVerifier + self.assertionRegistrer = assertionRegistrer + self.appAttestationRegistrer = appAttestationRegistrer + } + + public func registerAttestationIfNeeded() async throws { + try await appAttestationRegistrer.registerAttestationIfNeeded() + } + + public func verifyOrigin() async throws { + try await originVerifier.verifyOrigin() + } + + public func registerAssertion() async throws { + try await assertionRegistrer.registerAssertion() + } + +} diff --git a/Sources/WalletConnectVerify/VerifyClientFactory.swift b/Sources/WalletConnectVerify/VerifyClientFactory.swift new file mode 100644 index 000000000..7554a3189 --- /dev/null +++ b/Sources/WalletConnectVerify/VerifyClientFactory.swift @@ -0,0 +1,24 @@ +import Foundation +import WalletConnectUtils + +@available(iOS 14.0, *) +@available(macOS 11.0, *) +public class VerifyClientFactory { + + public static func create() throws -> VerifyClient { + let originVerifier = OriginVerifier() + let assertionRegistrer = AssertionRegistrer() + let logger = ConsoleLogger(loggingLevel: .off) + let keyValueStorage = UserDefaults.standard + let keyIdStorage = CodableStore(defaults: keyValueStorage, identifier: VerifyStorageIdentifier.keyId) + let attestKeyGenerator = AttestKeyGenerator(logger: logger, keyIdStorage: keyIdStorage) + let attestChallengeProvider = AttestChallengeProvider() + let keyAttestationService = KeyAttestationService() + let appAttestationRegistrer = AppAttestationRegistrer(logger: logger, + keyIdStorage: keyIdStorage, + attestKeyGenerator: attestKeyGenerator, + attestChallengeProvider: attestChallengeProvider, + keyAttestationService: keyAttestationService) + return try VerifyClient(originVerifier: originVerifier, assertionRegistrer: assertionRegistrer, appAttestationRegistrer: appAttestationRegistrer) + } +} diff --git a/Sources/WalletConnectVerify/VerifyStorageIdentifier.swift b/Sources/WalletConnectVerify/VerifyStorageIdentifier.swift new file mode 100644 index 000000000..18b6484dd --- /dev/null +++ b/Sources/WalletConnectVerify/VerifyStorageIdentifier.swift @@ -0,0 +1,5 @@ +import Foundation + +enum VerifyStorageIdentifier { + static let keyId = "com.walletconnect.sdk.keyId" +} diff --git a/Tests/AuthTests/AppRespondSubscriberTests.swift b/Tests/AuthTests/AppRespondSubscriberTests.swift index 07492323e..72f3f4f51 100644 --- a/Tests/AuthTests/AppRespondSubscriberTests.swift +++ b/Tests/AuthTests/AppRespondSubscriberTests.swift @@ -8,21 +8,20 @@ import XCTest import JSONRPC class AppRespondSubscriberTests: XCTestCase { + var networkingInteractor: NetworkingInteractorMock! var sut: AppRespondSubscriber! var messageFormatter: SIWEMessageFormatter! var rpcHistory: RPCHistory! let defaultTimeout: TimeInterval = 0.01 - let walletAccount = Account(chainIdentifier: "eip155:1", address: "0x724d0D2DaD3fbB0C168f947B87Fa5DBe36F1A8bf")! - let prvKey = Data(hex: "462c1dad6832d7d96ccf87bd6a686a4110e114aaaebd5512e552c0e3a87b480f") - var messageSigner: (MessageSigning & MessageSignatureVerifying)! + var messageSigner: AuthMessageSigner! var pairingStorage: WCPairingStorageMock! var pairingRegisterer: PairingRegistererMock! override func setUp() { networkingInteractor = NetworkingInteractorMock() messageFormatter = SIWEMessageFormatter() - messageSigner = MessageSignerFactory.create(projectId: "project-id") + messageSigner = MessageSignerMock() rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: RuntimeKeyValueStorage()) pairingStorage = WCPairingStorageMock() pairingRegisterer = PairingRegistererMock() @@ -41,6 +40,12 @@ class AppRespondSubscriberTests: XCTestCase { // set history record for a request let topic = "topic" let requestId: RPCID = RPCID(1234) + + let params = AuthRequestParams.stub() + let compromissedParams = AuthRequestParams.stub(nonce: "Compromissed nonce") + + XCTAssertNotEqual(params.payloadParams, compromissedParams.payloadParams) + let request = RPCRequest(method: "wc_authRequest", params: AuthRequestParams.stub(), id: requestId.right!) try! rpcHistory.set(request, forTopic: topic, emmitedBy: .local) @@ -53,13 +58,12 @@ class AppRespondSubscriberTests: XCTestCase { } // subscribe on compromised cacao - let header = CacaoHeader(t: "eip4361") - let payload = CacaoPayload(params: AuthPayload.stub(nonce: "compromised nonce"), didpkh: DIDPKH(account: walletAccount)) - - let message = try! messageFormatter.formatMessage(from: payload) - let cacaoSignature = try! messageSigner.sign(message: message, privateKey: prvKey, type: .eip191) + let account = Account(chainIdentifier: "eip155:1", address: "0x724d0D2DaD3fbB0C168f947B87Fa5DBe36F1A8bf")! + let cacaoHeader = CacaoHeader(t: "eip4361") + let cacaoPayload = CacaoPayload(params: compromissedParams.payloadParams, didpkh: DIDPKH(account: account)) + let cacaoSignature = CacaoSignature(t: .eip191, s: "") - let cacao = Cacao(h: header, p: payload, s: cacaoSignature) + let cacao = Cacao(h: cacaoHeader, p: cacaoPayload, s: cacaoSignature) let response = RPCResponse(id: requestId, result: cacao) networkingInteractor.responsePublisherSubject.send((topic, request, response)) diff --git a/Tests/AuthTests/EIP1271VerifierTests.swift b/Tests/AuthTests/EIP1271VerifierTests.swift deleted file mode 100644 index 796c146ff..000000000 --- a/Tests/AuthTests/EIP1271VerifierTests.swift +++ /dev/null @@ -1,38 +0,0 @@ -import Foundation -import XCTest -@testable import Auth -import JSONRPC -import TestingUtils - -class EIP1271VerifierTests: XCTestCase { - - let signature = Data(hex: "c1505719b2504095116db01baaf276361efd3a73c28cf8cc28dabefa945b8d536011289ac0a3b048600c1e692ff173ca944246cf7ceb319ac2262d27b395c82b1c") - let message = Data(hex: "3aaa8393796c7388e4e062861d8238503de7584c977676fe9d8d551c30e11f84") - let address = "0x2faf83c542b68f1b4cdc0e770e8cb9f567b08f71" - let chainId = "eip155:1" - - func testSuccessVerify() async throws { - let response = RPCResponse(id: "1", result: "0x1626ba7e00000000000000000000000000000000000000000000000000000000") - let httpClient = HTTPClientMock(object: response) - let verifier = EIP1271Verifier(projectId: "project-id", httpClient: httpClient) - try await verifier.verify( - signature: signature, - message: message, - address: address, - chainId: chainId - ) - } - - func testFailureVerify() async throws { - let response = RPCResponse(id: "1", error: .internalError) - let httpClient = HTTPClientMock(object: response) - let verifier = EIP1271Verifier(projectId: "project-id", httpClient: httpClient) - - await XCTAssertThrowsErrorAsync(try await verifier.verify( - signature: signature, - message: message, - address: address, - chainId: chainId - )) - } -} diff --git a/Tests/AuthTests/Mocks/SIWEMessageFormatterMock.swift b/Tests/AuthTests/Mocks/SIWEMessageFormatterMock.swift index e15d682be..64adc7c96 100644 --- a/Tests/AuthTests/Mocks/SIWEMessageFormatterMock.swift +++ b/Tests/AuthTests/Mocks/SIWEMessageFormatterMock.swift @@ -4,7 +4,7 @@ import Foundation class SIWEMessageFormatterMock: SIWEMessageFormatting { var formattedMessage: String! - func formatMessage(from authPayload: AuthPayload, address: String) -> String? { + func formatMessage(from authPayload: AuthPayload, address: String) throws -> String { return formattedMessage } diff --git a/Tests/AuthTests/Mocks/SignerFactoryMock.swift b/Tests/AuthTests/Mocks/SignerFactoryMock.swift new file mode 100644 index 000000000..f8110b951 --- /dev/null +++ b/Tests/AuthTests/Mocks/SignerFactoryMock.swift @@ -0,0 +1,24 @@ +import Foundation +import Auth + +struct SignerFactoryMock: SignerFactory { + + func createEthereumSigner() -> EthereumSigner { + return EthereumSignerMock() + } +} + +struct EthereumSignerMock: EthereumSigner { + + func sign(message: Data, with key: Data) throws -> EthereumSignature { + return EthereumSignature(v: 0, r: [], s: []) + } + + func recoverPubKey(signature: EthereumSignature, message: Data) throws -> Data { + return Data() + } + + func keccak256(_ data: Data) -> Data { + return Data() + } +} diff --git a/Tests/AuthTests/SIWEMessageFormatterTests.swift b/Tests/AuthTests/SIWEMessageFormatterTests.swift index 1b14c21a4..859b2cf41 100644 --- a/Tests/AuthTests/SIWEMessageFormatterTests.swift +++ b/Tests/AuthTests/SIWEMessageFormatterTests.swift @@ -10,7 +10,7 @@ class SIWEMessageFormatterTests: XCTestCase { sut = SIWEMessageFormatter() } - func testFormatMessage() { + func testFormatMessage() throws { let expectedMessage = """ service.invalid wants you to sign in with your Ethereum account: @@ -27,11 +27,11 @@ class SIWEMessageFormatterTests: XCTestCase { - ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/ - https://example.com/my-web2-claim.json """ - let message = sut.formatMessage(from: AuthPayload.stub(), address: address) + let message = try sut.formatMessage(from: AuthPayload.stub(), address: address) XCTAssertEqual(message, expectedMessage) } - func testNilStatement() { + func testNilStatement() throws { let expectedMessage = """ service.invalid wants you to sign in with your Ethereum account: @@ -46,14 +46,14 @@ class SIWEMessageFormatterTests: XCTestCase { - ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/ - https://example.com/my-web2-claim.json """ - let message = sut.formatMessage( + let message = try sut.formatMessage( from: AuthPayload.stub( requestParams: RequestParams.stub(statement: nil)), address: address) XCTAssertEqual(message, expectedMessage) } - func testNilResources() { + func testNilResources() throws { let expectedMessage = """ service.invalid wants you to sign in with your Ethereum account: @@ -67,14 +67,14 @@ class SIWEMessageFormatterTests: XCTestCase { Nonce: 32891756 Issued At: 2021-09-30T16:25:24Z """ - let message = sut.formatMessage( + let message = try sut.formatMessage( from: AuthPayload.stub( requestParams: RequestParams.stub(resources: nil)), address: address) XCTAssertEqual(message, expectedMessage) } - func testNilAllOptionalParams() { + func testNilAllOptionalParams() throws { let expectedMessage = """ service.invalid wants you to sign in with your Ethereum account: @@ -86,7 +86,7 @@ class SIWEMessageFormatterTests: XCTestCase { Nonce: 32891756 Issued At: 2021-09-30T16:25:24Z """ - let message = sut.formatMessage( + let message = try sut.formatMessage( from: AuthPayload.stub( requestParams: RequestParams.stub(statement: nil, resources: nil)), diff --git a/Tests/AuthTests/Stubs/MessageSignerMock.swift b/Tests/AuthTests/Stubs/MessageSignerMock.swift new file mode 100644 index 000000000..4275cd1b9 --- /dev/null +++ b/Tests/AuthTests/Stubs/MessageSignerMock.swift @@ -0,0 +1,21 @@ +import Foundation +import Auth + +struct MessageSignerMock: AuthMessageSigner { + + func verify(signature: CacaoSignature, + message: String, + address: String, + chainId: String + ) async throws { + + } + + func sign(payload: AuthPayload, + address: String, + privateKey: Data, + type: CacaoSignatureType + ) throws -> CacaoSignature { + return CacaoSignature(t: .eip191, s: "") + } +} diff --git a/Tests/AuthTests/Stubs/RequestSubscriptionPayload.swift b/Tests/AuthTests/Stubs/RequestSubscriptionPayload.swift index 7944b241b..baf9546bf 100644 --- a/Tests/AuthTests/Stubs/RequestSubscriptionPayload.swift +++ b/Tests/AuthTests/Stubs/RequestSubscriptionPayload.swift @@ -4,11 +4,10 @@ import WalletConnectNetworking @testable import Auth extension AuthRequestParams { - static func stub(id: RPCID) -> AuthRequestParams { + static func stub(id: RPCID, iat: String) -> AuthRequestParams { let appMetadata = AppMetadata(name: "", description: "", url: "", icons: []) let requester = AuthRequestParams.Requester(publicKey: "", metadata: appMetadata) - let issueAt = ISO8601DateFormatter().string(from: Date()) - let payload = AuthPayload(requestParams: RequestParams.stub(), iat: issueAt) + let payload = AuthPayload(requestParams: RequestParams.stub(), iat: iat) return AuthRequestParams(requester: requester, payloadParams: payload) } } diff --git a/Tests/AuthTests/WalletRequestSubscriberTests.swift b/Tests/AuthTests/WalletRequestSubscriberTests.swift index de7dbe28d..d94567432 100644 --- a/Tests/AuthTests/WalletRequestSubscriberTests.swift +++ b/Tests/AuthTests/WalletRequestSubscriberTests.swift @@ -23,31 +23,31 @@ class WalletRequestSubscriberTests: XCTestCase { sut = WalletRequestSubscriber(networkingInteractor: networkingInteractor, logger: ConsoleLoggerMock(), kms: KeyManagementServiceMock(), - messageFormatter: messageFormatter, address: "", walletErrorResponder: walletErrorResponder, pairingRegisterer: pairingRegisterer) } func testSubscribeRequest() { - let expectedMessage = "Expected Message" + let iat = ISO8601DateFormatter().string(from: Date()) + let expectedPayload = AuthPayload(requestParams: .stub(), iat: iat) let expectedRequestId: RPCID = RPCID(1234) let messageExpectation = expectation(description: "receives formatted message") - messageFormatter.formattedMessage = expectedMessage - var messageId: RPCID! - var message: String! + + var requestId: RPCID! + var requestPayload: AuthPayload! sut.onRequest = { request in - messageId = request.id - message = request.message + requestId = request.id + requestPayload = request.payload messageExpectation.fulfill() } - let payload = RequestSubscriptionPayload(id: expectedRequestId, topic: "123", request: AuthRequestParams.stub(id: expectedRequestId)) + let payload = RequestSubscriptionPayload(id: expectedRequestId, topic: "123", request: AuthRequestParams.stub(id: expectedRequestId, iat: iat)) pairingRegisterer.subject.send(payload) wait(for: [messageExpectation], timeout: defaultTimeout) XCTAssertTrue(pairingRegisterer.isActivateCalled) - XCTAssertEqual(message, expectedMessage) - XCTAssertEqual(messageId, expectedRequestId) + XCTAssertEqual(requestPayload, expectedPayload) + XCTAssertEqual(requestId, expectedRequestId) } } diff --git a/Tests/VerifyTests/AppAttestationRegistrerTests.swift b/Tests/VerifyTests/AppAttestationRegistrerTests.swift new file mode 100644 index 000000000..e1e81bc40 --- /dev/null +++ b/Tests/VerifyTests/AppAttestationRegistrerTests.swift @@ -0,0 +1,47 @@ + +import Foundation +import XCTest +import WalletConnectUtils +import TestingUtils +@testable import WalletConnectVerify + +@available(iOS 14.0, *) +@available(macOS 11.0, *) +class AppAttestationRegistrerTests: XCTestCase { + var attestKeyGenerator: AttestKeyGeneratingMock! + var attestChallengeProvider: AttestChallengeProvidingMock! + var keyAttestationService: KeyAttestingMock! + var sut: AppAttestationRegistrer! + var keyIdStorage: CodableStore! + + override func setUp() { + let kvStorage = RuntimeKeyValueStorage() + + keyIdStorage = CodableStore(defaults: kvStorage, identifier: "") + attestKeyGenerator = AttestKeyGeneratingMock() + attestChallengeProvider = AttestChallengeProvidingMock() + keyAttestationService = KeyAttestingMock() + + sut = AppAttestationRegistrer( + logger: ConsoleLoggerMock(), + keyIdStorage: keyIdStorage, + attestKeyGenerator: attestKeyGenerator, + attestChallengeProvider: attestChallengeProvider, + keyAttestationService: keyAttestationService) + } + + func testAttestation() async { + try! await sut.registerAttestationIfNeeded() + XCTAssertTrue(attestKeyGenerator.keysGenerated) + XCTAssertTrue(attestChallengeProvider.challengeProvided) + XCTAssertTrue(keyAttestationService.keyAttested) + } + + func testAttestationAlreadyRegistered() async { + keyIdStorage.set("123", forKey: "attested_key_id") + try! await sut.registerAttestationIfNeeded() + XCTAssertFalse(attestKeyGenerator.keysGenerated) + XCTAssertFalse(attestChallengeProvider.challengeProvided) + XCTAssertFalse(keyAttestationService.keyAttested) + } +} diff --git a/Tests/VerifyTests/Mocks/AttestChallengeProvidingMock.swift b/Tests/VerifyTests/Mocks/AttestChallengeProvidingMock.swift new file mode 100644 index 000000000..1bc958055 --- /dev/null +++ b/Tests/VerifyTests/Mocks/AttestChallengeProvidingMock.swift @@ -0,0 +1,11 @@ + +import Foundation +@testable import WalletConnectVerify + +class AttestChallengeProvidingMock: AttestChallengeProviding { + var challengeProvided = false + func getChallenge() async throws -> Data { + challengeProvided = true + return Data() + } +} diff --git a/Tests/VerifyTests/Mocks/AttestKeyGeneratingMock.swift b/Tests/VerifyTests/Mocks/AttestKeyGeneratingMock.swift new file mode 100644 index 000000000..a44916d56 --- /dev/null +++ b/Tests/VerifyTests/Mocks/AttestKeyGeneratingMock.swift @@ -0,0 +1,12 @@ + +import Foundation +@testable import WalletConnectVerify + + +class AttestKeyGeneratingMock: AttestKeyGenerating { + var keysGenerated = false + func generateKeys() async throws -> String { + keysGenerated = true + return "" + } +} diff --git a/Tests/VerifyTests/Mocks/KeyAttestingMock.swift b/Tests/VerifyTests/Mocks/KeyAttestingMock.swift new file mode 100644 index 000000000..d68057125 --- /dev/null +++ b/Tests/VerifyTests/Mocks/KeyAttestingMock.swift @@ -0,0 +1,9 @@ +@testable import WalletConnectVerify +import Foundation + +class KeyAttestingMock: KeyAttesting { + var keyAttested = false + func attestKey(keyId: String, clientDataHash: Data) async throws { + keyAttested = true + } +} diff --git a/WalletConnectSwiftV2.podspec b/WalletConnectSwiftV2.podspec index 256509ece..881bfe09c 100644 --- a/WalletConnectSwiftV2.podspec +++ b/WalletConnectSwiftV2.podspec @@ -82,7 +82,6 @@ Pod::Spec.new do |spec| spec.subspec 'WalletConnectAuth' do |ss| ss.source_files = 'Sources/Auth/**/*' ss.dependency 'WalletConnectSwiftV2/WalletConnectPairing' - ss.dependency "WalletConnectWeb3", "1.0.1" end spec.subspec 'WalletConnectChat' do |ss|