diff --git a/.gitmodules b/.gitmodules index 744ac54b..d2bb2da1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,9 @@ [submodule "lib/I4337"] path = lib/I4337 url = https://github.com/leekt/I4337 +[submodule "lib/p256-verifier"] + path = lib/p256-verifier + url = https://github.com/daimo-eth/p256-verifier +[submodule "lib/FreshCryptoLib"] + path = lib/FreshCryptoLib + url = https://github.com/rdubois-crypto/FreshCryptoLib diff --git a/foundry.toml b/foundry.toml index 77dc9e49..d4dbafef 100644 --- a/foundry.toml +++ b/foundry.toml @@ -2,7 +2,7 @@ src = 'src' out = 'out' libs = ['lib'] -solc_version = '0.8.19' +solc_version = '0.8.21' bytecode_hash = "none" cbor_metadata = false optimize = true diff --git a/lib/FreshCryptoLib b/lib/FreshCryptoLib new file mode 160000 index 00000000..e2830cb5 --- /dev/null +++ b/lib/FreshCryptoLib @@ -0,0 +1 @@ +Subproject commit e2830cb5d7b0f6ae35b5800287c0f5c92388070b diff --git a/lib/forge-std b/lib/forge-std index 705263c9..37a37ab7 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 705263c95892a906d7af65f0f73ce8a4a0c80b80 +Subproject commit 37a37ab73364d6644bfe11edf88a07880f99bd56 diff --git a/lib/p256-verifier b/lib/p256-verifier new file mode 160000 index 00000000..fcaa173c --- /dev/null +++ b/lib/p256-verifier @@ -0,0 +1 @@ +Subproject commit fcaa173c36e6a0fd55370f74f949998356869b2c diff --git a/lib/solady b/lib/solady index 6f724bd0..cde0a5fb 160000 --- a/lib/solady +++ b/lib/solady @@ -1 +1 @@ -Subproject commit 6f724bd0f654b1199ee4cd909d206878c405bbcb +Subproject commit cde0a5fb594da8655ba6bfcdc2e40a7c870c0cc0 diff --git a/remappings.txt b/remappings.txt index e55a454b..82c83131 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,3 +1,5 @@ ds-test/=lib/forge-std/lib/ds-test/src/ forge-std/=lib/forge-std/src/ solady/=lib/solady/src/ +p256-verifier/=lib/p256-verifier/src/ +FreshCryptoLib/=lib/FreshCryptoLib/solidity/src/ \ No newline at end of file diff --git a/src/validator/P256Validator.sol b/src/validator/P256Validator.sol new file mode 100644 index 00000000..02f122fd --- /dev/null +++ b/src/validator/P256Validator.sol @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {UserOperation} from "I4337/interfaces/UserOperation.sol"; +import {ECDSA} from "solady/utils/ECDSA.sol"; +import {IKernelValidator} from "../interfaces/IKernelValidator.sol"; +import {ValidationData} from "../common/Types.sol"; +import {SIG_VALIDATION_FAILED} from "../common/Constants.sol"; +import {P256} from "p256-verifier/P256.sol"; + +contract P256Validator is IKernelValidator { + event P256PublicKeysChanged(address indexed kernel, PublicKey indexed oldKeys, PublicKey indexed newKeys); + + struct PublicKey { + uint256 x; + uint256 y; + } + + error BadKey(); + + mapping(address => PublicKey) public p256PublicKey; + + function enable(bytes calldata _data) external payable override { + PublicKey memory key = abi.decode(_data, (PublicKey)); + if (key.x == 0 || key.y == 0) { + revert BadKey(); + } + PublicKey memory oldKey = p256PublicKey[msg.sender]; + p256PublicKey[msg.sender] = key; + emit P256PublicKeysChanged(msg.sender, oldKey, key); + } + + function disable(bytes calldata) external payable override { + delete p256PublicKey[msg.sender]; + } + + function validateUserOp(UserOperation calldata _userOp, bytes32 _userOpHash, uint256) external payable override returns (ValidationData validationData) { + (uint256 r, uint256 s) = abi.decode(_userOp.signature, (uint256, uint256)); + PublicKey memory key = p256PublicKey[_userOp.sender]; + bytes32 hash = ECDSA.toEthSignedMessageHash(_userOpHash); + if (P256.verifySignature(hash, r, s, key.x, key.y)) { + return ValidationData.wrap(0); + } + if (!P256.verifySignature(_userOpHash, r, s, key.x, key.y)) { + return SIG_VALIDATION_FAILED; + } + } + + function validateSignature(bytes32 hash, bytes calldata signature) external view override returns (ValidationData) { + (uint256 r, uint256 s) = abi.decode(signature, (uint256, uint256)); + PublicKey memory key = p256PublicKey[msg.sender]; + if (P256.verifySignature(hash, r, s, key.x, key.y)) { + return ValidationData.wrap(0); + } + bytes32 ethHash = ECDSA.toEthSignedMessageHash(hash); + if (!P256.verifySignature(ethHash, r, s, key.x, key.y)) { + return SIG_VALIDATION_FAILED; + } + return ValidationData.wrap(0); + } + + function validCaller(address _caller, bytes calldata) external view override returns (bool) { + revert NotImplemented(); + } +} diff --git a/test/foundry/KernelECDSA.t.sol b/test/foundry/KernelECDSA.t.sol index b22c2c4c..8d68abce 100644 --- a/test/foundry/KernelECDSA.t.sol +++ b/test/foundry/KernelECDSA.t.sol @@ -68,8 +68,7 @@ contract KernelECDSATest is KernelTestBase { } function test_default_validator_enable() external override { - UserOperation memory op = entryPoint.fillUserOp( - address(kernel), + UserOperation memory op = buildUserOperation( abi.encodeWithSelector( IKernel.execute.selector, address(defaultValidator), @@ -78,17 +77,13 @@ contract KernelECDSATest is KernelTestBase { Operation.Call ) ); - op.signature = signUserOp(op); - UserOperation[] memory ops = new UserOperation[](1); - ops[0] = op; - entryPoint.handleOps(ops, beneficiary); + performUserOperationWithSig(op); (address owner) = ECDSAValidator(address(defaultValidator)).ecdsaValidatorStorage(address(kernel)); assertEq(owner, address(0xdeadbeef), "owner should be 0xdeadbeef"); } function test_default_validator_disable() external override { - UserOperation memory op = entryPoint.fillUserOp( - address(kernel), + UserOperation memory op = buildUserOperation( abi.encodeWithSelector( IKernel.execute.selector, address(defaultValidator), @@ -97,10 +92,7 @@ contract KernelECDSATest is KernelTestBase { Operation.Call ) ); - op.signature = signUserOp(op); - UserOperation[] memory ops = new UserOperation[](1); - ops[0] = op; - entryPoint.handleOps(ops, beneficiary); + performUserOperationWithSig(op); (address owner) = ECDSAValidator(address(defaultValidator)).ecdsaValidatorStorage(address(kernel)); assertEq(owner, address(0), "owner should be 0"); } diff --git a/test/foundry/KernelLiteECDSA.t.sol b/test/foundry/KernelLiteECDSA.t.sol index e18f21ff..3496986c 100644 --- a/test/foundry/KernelLiteECDSA.t.sol +++ b/test/foundry/KernelLiteECDSA.t.sol @@ -57,16 +57,12 @@ contract KernelECDSATest is KernelTestBase { function test_set_default_validator() external override { TestValidator newValidator = new TestValidator(); bytes memory empty; - UserOperation memory op = entryPoint.fillUserOp( - address(kernel), + UserOperation memory op = buildUserOperation( abi.encodeWithSelector(KernelStorage.setDefaultValidator.selector, address(newValidator), empty) ); - op.signature = signUserOp(op); - UserOperation[] memory ops = new UserOperation[](1); - ops[0] = op; vm.expectEmit(true, true, true, false, address(entryPoint)); emit UserOperationEvent(entryPoint.getUserOpHash(op), address(kernel), address(0), op.nonce, false, 0, 0); - entryPoint.handleOps(ops, beneficiary); + performUserOperationWithSig(op); } function signUserOp(UserOperation memory op) internal view override returns (bytes memory) { @@ -101,14 +97,9 @@ contract KernelECDSATest is KernelTestBase { function test_transfer_ownership() external { address newOwner = makeAddr("new owner"); - UserOperation memory op = entryPoint.fillUserOp( - address(kernel), abi.encodeWithSelector(KernelLiteECDSA.transferOwnership.selector, newOwner) - ); - op.signature = signUserOp(op); - UserOperation[] memory ops = new UserOperation[](1); - ops[0] = op; + UserOperation memory op = buildUserOperation(abi.encodeWithSelector(KernelLiteECDSA.transferOwnership.selector, newOwner)); vm.expectEmit(true, true, true, false, address(entryPoint)); emit UserOperationEvent(entryPoint.getUserOpHash(op), address(kernel), address(0), op.nonce, false, 0, 0); - entryPoint.handleOps(ops, beneficiary); + performUserOperationWithSig(op); } } diff --git a/test/foundry/KernelTestBase.sol b/test/foundry/KernelTestBase.sol index b4501cdd..1951cba4 100644 --- a/test/foundry/KernelTestBase.sol +++ b/test/foundry/KernelTestBase.sol @@ -90,7 +90,7 @@ abstract contract KernelTestBase is Test { function test_default_validator_disable() external virtual; - function test_external_call_execute_success() external { + function test_external_call_execute_success() external virtual { address[] memory validCallers = getOwners(); for (uint256 i = 0; i < validCallers.length; i++) { vm.prank(validCallers[i]); @@ -98,7 +98,7 @@ abstract contract KernelTestBase is Test { } } - function test_external_call_execute_delegatecall_success() external { + function test_external_call_execute_delegatecall_success() external virtual { address[] memory validCallers = getOwners(); for (uint256 i = 0; i < validCallers.length; i++) { vm.prank(validCallers[i]); @@ -106,7 +106,7 @@ abstract contract KernelTestBase is Test { } } - function test_external_call_execute_delegatecall_fail() external { + function test_external_call_execute_delegatecall_fail() external virtual { address[] memory validCallers = getOwners(); for (uint256 i = 0; i < validCallers.length; i++) { vm.prank(address(uint160(validCallers[i]) + 1)); @@ -133,7 +133,7 @@ abstract contract KernelTestBase is Test { } } - function test_external_call_batch_execute_success() external { + function test_external_call_batch_execute_success() external virtual { Call[] memory calls = new Call[](1); calls[0] = Call(owner, 0, ""); vm.prank(owner); @@ -174,7 +174,7 @@ abstract contract KernelTestBase is Test { kernel.upgradeTo(address(0xdeadbeef)); } - function test_external_call_default() external { + function test_external_call_default() external virtual { vm.startPrank(owner); (bool success,) = address(kernel).call(abi.encodePacked("Hello world")); assertEq(success, true); @@ -194,7 +194,7 @@ abstract contract KernelTestBase is Test { assertEq(proxy, address(kernel)); } - function test_validate_signature() external { + function test_validate_signature() external virtual { Kernel kernel2 = Kernel(payable(factory.createAccount(address(kernelImpl), getInitializeData(), 3))); bytes32 hash = keccak256(abi.encodePacked("hello world")); bytes32 digest = keccak256( @@ -207,7 +207,7 @@ abstract contract KernelTestBase is Test { assertEq(kernel2.isValidSignature(hash, sig), bytes4(0xffffffff)); } - function test_fail_validate_wrongsignature() external { + function test_fail_validate_wrongsignature() external virtual { bytes32 hash = keccak256(abi.encodePacked("hello world")); bytes memory sig = getWrongSignature(hash); assertEq(kernel.isValidSignature(hash, sig), bytes4(0xffffffff)); @@ -262,35 +262,25 @@ abstract contract KernelTestBase is Test { function test_set_default_validator() external virtual { TestValidator newDefaultValidator = new TestValidator(); bytes memory empty; - UserOperation memory op = entryPoint.fillUserOp( - address(kernel), + UserOperation memory op = buildUserOperation( abi.encodeWithSelector(IKernel.setDefaultValidator.selector, address(newDefaultValidator), empty) ); - op.signature = signUserOp(op); - UserOperation[] memory ops = new UserOperation[](1); - ops[0] = op; - entryPoint.handleOps(ops, beneficiary); + performUserOperationWithSig(op); assertEq(address(IKernel(address(kernel)).getDefaultValidator()), address(newDefaultValidator)); } function test_disable_mode() external { vm.warp(1000); bytes memory empty; - UserOperation memory op = entryPoint.fillUserOp( - address(kernel), abi.encodeWithSelector(IKernel.disableMode.selector, bytes4(0x00000001), address(0), empty) - ); - op.signature = signUserOp(op); - UserOperation[] memory ops = new UserOperation[](1); - ops[0] = op; - entryPoint.handleOps(ops, beneficiary); + UserOperation memory op = buildUserOperation(abi.encodeWithSelector(IKernel.disableMode.selector, bytes4(0x00000001), address(0), empty)); + performUserOperationWithSig(op); assertEq(uint256(bytes32(IKernel(address(kernel)).getDisabledMode())), 1 << 224); assertEq(uint256(IKernel(address(kernel)).getLastDisabledTime()), block.timestamp); } function test_set_execution() external { TestValidator newValidator = new TestValidator(); - UserOperation memory op = entryPoint.fillUserOp( - address(kernel), + UserOperation memory op = buildUserOperation( abi.encodeWithSelector( IKernel.setExecution.selector, bytes4(0xdeadbeef), @@ -301,10 +291,7 @@ abstract contract KernelTestBase is Test { bytes("") ) ); - op.signature = signUserOp(op); - UserOperation[] memory ops = new UserOperation[](1); - ops[0] = op; - entryPoint.handleOps(ops, beneficiary); + performUserOperationWithSig(op); ExecutionDetail memory execution = IKernel(address(kernel)).getExecution(bytes4(0xdeadbeef)); assertEq(execution.executor, address(0xdead)); assertEq(address(execution.validator), address(newValidator)); @@ -312,10 +299,9 @@ abstract contract KernelTestBase is Test { assertEq(uint256(ValidAfter.unwrap(execution.validAfter)), uint256(0)); } - function test_external_call_execution() external { + function test_external_call_execution() external virtual { TestValidator newValidator = new TestValidator(); - UserOperation memory op = entryPoint.fillUserOp( - address(kernel), + UserOperation memory op = buildUserOperation( abi.encodeWithSelector( IKernel.setExecution.selector, bytes4(0xdeadbeef), @@ -326,10 +312,7 @@ abstract contract KernelTestBase is Test { bytes("") ) ); - op.signature = signUserOp(op); - UserOperation[] memory ops = new UserOperation[](1); - ops[0] = op; - entryPoint.handleOps(ops, beneficiary); + performUserOperationWithSig(op); ExecutionDetail memory execution = IKernel(address(kernel)).getExecution(bytes4(0xdeadbeef)); assertEq(execution.executor, address(0xdead)); assertEq(address(execution.validator), address(newValidator)); @@ -353,39 +336,28 @@ abstract contract KernelTestBase is Test { function test_revert_when_mode_disabled() external { vm.warp(1000); bytes memory empty; - UserOperation memory op = entryPoint.fillUserOp( - address(kernel), abi.encodeWithSelector(IKernel.disableMode.selector, bytes4(0x00000001), address(0), empty) + UserOperation memory op = buildUserOperation(abi.encodeWithSelector(IKernel.disableMode.selector, bytes4(0x00000001), address(0), empty) ); - op.signature = signUserOp(op); - UserOperation[] memory ops = new UserOperation[](1); - ops[0] = op; - entryPoint.handleOps(ops, beneficiary); + performUserOperationWithSig(op); // try to run with mode 0x00000001 - op = entryPoint.fillUserOp( - address(kernel), abi.encodeWithSelector(IKernel.disableMode.selector, bytes4(0x00000001)) - ); + op = buildUserOperation(abi.encodeWithSelector(IKernel.disableMode.selector, bytes4(0x00000001))); op.signature = abi.encodePacked(bytes4(0x00000001), entryPoint.signUserOpHash(vm, ownerKey, op)); - ops[0] = op; - vm.expectRevert( abi.encodeWithSelector(IEntryPoint.FailedOp.selector, 0, string.concat("AA23 reverted (or OOG)")) ); - entryPoint.handleOps(ops, beneficiary); + performUserOperation(op); } // validate user op function test_validateUserOp_fail_not_entryPoint() external { - UserOperation memory op = - entryPoint.fillUserOp(address(kernel), abi.encodeWithSelector(TestExecutor.doNothing.selector)); - op.signature = signUserOp(op); + UserOperation memory op = buildUserOperation(abi.encodeWithSelector(TestExecutor.doNothing.selector)); vm.expectRevert(IKernel.NotEntryPoint.selector); kernel.validateUserOp(op, bytes32(hex"deadbeef"), uint256(100)); } function test_validateUserOp_fail_invalid_mode() external { - UserOperation memory op = - entryPoint.fillUserOp(address(kernel), abi.encodeWithSelector(TestExecutor.doNothing.selector)); + UserOperation memory op = buildUserOperation(abi.encodeWithSelector(TestExecutor.doNothing.selector)); op.signature = hex"00000003"; vm.prank(address(entryPoint)); ValidationData res = kernel.validateUserOp(op, bytes32(hex"deadbeef"), uint256(100)); @@ -393,36 +365,27 @@ abstract contract KernelTestBase is Test { } function test_sudo() external { - UserOperation memory op = - entryPoint.fillUserOp(address(kernel), abi.encodeWithSelector(TestExecutor.doNothing.selector)); - op.signature = signUserOp(op); - UserOperation[] memory ops = new UserOperation[](1); - ops[0] = op; - entryPoint.handleOps(ops, beneficiary); + UserOperation memory op = buildUserOperation(abi.encodeWithSelector(TestExecutor.doNothing.selector)); + performUserOperationWithSig(op); } function test_sudo_wrongSig() external { - UserOperation memory op = - entryPoint.fillUserOp(address(kernel), abi.encodeWithSelector(TestExecutor.doNothing.selector)); + UserOperation memory op = buildUserOperation(abi.encodeWithSelector(TestExecutor.doNothing.selector)); op.signature = getWrongSignature(op); - UserOperation[] memory ops = new UserOperation[](1); - ops[0] = op; vm.expectRevert(); - entryPoint.handleOps(ops, beneficiary); + performUserOperation(op); } // mode 2 tests function test_mode_2() public { - UserOperation memory op = entryPoint.fillUserOp(address(kernel), abi.encodePacked(executionSig)); + UserOperation memory op = buildUserOperation(abi.encodePacked(executionSig)); op.signature = buildEnableSignature( op, executionSig, uint48(0), uint48(0), executionDetail.validator, executionDetail.executor ); - UserOperation[] memory ops = new UserOperation[](1); - ops[0] = op; vm.expectEmit(true, true, true, false, address(entryPoint)); emit UserOperationEvent(entryPoint.getUserOpHash(op), address(kernel), address(0), op.nonce, false, 0, 0); - entryPoint.handleOps(ops, beneficiary); + performUserOperation(op); } function buildEnableSignature( @@ -453,8 +416,7 @@ abstract contract KernelTestBase is Test { } function test_enable_then_mode_1() public { - UserOperation memory op = entryPoint.fillUserOp( - address(kernel), + UserOperation memory op = buildUserOperation( abi.encodeWithSelector( IKernel.setExecution.selector, executionSig, @@ -465,18 +427,10 @@ abstract contract KernelTestBase is Test { getEnableData() ) ); - op.signature = signUserOp(op); - UserOperation[] memory ops = new UserOperation[](1); - ops[0] = op; - - entryPoint.handleOps(ops, beneficiary); - // vm.expectEmit(true, false, false, false); - // emit TestValidator.TestValidateUserOp(opHash); - op = entryPoint.fillUserOp(address(kernel), abi.encodeWithSelector(executionSig)); - // registered + performUserOperationWithSig(op); + op = buildUserOperation(abi.encodeWithSelector(executionSig)); op.signature = abi.encodePacked(bytes4(0x00000001), getValidatorSignature(op)); - ops[0] = op; - entryPoint.handleOps(ops, beneficiary); + performUserOperation(op); } function _setAddress() internal { @@ -501,4 +455,21 @@ abstract contract KernelTestBase is Test { ) ); } + + function buildUserOperation(bytes memory callData) internal view returns (UserOperation memory op) { + return entryPoint.fillUserOp(address(kernel), callData); + } + + function performUserOperationWithSig(UserOperation memory op) internal { + op.signature = signUserOp(op); + UserOperation[] memory ops = new UserOperation[](1); + ops[0] = op; + entryPoint.handleOps(ops, beneficiary); + } + + function performUserOperation(UserOperation memory op) internal { + UserOperation[] memory ops = new UserOperation[](1); + ops[0] = op; + entryPoint.handleOps(ops, beneficiary); + } } diff --git a/test/foundry/validator/KillSwitchValidator.t.sol b/test/foundry/validator/KillSwitchValidator.t.sol index 47d12f11..6b080d64 100644 --- a/test/foundry/validator/KillSwitchValidator.t.sol +++ b/test/foundry/validator/KillSwitchValidator.t.sol @@ -68,16 +68,12 @@ contract KillSwitchValidatorTest is KernelECDSATest { } function test_force_unblock() external { - UserOperation memory op = entryPoint.fillUserOp( - address(kernel), abi.encodeWithSelector(Kernel.execute.selector, owner, 0, "", Operation.Call) - ); + UserOperation memory op = buildUserOperation(abi.encodeWithSelector(Kernel.execute.selector, owner, 0, "", Operation.Call)); op.signature = bytes.concat(bytes4(0), entryPoint.signUserOpHash(vm, ownerKey, op)); - UserOperation[] memory ops = new UserOperation[](1); - ops[0] = op; - entryPoint.handleOps(ops, beneficiary); + performUserOperation(op); - op = entryPoint.fillUserOp(address(kernel), abi.encodeWithSelector(KillSwitchAction.toggleKillSwitch.selector)); + op = buildUserOperation(abi.encodeWithSelector(KillSwitchAction.toggleKillSwitch.selector)); bytes memory enableData = abi.encodePacked(guardian); { bytes32 digest = getTypedDataHash( @@ -110,15 +106,13 @@ contract KillSwitchValidatorTest is KernelECDSATest { op.signature = bytes.concat(op.signature, bytes6(uint48(pausedUntil)), sig); } - ops[0] = op; - entryPoint.handleOps(ops, beneficiary); + performUserOperation(op); assertEq(kernel.getDisabledMode(), bytes4(0xffffffff)); assertEq(address(kernel.getDefaultValidator()), address(killSwitch)); - op = entryPoint.fillUserOp(address(kernel), abi.encodeWithSelector(KillSwitchAction.toggleKillSwitch.selector)); + op = buildUserOperation(abi.encodeWithSelector(KillSwitchAction.toggleKillSwitch.selector)); op.signature = bytes.concat(bytes4(0), entryPoint.signUserOpHash(vm, guardianKey, op)); - ops[0] = op; - entryPoint.handleOps(ops, beneficiary); + performUserOperation(op); assertEq(kernel.getDisabledMode(), bytes4(0)); } } diff --git a/test/foundry/validator/P256Validator.t.sol b/test/foundry/validator/P256Validator.t.sol new file mode 100644 index 00000000..25336449 --- /dev/null +++ b/test/foundry/validator/P256Validator.t.sol @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IEntryPoint} from "I4337/interfaces/IEntryPoint.sol"; +import "src/Kernel.sol"; +// test artifacts +// test utils +import "forge-std/Test.sol"; +import {ERC4337Utils} from "../utils/ERC4337Utils.sol"; +import {KernelTestBase} from "../KernelTestBase.sol"; +import {TestExecutor} from "../mock/TestExecutor.sol"; +import {TestValidator} from "../mock/TestValidator.sol"; +import {P256Validator} from "src/validator/P256Validator.sol"; +import {P256Verifier} from "p256-verifier/P256Verifier.sol"; +import {P256} from "p256-verifier/P256.sol"; +import {FCL_ecdsa_utils} from "FreshCryptoLib/FCL_ecdsa_utils.sol"; +import {IKernel} from "src/interfaces/IKernel.sol"; + + +using ERC4337Utils for IEntryPoint; + +contract P256ValidatorTest is KernelTestBase { + P256Verifier p256Verifier; + P256Validator p256Validator; + + // Curve order (number of points) + uint256 constant n = + 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551; + + + uint256 x; + uint256 y; + + function setUp() public { + p256Validator = new P256Validator(); + p256Verifier = new P256Verifier(); + + vm.etch(0xc2b78104907F722DABAc4C69f826a522B2754De4, address(p256Verifier).code); + + _initialize(); + (x, y) = generatePublicKey(ownerKey); + _setAddress(); + _setExecutionDetail(); + } + + function _setExecutionDetail() internal virtual override { + executionDetail.executor = address(new TestExecutor()); + executionSig = TestExecutor.doNothing.selector; + executionDetail.validator = new TestValidator(); + } + + function getValidatorSignature(UserOperation memory _op) internal view virtual override returns (bytes memory) { + bytes32 hash = entryPoint.getUserOpHash(_op); + (uint256 r, uint256 s) = generateSignature(ownerKey, ECDSA.toEthSignedMessageHash(hash)); + return abi.encodePacked(bytes4(0x00000000), abi.encode(r, s)); + } + + function getOwners() internal virtual override returns (address[] memory _owners){ + _owners = new address[](1); + _owners[0] = address(0); + return _owners; + } + + function getEnableData() internal view virtual override returns (bytes memory) { + return ""; + } + + function getInitializeData() internal view override returns (bytes memory) { + return abi.encodeWithSelector(KernelStorage.initialize.selector, p256Validator, abi.encode(x, y)); + } + + function test_default_validator_enable() external override{ + UserOperation memory op = buildUserOperation( + abi.encodeWithSelector( + IKernel.execute.selector, + address(p256Validator), + 0, + abi.encodeWithSelector(P256Validator.enable.selector, abi.encode(x, y)), + Operation.Call + ) + ); + performUserOperationWithSig(op); + (uint256 x2, uint256 y2) = P256Validator(address(p256Validator)).p256PublicKey(address(kernel)); + verifyPublicKey(x2, y2, x, y); + } + + function test_default_validator_disable() external override { + UserOperation memory op = buildUserOperation( + abi.encodeWithSelector( + IKernel.execute.selector, + address(p256Validator), + 0, + abi.encodeWithSelector(P256Validator.disable.selector, ""), + Operation.Call + ) + ); + performUserOperationWithSig(op); + (uint256 x2, uint256 y2) = P256Validator(address(p256Validator)).p256PublicKey(address(kernel)); + verifyPublicKey(x2, y2, 0, 0); + } + + function test_external_call_batch_execute_success() external override { + vm.skip(true); + } + + function test_external_call_execute_success() external override { + vm.skip(true); + } + + function test_external_call_execute_delegatecall_success() external override { + vm.skip(true); + } + + function test_external_call_execute_delegatecall_fail() external override { + vm.skip(true); + } + + function test_external_call_default() external override { + vm.skip(true); + } + + function test_external_call_execution() external override { + vm.skip(true); + } + + function generatePublicKey(uint256 privateKey) internal view returns (uint256, uint256) { + return FCL_ecdsa_utils.ecdsa_derivKpub(privateKey); + } + + function generateSignature(uint256 privateKey, bytes32 hash) internal view returns (uint256 r, uint256 s) { + // Securely generate a random k value for each signature + uint256 k = uint256(keccak256(abi.encodePacked(hash, block.timestamp, block.difficulty, privateKey))) % n; + while (k == 0) { + k = uint256(keccak256(abi.encodePacked(k))) % n; + } + + // Generate the signature using the k value and the private key + (r, s) = FCL_ecdsa_utils.ecdsa_sign(hash, k, privateKey); + + // Ensure that s is in the lower half of the range [1, n-1] + if (r == 0 || s == 0 || s > P256.P256_N_DIV_2) { + s = n - s; // If s is in the upper half, use n - s instead + } + + return (r, s); + } + + function test_utils(uint256 privateKey, bytes32 hash) external { + vm.assume(hash != 0); + vm.assume(privateKey != 0); + (uint256 x1, uint256 y1) = generatePublicKey(privateKey); + (uint256 r, uint256 s) = generateSignature(privateKey, hash); + + vm.assume(x1 != 0); + vm.assume(y1 != 0); + vm.assume(r != 0); + vm.assume(s < P256.P256_N_DIV_2); + assertEq(P256.verifySignature(hash, r, s, x1, y1), true); + } + + function test_validate_signature() external override { + Kernel kernel2 = Kernel(payable(factory.createAccount(address(kernelImpl), getInitializeData(), 3))); + bytes32 hash = keccak256(abi.encodePacked("hello world")); + + bytes32 digest = keccak256( + abi.encodePacked( + "\x19\x01", ERC4337Utils._buildDomainSeparator(KERNEL_NAME, KERNEL_VERSION, address(kernel)), hash + ) + ); + + (uint256 r, uint256 s) = generateSignature(ownerKey, digest); + + assertEq(kernel.isValidSignature(hash, abi.encode(r, s)), Kernel.isValidSignature.selector); + assertEq(kernel2.isValidSignature(hash, abi.encode(r, s)), bytes4(0xffffffff)); + } + + function test_fail_validate_wrongsignature() external override { + bytes32 hash = keccak256(abi.encodePacked("hello world")); + bytes memory sig = getWrongSignature(hash); + assertEq(kernel.isValidSignature(hash, sig), bytes4(0xffffffff)); + } + + function signUserOp(UserOperation memory op) internal view override returns (bytes memory) { + bytes32 hash = entryPoint.getUserOpHash(op); + (uint256 r, uint256 s) = generateSignature(ownerKey, hash); + return abi.encodePacked(bytes4(0x00000000), abi.encode(r, s)); + } + + function getWrongSignature(UserOperation memory op) internal view override returns (bytes memory) { + bytes32 hash = entryPoint.getUserOpHash(op); + (uint256 r, uint256 s) = generateSignature(ownerKey + 1, hash); + return abi.encodePacked(bytes4(0x00000000), abi.encode(r, s)); + } + + function signHash(bytes32 hash) internal view override returns (bytes memory) { + (uint256 r, uint256 s) = generateSignature(ownerKey, ECDSA.toEthSignedMessageHash(hash)); + return abi.encode(r, s); + } + + function getWrongSignature(bytes32 hash) internal view override returns (bytes memory) { + (uint256 r, uint256 s) = generateSignature(ownerKey + 1, ECDSA.toEthSignedMessageHash(hash)); + return abi.encode(r, s); + } + + function verifyPublicKey(uint256 actualX, uint256 actualY, uint256 expectedX, uint256 expectedY) internal { + assertEq(actualX, expectedX, "Public key X component mismatch"); + assertEq(actualY, expectedY, "Public key Y component mismatch"); + } +} diff --git a/test/foundry/validator/SessionKeyValidator.t.sol b/test/foundry/validator/SessionKeyValidator.t.sol index bf831ec2..3c7927c5 100644 --- a/test/foundry/validator/SessionKeyValidator.t.sol +++ b/test/foundry/validator/SessionKeyValidator.t.sol @@ -107,7 +107,7 @@ contract SessionKeyValidatorTest is KernelECDSATest { ) }); - op = entryPoint.fillUserOp(address(kernel), abi.encodeWithSelector(IKernel.executeBatch.selector, calls)); + op = buildUserOperation(abi.encodeWithSelector(IKernel.executeBatch.selector, calls)); if (usingPaymasterMode != 0) { // 0 = no paymaster // 1 = unknown paymaster @@ -156,8 +156,7 @@ contract SessionKeyValidatorTest is KernelECDSATest { bool param1Faulty, bool param2Faulty ) internal view returns (UserOperation memory op) { - op = entryPoint.fillUserOp( - address(kernel), + op = buildUserOperation( abi.encodeWithSelector( IKernel.execute.selector, permissions[indexToUse].target, @@ -306,12 +305,10 @@ contract SessionKeyValidatorTest is KernelECDSATest { ) ); - UserOperation[] memory ops = new UserOperation[](1); - ops[0] = op; if (shouldFail) { vm.expectRevert(); } - entryPoint.handleOps(ops, beneficiary); + performUserOperation(op); if (config.interval > 0 && config.validAfter > 0 && !shouldFail) { (ValidAfter updatedValidAfter, uint48 r) = sessionKeyValidator.executionStatus( keccak256(abi.encodePacked(sessionData.nonce, uint32(config.indexToUse))), address(kernel) @@ -335,10 +332,10 @@ contract SessionKeyValidatorTest is KernelECDSATest { if (config.earlyRun == i) { vm.expectRevert(); } - entryPoint.handleOps(ops, beneficiary); + performUserOperation(op); if (config.earlyRun == i) { vm.warp(config.validAfter + config.interval * i); - entryPoint.handleOps(ops, beneficiary); + performUserOperation(op); } (ValidAfter updatedValidAfter, uint48 r) = sessionKeyValidator.executionStatus( keccak256(abi.encodePacked(sessionData.nonce, uint32(config.indexToUse))), address(kernel) @@ -355,7 +352,7 @@ contract SessionKeyValidatorTest is KernelECDSATest { op.nonce = op.nonce + 1; op.signature = _getBatchActionSignature(op, permissions, config.indexToUse); vm.expectRevert(); - entryPoint.handleOps(ops, beneficiary); + performUserOperation(op); } } @@ -413,12 +410,10 @@ contract SessionKeyValidatorTest is KernelECDSATest { ) ); - UserOperation[] memory ops = new UserOperation[](1); - ops[0] = op; if (shouldFail) { vm.expectRevert(); } - entryPoint.handleOps(ops, beneficiary); + performUserOperation(op); if (config.interval > 0 && config.validAfter > 0 && !shouldFail) { (ValidAfter updatedValidAfter, uint48 r) = sessionKeyValidator.executionStatus( keccak256(abi.encodePacked(sessionData.nonce, uint32(config.indexToUse))), address(kernel) @@ -442,10 +437,10 @@ contract SessionKeyValidatorTest is KernelECDSATest { if (config.earlyRun == i) { vm.expectRevert(); } - entryPoint.handleOps(ops, beneficiary); + performUserOperation(op); if (config.earlyRun == i) { vm.warp(config.validAfter + config.interval * i); - entryPoint.handleOps(ops, beneficiary); + performUserOperation(op); } (ValidAfter updatedValidAfter, uint48 r) = sessionKeyValidator.executionStatus( keccak256(abi.encodePacked(sessionData.nonce, uint32(config.indexToUse))), address(kernel) @@ -462,7 +457,7 @@ contract SessionKeyValidatorTest is KernelECDSATest { op.nonce = op.nonce + 1; op.signature = _getSingleActionSignature(op, permissions, config.indexToUse); vm.expectRevert(); - entryPoint.handleOps(ops, beneficiary); + performUserOperation(op); } }