diff --git a/.github/workflows/primitives.yml b/.github/workflows/primitives.yml new file mode 100644 index 00000000..49ab7783 --- /dev/null +++ b/.github/workflows/primitives.yml @@ -0,0 +1,41 @@ +name: primitives + +on: + push: + branches: + - main + pull_request: + paths: + - primitives/** + +defaults: + run: + working-directory: ./primitives + +jobs: + check: + strategy: + fail-fast: true + + name: Foundry project + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - name: Run Forge build + run: | + forge --version + forge build --sizes + id: build + + - name: Run Forge tests + run: | + forge test -vvv + id: test \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 49eaa904..88d2ab4c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,3 +22,6 @@ [submodule "account-integrations/kernel/lib/kernel"] path = account-integrations/kernel/lib/kernel url = https://github.com/zerodevapp/kernel +[submodule "primitives/lib/forge-std"] + path = primitives/lib/forge-std + url = https://github.com/foundry-rs/forge-std diff --git a/account-integrations/safe/foundry.toml b/account-integrations/safe/foundry.toml index e883058f..018343db 100644 --- a/account-integrations/safe/foundry.toml +++ b/account-integrations/safe/foundry.toml @@ -3,4 +3,6 @@ src = "src" out = "out" libs = ["lib"] +allow_paths = ["../../primitives"] + # See more config options https://github.com/foundry-rs/foundry/tree/master/config diff --git a/account-integrations/safe/lib/primitives b/account-integrations/safe/lib/primitives new file mode 120000 index 00000000..4c5283d1 --- /dev/null +++ b/account-integrations/safe/lib/primitives @@ -0,0 +1 @@ +../../../primitives \ No newline at end of file diff --git a/account-integrations/safe/remappings.txt b/account-integrations/safe/remappings.txt index 40608091..bb5480fb 100644 --- a/account-integrations/safe/remappings.txt +++ b/account-integrations/safe/remappings.txt @@ -3,4 +3,5 @@ forge-std/=lib/forge-std/src/ openzeppelin-contracts/=lib/openzeppelin-contracts/ @openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ account-abstraction/=lib/account-abstraction/ -safe-contracts/=lib/safe-contracts/ \ No newline at end of file +safe-contracts/=lib/safe-contracts/ +wax/primitives/=lib/primitives/ \ No newline at end of file diff --git a/account-integrations/safe/src/SafeWebAuthnPlugin.sol b/account-integrations/safe/src/SafeWebAuthnPlugin.sol index bd018546..56c6e0b4 100644 --- a/account-integrations/safe/src/SafeWebAuthnPlugin.sol +++ b/account-integrations/safe/src/SafeWebAuthnPlugin.sol @@ -3,8 +3,7 @@ pragma solidity >=0.8.0 <0.9.0; import {BaseAccount} from "account-abstraction/contracts/core/BaseAccount.sol"; import {IEntryPoint, UserOperation} from "account-abstraction/contracts/interfaces/IEntryPoint.sol"; - -import {FCL_WebAuthn} from "./lib/FCL_Webauthn.sol"; +import {WebAuthn} from "wax/primitives/src/WebAuthn.sol"; interface ISafe { function enableModule(address module) external; @@ -17,7 +16,7 @@ interface ISafe { ) external returns (bool success); } -contract SafeWebAuthnPlugin is BaseAccount { +contract SafeWebAuthnPlugin is BaseAccount, WebAuthn { address public immutable myAddress; address private immutable _entryPoint; uint256[2] private _publicKey; @@ -145,7 +144,7 @@ contract SafeWebAuthnPlugin is BaseAccount { i += ((dataLen >> 5) + 1) << 5; // advance index (round up to next slot) } - bool verified = FCL_WebAuthn.checkSignature( + bool verified = verifySignature( authenticatorData, s.authenticatorDataFlagMask, clientData, diff --git a/account-integrations/safe/src/utils/TypechainTypesHelper.sol b/account-integrations/safe/src/utils/TypechainTypesHelper.sol index 1914d2b5..b7d7cff3 100644 --- a/account-integrations/safe/src/utils/TypechainTypesHelper.sol +++ b/account-integrations/safe/src/utils/TypechainTypesHelper.sol @@ -1,7 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.7.0 <0.9.0; -/** Helper file to import Safe contracts so that hardhat can generate required typechain types for integration tests */ +/** + * Helper file to import contracts so that hardhat can generate required typechain types + */ import {SimulateTxAccessor} from "safe-contracts/contracts/accessors/SimulateTxAccessor.sol"; import {SafeProxyFactory} from "safe-contracts/contracts/proxies/SafeProxyFactory.sol"; import {TokenCallbackHandler} from "safe-contracts/contracts/handler/TokenCallbackHandler.sol"; @@ -13,3 +15,4 @@ import {SignMessageLib} from "safe-contracts/contracts/libraries/SignMessageLib. import {SafeL2} from "safe-contracts/contracts/SafeL2.sol"; import {Safe} from "safe-contracts/contracts/Safe.sol"; import {EntryPoint} from "account-abstraction/contracts/core/EntryPoint.sol"; +import {WebAuthn} from "wax/primitives/src/WebAuthn.sol"; diff --git a/account-integrations/safe/test/forge/utils/TestHelper.sol b/account-integrations/safe/test/forge/utils/TestHelper.sol index f37d0072..dc1cdc81 100644 --- a/account-integrations/safe/test/forge/utils/TestHelper.sol +++ b/account-integrations/safe/test/forge/utils/TestHelper.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.12; import "forge-std/Test.sol"; import {EntryPoint, UserOperation} from "account-abstraction/contracts/core/EntryPoint.sol"; -import {Webauthn} from "../../../src/WebAuthn.sol"; /* solhint-disable private-vars-leading-underscore */ diff --git a/primitives/.gitignore b/primitives/.gitignore new file mode 100644 index 00000000..85198aaa --- /dev/null +++ b/primitives/.gitignore @@ -0,0 +1,14 @@ +# Compiler files +cache/ +out/ + +# Ignores development broadcast logs +!/broadcast +/broadcast/*/31337/ +/broadcast/**/dry-run/ + +# Docs +docs/ + +# Dotenv file +.env diff --git a/primitives/README.md b/primitives/README.md index 3ebd8ca8..5f4d59db 100644 --- a/primitives/README.md +++ b/primitives/README.md @@ -1,3 +1,4 @@ # Cryptographic Primitives + A place to integrate primitives into a modular verification components, and test in isolation. After successful development here, the primitive can be integrated into smart accounts. diff --git a/primitives/foundry.toml b/primitives/foundry.toml new file mode 100644 index 00000000..25b918f9 --- /dev/null +++ b/primitives/foundry.toml @@ -0,0 +1,6 @@ +[profile.default] +src = "src" +out = "out" +libs = ["lib"] + +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/primitives/lib/forge-std b/primitives/lib/forge-std new file mode 160000 index 00000000..74cfb77e --- /dev/null +++ b/primitives/lib/forge-std @@ -0,0 +1 @@ +Subproject commit 74cfb77e308dd188d2f58864aaf44963ae6b88b1 diff --git a/account-integrations/safe/src/WebAuthn.sol b/primitives/src/WebAuthn.sol similarity index 81% rename from account-integrations/safe/src/WebAuthn.sol rename to primitives/src/WebAuthn.sol index 47970133..4741dbc7 100644 --- a/account-integrations/safe/src/WebAuthn.sol +++ b/primitives/src/WebAuthn.sol @@ -3,22 +3,22 @@ pragma solidity ^0.8.12; import {FCL_WebAuthn} from "./lib/FCL_Webauthn.sol"; -contract Webauthn { +contract WebAuthn { function verifySignature( bytes calldata authenticatorData, bytes1 authenticatorDataFlagMask, bytes calldata clientData, - bytes32 messageHash, + bytes32 clientChallenge, uint256 clientChallengeDataOffset, uint256[2] calldata signature, uint256[2] calldata publicKey - ) external returns (bool) { - return + ) internal returns (bool verified) { + verified = FCL_WebAuthn.checkSignature( authenticatorData, authenticatorDataFlagMask, clientData, - messageHash, + clientChallenge, clientChallengeDataOffset, signature, publicKey diff --git a/account-integrations/safe/src/lib/Base64URL.sol b/primitives/src/lib/Base64URL.sol similarity index 100% rename from account-integrations/safe/src/lib/Base64URL.sol rename to primitives/src/lib/Base64URL.sol diff --git a/account-integrations/safe/src/lib/FCL_Webauthn.sol b/primitives/src/lib/FCL_Webauthn.sol similarity index 100% rename from account-integrations/safe/src/lib/FCL_Webauthn.sol rename to primitives/src/lib/FCL_Webauthn.sol diff --git a/account-integrations/safe/src/lib/FCL_elliptic.sol b/primitives/src/lib/FCL_elliptic.sol similarity index 100% rename from account-integrations/safe/src/lib/FCL_elliptic.sol rename to primitives/src/lib/FCL_elliptic.sol diff --git a/account-integrations/safe/test/forge/WebAuthn.t.sol b/primitives/test/WebAuthn.t.sol similarity index 84% rename from account-integrations/safe/test/forge/WebAuthn.t.sol rename to primitives/test/WebAuthn.t.sol index 28040b11..47b0c987 100644 --- a/account-integrations/safe/test/forge/WebAuthn.t.sol +++ b/primitives/test/WebAuthn.t.sol @@ -3,17 +3,17 @@ pragma solidity ^0.8.12; import "forge-std/Test.sol"; import {TestHelper} from "./utils/TestHelper.sol"; -import {Webauthn} from "../../src/WebAuthn.sol"; +import {WebAuthnHarness} from "./utils/WebAuthnHarness.sol"; /* solhint-disable func-name-mixedcase */ contract WebauthnTest is TestHelper { constructor() TestHelper() {} - Webauthn public webauthn; + WebAuthnHarness public webauthn; function setUp() public { - webauthn = new Webauthn(); + webauthn = new WebAuthnHarness(); } function test_verifySignature_ValidSignature() public { @@ -29,7 +29,7 @@ contract WebauthnTest is TestHelper { uint256[2] memory publicKey = getWebAuthnPublicKey(); // Act - bool verified = webauthn.verifySignature( + bool verified = webauthn.exposed_verifySignature( authenticatorData, authenticatorDataFlagMask, clientData, diff --git a/primitives/test/utils/TestHelper.sol b/primitives/test/utils/TestHelper.sol new file mode 100644 index 00000000..498fcded --- /dev/null +++ b/primitives/test/utils/TestHelper.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import "forge-std/Test.sol"; +import {WebAuthn} from "../../src/WebAuthn.sol"; + +/* solhint-disable private-vars-leading-underscore */ + +abstract contract TestHelper is Test { + address internal constant ALICE = address(1); + address internal constant BOB = address(2); + address[] internal testAccounts = [ALICE, BOB]; + + constructor() {} + + function getWebAuthnPublicKey() internal pure returns (uint256[2] memory publicKey) { + publicKey = [ + 114874632398302156264159990279427641021947882640101801130664833947273521181002, + 32136952818958550240756825111900051564117520891182470183735244184006536587423 + ]; + } + + function getWebAuthnSignatureValues() + internal + pure + returns ( + bytes memory authenticatorData, + bytes1 authenticatorDataFlagMask, + bytes memory clientData, + bytes32 clientChallenge, + uint256 clientChallengeDataOffset, + uint256[2] memory signature + ) + { + authenticatorData = hex"f8e4b678e1c62f7355266eaa4dc1148573440937063a46d848da1e25babbd20b010000004d"; + authenticatorDataFlagMask = 0x01; + clientData = + hex"7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a224e546f2d3161424547526e78786a6d6b61544865687972444e5833697a6c7169316f776d4f643955474a30222c226f726967696e223a2268747470733a2f2f66726573682e6c65646765722e636f6d222c2263726f73734f726967696e223a66616c73657d"; + clientChallenge = hex"353a3ed5a0441919f1c639a46931de872ac3357de2ce5aa2d68c2639df54189d"; + clientChallengeDataOffset = 36; + signature = [ + 45847212378479006099766816358861726414873720355505495069909394794949093093607, + 55835259151215769394881684156457977412783812617123006733908193526332337539398 + ]; + } +} diff --git a/primitives/test/utils/WebAuthnHarness.sol b/primitives/test/utils/WebAuthnHarness.sol new file mode 100644 index 00000000..189bf695 --- /dev/null +++ b/primitives/test/utils/WebAuthnHarness.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import {WebAuthn} from "../../src/WebAuthn.sol"; + +/** Helper contract to expose internal functions for testing */ +contract WebAuthnHarness is WebAuthn { + constructor() WebAuthn() {} + + function exposed_verifySignature( + bytes calldata authenticatorData, + bytes1 authenticatorDataFlagMask, + bytes calldata clientData, + bytes32 clientChallenge, + uint256 clientChallengeDataOffset, + uint256[2] calldata signature, + uint256[2] calldata publicKey + ) external returns (bool) { + return verifySignature( + authenticatorData, + authenticatorDataFlagMask, + clientData, + clientChallenge, + clientChallengeDataOffset, + signature, + publicKey + ); + } +}