diff --git a/.gas-snapshot b/.gas-snapshot index e41656f8..09164ee6 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,160 +1,235 @@ KernelECDSATest:test_default_validator_disable() (gas: 0) -KernelECDSATest:test_default_validator_disable() (gas: 180311) +KernelECDSATest:test_default_validator_disable() (gas: 180903) KernelECDSATest:test_default_validator_enable() (gas: 0) -KernelECDSATest:test_default_validator_enable() (gas: 187800) -KernelECDSATest:test_disable_mode() (gas: 182795) -KernelECDSATest:test_disable_mode() (gas: 192079) -KernelECDSATest:test_eip712() (gas: 15684) -KernelECDSATest:test_eip712() (gas: 15684) -KernelECDSATest:test_enable_then_mode_1() (gas: 277994) -KernelECDSATest:test_enable_then_mode_1() (gas: 286106) -KernelECDSATest:test_external_call_batch_execute_fail() (gas: 23628) -KernelECDSATest:test_external_call_batch_execute_fail() (gas: 29534) -KernelECDSATest:test_external_call_batch_execute_success() (gas: 20629) -KernelECDSATest:test_external_call_batch_execute_success() (gas: 26536) -KernelECDSATest:test_external_call_default() (gas: 23511) -KernelECDSATest:test_external_call_default() (gas: 29338) -KernelECDSATest:test_external_call_execute_delegatecall_fail() (gas: 22499) -KernelECDSATest:test_external_call_execute_delegatecall_fail() (gas: 28381) -KernelECDSATest:test_external_call_execute_delegatecall_option_fail() (gas: 20434) -KernelECDSATest:test_external_call_execute_delegatecall_option_fail() (gas: 26329) -KernelECDSATest:test_external_call_execute_delegatecall_success() (gas: 19738) -KernelECDSATest:test_external_call_execute_delegatecall_success() (gas: 25621) -KernelECDSATest:test_external_call_execute_fail() (gas: 21968) -KernelECDSATest:test_external_call_execute_fail() (gas: 27862) -KernelECDSATest:test_external_call_execute_success() (gas: 20070) -KernelECDSATest:test_external_call_execute_success() (gas: 25987) -KernelECDSATest:test_external_call_execution() (gas: 497819) -KernelECDSATest:test_external_call_execution() (gas: 510696) +KernelECDSATest:test_default_validator_enable() (gas: 188628) +KernelECDSATest:test_disable_mode() (gas: 182553) +KernelECDSATest:test_disable_mode() (gas: 191433) +KernelECDSATest:test_eip712() (gas: 15037) +KernelECDSATest:test_eip712() (gas: 15059) +KernelECDSATest:test_enable_then_mode_1() (gas: 279181) +KernelECDSATest:test_enable_then_mode_1() (gas: 287578) +KernelECDSATest:test_external_call_batch_execute_fail() (gas: 22756) +KernelECDSATest:test_external_call_batch_execute_fail() (gas: 28852) +KernelECDSATest:test_external_call_batch_execute_success() (gas: 20270) +KernelECDSATest:test_external_call_batch_execute_success() (gas: 26366) +KernelECDSATest:test_external_call_default() (gas: 22692) +KernelECDSATest:test_external_call_default() (gas: 28797) +KernelECDSATest:test_external_call_execute_delegatecall_fail() (gas: 21787) +KernelECDSATest:test_external_call_execute_delegatecall_fail() (gas: 27771) +KernelECDSATest:test_external_call_execute_delegatecall_option_fail() (gas: 19427) +KernelECDSATest:test_external_call_execute_delegatecall_option_fail() (gas: 25605) +KernelECDSATest:test_external_call_execute_delegatecall_success() (gas: 18939) +KernelECDSATest:test_external_call_execute_delegatecall_success() (gas: 24923) +KernelECDSATest:test_external_call_execute_fail() (gas: 21852) +KernelECDSATest:test_external_call_execute_fail() (gas: 28030) +KernelECDSATest:test_external_call_execute_success() (gas: 19187) +KernelECDSATest:test_external_call_execute_success() (gas: 25387) +KernelECDSATest:test_external_call_execution() (gas: 569078) +KernelECDSATest:test_external_call_execution() (gas: 582722) KernelECDSATest:test_fail_validate_not_activate() (gas: 0) -KernelECDSATest:test_fail_validate_not_activate() (gas: 303491) -KernelECDSATest:test_fail_validate_wrongsignature() (gas: 24904) -KernelECDSATest:test_fail_validate_wrongsignature() (gas: 34133) -KernelECDSATest:test_get_nonce() (gas: 25179) -KernelECDSATest:test_get_nonce() (gas: 25201) -KernelECDSATest:test_get_nonce(uint192) (runs: 1024, μ: 19842, ~: 19842) -KernelECDSATest:test_get_nonce(uint192) (runs: 1024, μ: 19842, ~: 19842) -KernelECDSATest:test_ignore() (gas: 868) -KernelECDSATest:test_initialize() (gas: 126784) -KernelECDSATest:test_initialize() (gas: 154121) -KernelECDSATest:test_initialize_twice() (gas: 18555) -KernelECDSATest:test_initialize_twice() (gas: 18698) -KernelECDSATest:test_mode_2() (gas: 243052) -KernelECDSATest:test_mode_2() (gas: 250243) -KernelECDSATest:test_revert_when_mode_disabled() (gas: 199003) -KernelECDSATest:test_revert_when_mode_disabled() (gas: 208287) -KernelECDSATest:test_set_default_validator() (gas: 417145) -KernelECDSATest:test_set_default_validator() (gas: 423335) -KernelECDSATest:test_set_execution() (gas: 458142) -KernelECDSATest:test_set_execution() (gas: 468303) -KernelECDSATest:test_should_emit_event_on_receive() (gas: 23220) -KernelECDSATest:test_should_emit_event_on_receive() (gas: 23220) -KernelECDSATest:test_should_receive_erc1155() (gas: 701145) -KernelECDSATest:test_should_receive_erc1155() (gas: 701171) -KernelECDSATest:test_should_receive_erc1155_batch() (gas: 728411) -KernelECDSATest:test_should_receive_erc1155_batch() (gas: 728411) -KernelECDSATest:test_should_receive_erc721() (gas: 597680) -KernelECDSATest:test_should_receive_erc721() (gas: 597702) -KernelECDSATest:test_should_return_address_if_deployed() (gas: 21970) -KernelECDSATest:test_should_return_address_if_deployed() (gas: 21976) -KernelECDSATest:test_sudo() (gas: 170002) -KernelECDSATest:test_sudo() (gas: 179450) -KernelECDSATest:test_sudo_wrongSig() (gas: 102566) -KernelECDSATest:test_sudo_wrongSig() (gas: 115709) -KernelECDSATest:test_transfer_ownership() (gas: 178398) -KernelECDSATest:test_upgrade() (gas: 21292) -KernelECDSATest:test_upgrade() (gas: 21292) -KernelECDSATest:test_validateUserOp_fail_invalid_mode() (gas: 26550) -KernelECDSATest:test_validateUserOp_fail_invalid_mode() (gas: 26550) -KernelECDSATest:test_validateUserOp_fail_not_entryPoint() (gas: 30591) -KernelECDSATest:test_validateUserOp_fail_not_entryPoint() (gas: 30634) -KernelECDSATest:test_validate_signature() (gas: 155714) -KernelECDSATest:test_validate_signature() (gas: 194502) -KernelHelperTest:testIntersect(uint48,uint48,uint48,uint48) (runs: 1024, μ: 871, ~: 869) -KillSwitchValidatorTest:test_default_validator_disable() (gas: 180432) -KillSwitchValidatorTest:test_default_validator_enable() (gas: 187921) -KillSwitchValidatorTest:test_disable_mode() (gas: 182916) -KillSwitchValidatorTest:test_eip712() (gas: 15820) -KillSwitchValidatorTest:test_enable_then_mode_1() (gas: 348586) -KillSwitchValidatorTest:test_external_call_batch_execute_fail() (gas: 29622) -KillSwitchValidatorTest:test_external_call_batch_execute_success() (gas: 26558) -KillSwitchValidatorTest:test_external_call_default() (gas: 29426) -KillSwitchValidatorTest:test_external_call_execute_delegatecall_fail() (gas: 28469) -KillSwitchValidatorTest:test_external_call_execute_delegatecall_option_fail() (gas: 26439) -KillSwitchValidatorTest:test_external_call_execute_delegatecall_success() (gas: 25709) -KillSwitchValidatorTest:test_external_call_execute_fail() (gas: 27906) -KillSwitchValidatorTest:test_external_call_execute_success() (gas: 26075) -KillSwitchValidatorTest:test_external_call_execution() (gas: 510817) -KillSwitchValidatorTest:test_fail_validate_not_activate() (gas: 303601) -KillSwitchValidatorTest:test_fail_validate_wrongsignature() (gas: 34276) -KillSwitchValidatorTest:test_force_unblock() (gas: 391544) -KillSwitchValidatorTest:test_get_nonce() (gas: 25201) -KillSwitchValidatorTest:test_get_nonce(uint192) (runs: 1024, μ: 19930, ~: 19930) -KillSwitchValidatorTest:test_ignore() (gas: 956) -KillSwitchValidatorTest:test_initialize() (gas: 154209) -KillSwitchValidatorTest:test_initialize_twice() (gas: 18643) -KillSwitchValidatorTest:test_mode_2() (gas: 312761) -KillSwitchValidatorTest:test_revert_when_mode_disabled() (gas: 199157) -KillSwitchValidatorTest:test_set_default_validator() (gas: 423434) -KillSwitchValidatorTest:test_set_execution() (gas: 468424) -KillSwitchValidatorTest:test_should_emit_event_on_receive() (gas: 23330) -KillSwitchValidatorTest:test_should_fail_with_not_implemented_isValidSignature() (gas: 9236) -KillSwitchValidatorTest:test_should_fail_with_not_implemented_isValidSignature(bytes32,bytes) (runs: 1024, μ: 9601, ~: 9556) -KillSwitchValidatorTest:test_should_fail_with_not_implemented_validCaller() (gas: 10157) -KillSwitchValidatorTest:test_should_fail_with_not_implemented_validCaller(address,bytes) (runs: 1024, μ: 9749, ~: 9703) -KillSwitchValidatorTest:test_should_receive_erc1155() (gas: 701233) -KillSwitchValidatorTest:test_should_receive_erc1155_batch() (gas: 728499) -KillSwitchValidatorTest:test_should_receive_erc721() (gas: 597790) -KillSwitchValidatorTest:test_should_return_address_if_deployed() (gas: 22086) -KillSwitchValidatorTest:test_sudo() (gas: 179549) -KillSwitchValidatorTest:test_sudo_wrongSig() (gas: 115830) -KillSwitchValidatorTest:test_upgrade() (gas: 21314) -KillSwitchValidatorTest:test_validateUserOp_fail_invalid_mode() (gas: 26638) -KillSwitchValidatorTest:test_validateUserOp_fail_not_entryPoint() (gas: 30708) -KillSwitchValidatorTest:test_validate_signature() (gas: 194579) -SessionKeyValidatorTest:test_default_validator_disable() (gas: 180311) -SessionKeyValidatorTest:test_default_validator_enable() (gas: 187800) -SessionKeyValidatorTest:test_disable_mode() (gas: 182795) -SessionKeyValidatorTest:test_eip712() (gas: 15684) -SessionKeyValidatorTest:test_enable_then_mode_1() (gas: 286128) -SessionKeyValidatorTest:test_external_call_batch_execute_fail() (gas: 29534) -SessionKeyValidatorTest:test_external_call_batch_execute_success() (gas: 26536) -SessionKeyValidatorTest:test_external_call_default() (gas: 29338) -SessionKeyValidatorTest:test_external_call_execute_delegatecall_fail() (gas: 28381) -SessionKeyValidatorTest:test_external_call_execute_delegatecall_option_fail() (gas: 26351) -SessionKeyValidatorTest:test_external_call_execute_delegatecall_success() (gas: 25621) -SessionKeyValidatorTest:test_external_call_execute_fail() (gas: 27862) -SessionKeyValidatorTest:test_external_call_execute_success() (gas: 26009) -SessionKeyValidatorTest:test_external_call_execution() (gas: 510718) -SessionKeyValidatorTest:test_fail_validate_not_activate() (gas: 303513) -SessionKeyValidatorTest:test_fail_validate_wrongsignature() (gas: 34165) -SessionKeyValidatorTest:test_get_nonce() (gas: 25179) -SessionKeyValidatorTest:test_get_nonce(uint192) (runs: 1024, μ: 19842, ~: 19842) -SessionKeyValidatorTest:test_ignore() (gas: 890) -SessionKeyValidatorTest:test_initialize() (gas: 154121) -SessionKeyValidatorTest:test_initialize_twice() (gas: 18555) -SessionKeyValidatorTest:test_mode_2() (gas: 250298) -SessionKeyValidatorTest:test_revert_when_mode_disabled() (gas: 199003) -SessionKeyValidatorTest:test_scenario_batch((uint8,uint8,uint8,uint8,uint48,uint48,uint48,uint48,bool,bool,bool,bool),(uint8)) (runs: 1024, μ: 43997409, ~: 32002162) -SessionKeyValidatorTest:test_scenario_non_batch((uint8,uint8,uint8,uint8,uint48,uint48,uint48,uint48,bool,bool,bool,bool)) (runs: 1024, μ: 17511495, ~: 17372686) -SessionKeyValidatorTest:test_set_default_validator() (gas: 423335) -SessionKeyValidatorTest:test_set_execution() (gas: 468303) -SessionKeyValidatorTest:test_should_emit_event_on_receive() (gas: 23242) -SessionKeyValidatorTest:test_should_receive_erc1155() (gas: 701145) -SessionKeyValidatorTest:test_should_receive_erc1155_batch() (gas: 728411) -SessionKeyValidatorTest:test_should_receive_erc721() (gas: 597724) -SessionKeyValidatorTest:test_should_return_address_if_deployed() (gas: 22003) -SessionKeyValidatorTest:test_sudo() (gas: 179450) -SessionKeyValidatorTest:test_sudo_wrongSig() (gas: 115731) -SessionKeyValidatorTest:test_upgrade() (gas: 21292) -SessionKeyValidatorTest:test_validateUserOp_fail_invalid_mode() (gas: 26574) -SessionKeyValidatorTest:test_validateUserOp_fail_not_entryPoint() (gas: 30634) -SessionKeyValidatorTest:test_validate_signature() (gas: 194502) -TestCallee:test_ignore() (gas: 206) -TestCounter:test_ignore() (gas: 164) -TestERC1155:test_ignore() (gas: 328) -TestERC20:test_ignore() (gas: 389) -TestERC721:test_ignore() (gas: 399) -TestExecutor:test_ignore() (gas: 130) -TestPaymaster:test_ignore() (gas: 174) -TestValidator:test_ignore() (gas: 351) \ No newline at end of file +KernelECDSATest:test_fail_validate_not_activate() (gas: 374629) +KernelECDSATest:test_fail_validate_wrongsignature() (gas: 23961) +KernelECDSATest:test_fail_validate_wrongsignature() (gas: 33419) +KernelECDSATest:test_get_nonce() (gas: 24893) +KernelECDSATest:test_get_nonce() (gas: 24914) +KernelECDSATest:test_get_nonce(uint192) (runs: 1024, μ: 19517, ~: 19517) +KernelECDSATest:test_get_nonce(uint192) (runs: 1024, μ: 19561, ~: 19561) +KernelECDSATest:test_ignore() (gas: 231) +KernelECDSATest:test_initialize() (gas: 126587) +KernelECDSATest:test_initialize() (gas: 154222) +KernelECDSATest:test_initialize_twice() (gas: 18215) +KernelECDSATest:test_initialize_twice() (gas: 18218) +KernelECDSATest:test_mode_2() (gas: 243855) +KernelECDSATest:test_mode_2() (gas: 251191) +KernelECDSATest:test_revert_when_mode_disabled() (gas: 200091) +KernelECDSATest:test_revert_when_mode_disabled() (gas: 209038) +KernelECDSATest:test_set_default_validator() (gas: 488911) +KernelECDSATest:test_set_default_validator() (gas: 495350) +KernelECDSATest:test_set_execution() (gas: 530141) +KernelECDSATest:test_set_execution() (gas: 540583) +KernelECDSATest:test_should_emit_event_on_receive() (gas: 22287) +KernelECDSATest:test_should_emit_event_on_receive() (gas: 22287) +KernelECDSATest:test_should_receive_erc1155() (gas: 859668) +KernelECDSATest:test_should_receive_erc1155() (gas: 859712) +KernelECDSATest:test_should_receive_erc1155_batch() (gas: 887339) +KernelECDSATest:test_should_receive_erc1155_batch() (gas: 887340) +KernelECDSATest:test_should_receive_erc721() (gas: 625633) +KernelECDSATest:test_should_receive_erc721() (gas: 625655) +KernelECDSATest:test_should_return_address_if_deployed() (gas: 21491) +KernelECDSATest:test_should_return_address_if_deployed() (gas: 21494) +KernelECDSATest:test_sudo() (gas: 169895) +KernelECDSATest:test_sudo() (gas: 179673) +KernelECDSATest:test_sudo_wrongSig() (gas: 102225) +KernelECDSATest:test_sudo_wrongSig() (gas: 115727) +KernelECDSATest:test_transfer_ownership() (gas: 183729) +KernelECDSATest:test_upgrade() (gas: 21204) +KernelECDSATest:test_upgrade() (gas: 21311) +KernelECDSATest:test_validateUserOp_fail_invalid_mode() (gas: 26572) +KernelECDSATest:test_validateUserOp_fail_invalid_mode() (gas: 26595) +KernelECDSATest:test_validateUserOp_fail_not_entryPoint() (gas: 23483) +KernelECDSATest:test_validateUserOp_fail_not_entryPoint() (gas: 23506) +KernelECDSATest:test_validate_signature() (gas: 155498) +KernelECDSATest:test_validate_signature() (gas: 195025) +KernelECDSATypedTest:test_default_validator_disable() (gas: 185197) +KernelECDSATypedTest:test_default_validator_enable() (gas: 192490) +KernelECDSATypedTest:test_disable_mode() (gas: 186804) +KernelECDSATypedTest:test_eip712() (gas: 15059) +KernelECDSATypedTest:test_enable_then_mode_1() (gas: 291831) +KernelECDSATypedTest:test_external_call_batch_execute_fail() (gas: 28808) +KernelECDSATypedTest:test_external_call_batch_execute_success() (gas: 26322) +KernelECDSATypedTest:test_external_call_default() (gas: 28753) +KernelECDSATypedTest:test_external_call_execute_delegatecall_fail() (gas: 27727) +KernelECDSATypedTest:test_external_call_execute_delegatecall_option_fail() (gas: 25561) +KernelECDSATypedTest:test_external_call_execute_delegatecall_success() (gas: 24879) +KernelECDSATypedTest:test_external_call_execute_fail() (gas: 27986) +KernelECDSATypedTest:test_external_call_execute_success() (gas: 25343) +KernelECDSATypedTest:test_external_call_execution() (gas: 586886) +KernelECDSATypedTest:test_fail_validate_not_activate() (gas: 374629) +KernelECDSATypedTest:test_fail_validate_wrongsignature() (gas: 35730) +KernelECDSATypedTest:test_get_nonce() (gas: 24914) +KernelECDSATypedTest:test_get_nonce(uint192) (runs: 1024, μ: 19561, ~: 19561) +KernelECDSATypedTest:test_ignore() (gas: 231) +KernelECDSATypedTest:test_initialize() (gas: 153746) +KernelECDSATypedTest:test_initialize_twice() (gas: 18215) +KernelECDSATypedTest:test_mode_2() (gas: 253563) +KernelECDSATypedTest:test_revert_when_mode_disabled() (gas: 204387) +KernelECDSATypedTest:test_set_default_validator() (gas: 499602) +KernelECDSATypedTest:test_set_execution() (gas: 544835) +KernelECDSATypedTest:test_should_emit_event_on_receive() (gas: 22287) +KernelECDSATypedTest:test_should_receive_erc1155() (gas: 859712) +KernelECDSATypedTest:test_should_receive_erc1155_batch() (gas: 887339) +KernelECDSATypedTest:test_should_receive_erc721() (gas: 625655) +KernelECDSATypedTest:test_should_return_address_if_deployed() (gas: 21494) +KernelECDSATypedTest:test_sudo() (gas: 183925) +KernelECDSATypedTest:test_sudo_wrongSig() (gas: 116245) +KernelECDSATypedTest:test_upgrade() (gas: 21204) +KernelECDSATypedTest:test_validateUserOp_fail_invalid_mode() (gas: 26572) +KernelECDSATypedTest:test_validateUserOp_fail_not_entryPoint() (gas: 23483) +KernelECDSATypedTest:test_validate_signature() (gas: 191688) +KernelHelperTest:testIntersect(uint48,uint48,uint48,uint48) (runs: 1024, μ: 1415, ~: 1419) +KillSwitchValidatorTest:test_default_validator_disable() (gas: 180990) +KillSwitchValidatorTest:test_default_validator_enable() (gas: 188633) +KillSwitchValidatorTest:test_disable_mode() (gas: 182486) +KillSwitchValidatorTest:test_eip712() (gas: 15126) +KillSwitchValidatorTest:test_enable_then_mode_1() (gas: 351532) +KillSwitchValidatorTest:test_external_call_batch_execute_fail() (gas: 28897) +KillSwitchValidatorTest:test_external_call_batch_execute_success() (gas: 26278) +KillSwitchValidatorTest:test_external_call_default() (gas: 28775) +KillSwitchValidatorTest:test_external_call_execute_delegatecall_fail() (gas: 27793) +KillSwitchValidatorTest:test_external_call_execute_delegatecall_option_fail() (gas: 25605) +KillSwitchValidatorTest:test_external_call_execute_delegatecall_success() (gas: 24945) +KillSwitchValidatorTest:test_external_call_execute_fail() (gas: 28030) +KillSwitchValidatorTest:test_external_call_execute_success() (gas: 25343) +KillSwitchValidatorTest:test_external_call_execution() (gas: 582720) +KillSwitchValidatorTest:test_fail_validate_not_activate() (gas: 374629) +KillSwitchValidatorTest:test_fail_validate_wrongsignature() (gas: 33461) +KillSwitchValidatorTest:test_force_unblock() (gas: 398854) +KillSwitchValidatorTest:test_get_nonce() (gas: 24936) +KillSwitchValidatorTest:test_get_nonce(uint192) (runs: 1024, μ: 19561, ~: 19561) +KillSwitchValidatorTest:test_ignore() (gas: 209) +KillSwitchValidatorTest:test_initialize() (gas: 154273) +KillSwitchValidatorTest:test_initialize_twice() (gas: 18288) +KillSwitchValidatorTest:test_mode_2() (gas: 315241) +KillSwitchValidatorTest:test_revert_when_mode_disabled() (gas: 200109) +KillSwitchValidatorTest:test_set_default_validator() (gas: 495459) +KillSwitchValidatorTest:test_set_execution() (gas: 540647) +KillSwitchValidatorTest:test_should_emit_event_on_receive() (gas: 22287) +KillSwitchValidatorTest:test_should_fail_with_not_implemented_isValidSignature() (gas: 9199) +KillSwitchValidatorTest:test_should_fail_with_not_implemented_isValidSignature(bytes32,bytes) (runs: 1024, μ: 9503, ~: 9456) +KillSwitchValidatorTest:test_should_fail_with_not_implemented_validCaller() (gas: 9290) +KillSwitchValidatorTest:test_should_fail_with_not_implemented_validCaller(address,bytes) (runs: 1024, μ: 9793, ~: 9745) +KillSwitchValidatorTest:test_should_receive_erc1155() (gas: 859712) +KillSwitchValidatorTest:test_should_receive_erc1155_batch() (gas: 887273) +KillSwitchValidatorTest:test_should_receive_erc721() (gas: 625678) +KillSwitchValidatorTest:test_should_return_address_if_deployed() (gas: 21545) +KillSwitchValidatorTest:test_sudo() (gas: 179715) +KillSwitchValidatorTest:test_sudo_wrongSig() (gas: 115725) +KillSwitchValidatorTest:test_upgrade() (gas: 21226) +KillSwitchValidatorTest:test_validateUserOp_fail_invalid_mode() (gas: 26594) +KillSwitchValidatorTest:test_validateUserOp_fail_not_entryPoint() (gas: 23505) +KillSwitchValidatorTest:test_validate_signature() (gas: 195118) +SessionKeyValidatorTest:test_default_validator_disable() (gas: 180955) +SessionKeyValidatorTest:test_default_validator_enable() (gas: 188686) +SessionKeyValidatorTest:test_disable_mode() (gas: 182538) +SessionKeyValidatorTest:test_eip712() (gas: 15166) +SessionKeyValidatorTest:test_enable_then_mode_1() (gas: 287585) +SessionKeyValidatorTest:test_external_call_batch_execute_fail() (gas: 28913) +SessionKeyValidatorTest:test_external_call_batch_execute_success() (gas: 26404) +SessionKeyValidatorTest:test_external_call_default() (gas: 28820) +SessionKeyValidatorTest:test_external_call_execute_delegatecall_fail() (gas: 27749) +SessionKeyValidatorTest:test_external_call_execute_delegatecall_option_fail() (gas: 25605) +SessionKeyValidatorTest:test_external_call_execute_delegatecall_success() (gas: 25010) +SessionKeyValidatorTest:test_external_call_execute_fail() (gas: 28030) +SessionKeyValidatorTest:test_external_call_execute_success() (gas: 25387) +SessionKeyValidatorTest:test_external_call_execution() (gas: 582763) +SessionKeyValidatorTest:test_fail_validate_not_activate() (gas: 374629) +SessionKeyValidatorTest:test_fail_validate_wrongsignature() (gas: 33448) +SessionKeyValidatorTest:test_get_nonce() (gas: 24914) +SessionKeyValidatorTest:test_get_nonce(uint192) (runs: 1024, μ: 19584, ~: 19584) +SessionKeyValidatorTest:test_ignore() (gas: 209) +SessionKeyValidatorTest:test_initialize() (gas: 154229) +SessionKeyValidatorTest:test_initialize_twice() (gas: 18244) +SessionKeyValidatorTest:test_mode_2() (gas: 251232) +SessionKeyValidatorTest:test_revert_when_mode_disabled() (gas: 200149) +SessionKeyValidatorTest:test_scenario_batch((uint8,uint8,uint8,uint8,uint48,uint48,uint48,uint48,bool,bool,bool,bool,bool),(uint8)) (runs: 1024, μ: 58166090, ~: 56971973) +SessionKeyValidatorTest:test_scenario_non_batch((uint8,uint8,uint8,uint8,uint48,uint48,uint48,uint48,bool,bool,bool,bool,bool)) (runs: 1024, μ: 54097883, ~: 56753303) +SessionKeyValidatorTest:test_set_default_validator() (gas: 495379) +SessionKeyValidatorTest:test_set_execution() (gas: 540624) +SessionKeyValidatorTest:test_should_emit_event_on_receive() (gas: 22287) +SessionKeyValidatorTest:test_should_receive_erc1155() (gas: 859735) +SessionKeyValidatorTest:test_should_receive_erc1155_batch() (gas: 887339) +SessionKeyValidatorTest:test_should_receive_erc721() (gas: 625655) +SessionKeyValidatorTest:test_should_return_address_if_deployed() (gas: 21545) +SessionKeyValidatorTest:test_sudo() (gas: 179702) +SessionKeyValidatorTest:test_sudo_wrongSig() (gas: 115756) +SessionKeyValidatorTest:test_upgrade() (gas: 21204) +SessionKeyValidatorTest:test_validateUserOp_fail_invalid_mode() (gas: 26550) +SessionKeyValidatorTest:test_validateUserOp_fail_not_entryPoint() (gas: 23483) +SessionKeyValidatorTest:test_validate_signature() (gas: 195105) +TestCallee:test_ignore() (gas: 184) +TestCounter:test_ignore() (gas: 120) +TestERC1155:test_ignore() (gas: 186) +TestERC20:test_ignore() (gas: 164) +TestERC721:test_ignore() (gas: 140) +TestExecutor:test_ignore() (gas: 120) +TestPaymaster:test_ignore() (gas: 120) +TestValidator:test_ignore() (gas: 206) +WebAuthnFclValidatorTest:test_default_validator_disable() (gas: 892468) +WebAuthnFclValidatorTest:test_default_validator_enable() (gas: 917513) +WebAuthnFclValidatorTest:test_disable_mode() (gas: 906453) +WebAuthnFclValidatorTest:test_dontRevertForDummySig() (gas: 33996) +WebAuthnFclValidatorTest:test_eip712() (gas: 15126) +WebAuthnFclValidatorTest:test_enable_then_mode_1() (gas: 1350842) +WebAuthnFclValidatorTest:test_external_call_batch_execute_fail() (gas: 23988) +WebAuthnFclValidatorTest:test_external_call_batch_execute_success() (gas: 0) +WebAuthnFclValidatorTest:test_external_call_default() (gas: 0) +WebAuthnFclValidatorTest:test_external_call_execute_delegatecall_fail() (gas: 0) +WebAuthnFclValidatorTest:test_external_call_execute_delegatecall_option_fail() (gas: 20913) +WebAuthnFclValidatorTest:test_external_call_execute_delegatecall_success() (gas: 0) +WebAuthnFclValidatorTest:test_external_call_execute_fail() (gas: 21042) +WebAuthnFclValidatorTest:test_external_call_execute_success() (gas: 0) +WebAuthnFclValidatorTest:test_external_call_execution() (gas: 0) +WebAuthnFclValidatorTest:test_fail_validate_InvalidWebAuthnData() (gas: 381009) +WebAuthnFclValidatorTest:test_fail_validate_not_activate() (gas: 374618) +WebAuthnFclValidatorTest:test_fail_validate_wrongsignature() (gas: 763214) +WebAuthnFclValidatorTest:test_get_nonce() (gas: 24914) +WebAuthnFclValidatorTest:test_get_nonce(uint192) (runs: 1024, μ: 19561, ~: 19561) +WebAuthnFclValidatorTest:test_initialize() (gas: 178292) +WebAuthnFclValidatorTest:test_initialize_twice() (gas: 20501) +WebAuthnFclValidatorTest:test_mode_2() (gas: 1311901) +WebAuthnFclValidatorTest:test_revert_when_mode_disabled() (gas: 924091) +WebAuthnFclValidatorTest:test_set_default_validator() (gas: 1211266) +WebAuthnFclValidatorTest:test_set_execution() (gas: 1276676) +WebAuthnFclValidatorTest:test_should_emit_event_on_receive() (gas: 22287) +WebAuthnFclValidatorTest:test_should_receive_erc1155() (gas: 859701) +WebAuthnFclValidatorTest:test_should_receive_erc1155_batch() (gas: 887273) +WebAuthnFclValidatorTest:test_should_receive_erc721() (gas: 625633) +WebAuthnFclValidatorTest:test_should_return_address_if_deployed() (gas: 23765) +WebAuthnFclValidatorTest:test_sudo() (gas: 906016) +WebAuthnFclValidatorTest:test_sudo_wrongSig() (gas: 841531) +WebAuthnFclValidatorTest:test_upgrade() (gas: 21226) +WebAuthnFclValidatorTest:test_validateUserOp_fail_invalid_mode() (gas: 26594) +WebAuthnFclValidatorTest:test_validateUserOp_fail_not_entryPoint() (gas: 23483) +WebAuthnFclValidatorTest:test_validate_signature() (gas: 751849) +WebAuthnFclValidatorTest:test_webAuthnSignatureGeneration(bytes32,uint256) (runs: 1024, μ: 1277845, ~: 1199457) +WebAuthnFclValidatorTest:test_webAuthnSignatureGeneration_solo() (gas: 1116434) \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 744ac54b..df6cfb84 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "lib/I4337"] path = lib/I4337 url = https://github.com/leekt/I4337 +[submodule "lib/FreshCryptoLib"] + path = lib/FreshCryptoLib + url = https://github.com/rdubois-crypto/FreshCryptoLib diff --git a/lib/FreshCryptoLib b/lib/FreshCryptoLib new file mode 160000 index 00000000..1a484cdf --- /dev/null +++ b/lib/FreshCryptoLib @@ -0,0 +1 @@ +Subproject commit 1a484cdfd810046203f37b3e3c794d05720fb99c diff --git a/lib/forge-std b/lib/forge-std index 2f112697..ae570fec 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 2f112697506eab12d433a65fdc31a639548fe365 +Subproject commit ae570fec082bfe1c1f45b0acca4a2b4f84d345ce diff --git a/remappings.txt b/remappings.txt index 82c83131..55b13aad 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,5 +1,4 @@ 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/script/DeployWebAuthnFclValidator.sol b/script/DeployWebAuthnFclValidator.sol new file mode 100644 index 00000000..235e1b5b --- /dev/null +++ b/script/DeployWebAuthnFclValidator.sol @@ -0,0 +1,24 @@ +pragma solidity ^0.8.0; + +import "src/factory/KernelFactory.sol"; +import "src/utils/P256VerifierWrapper.sol"; +import "src/validator/webauthn//WebAuthnFclValidator.sol"; +import "forge-std/Script.sol"; +import "forge-std/console.sol"; + +contract DeployWebAuthnFclValidator is Script { + + function run() public { + uint256 key = vm.envUint("DEPLOYER_PRIVATE_KEY"); + vm.startBroadcast(key); + + P256VerifierWrapper p256VerifierWrapper = new P256VerifierWrapper{salt:0}(); + console.log("p256 wrapper address: %s", address(p256VerifierWrapper)); + + WebAuthnFclValidator validator = new WebAuthnFclValidator{salt:0}(address(p256VerifierWrapper)); + console.log("validator address: %s", address(validator)); + + vm.stopBroadcast(); + } +} + diff --git a/src/utils/P256VerifierWrapper.sol b/src/utils/P256VerifierWrapper.sol new file mode 100644 index 00000000..bc6340ac --- /dev/null +++ b/src/utils/P256VerifierWrapper.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {FCL_ecdsa} from "FreshCryptoLib/FCL_ecdsa.sol"; + +/// @title P256VerifierWrapper +/// @author rdubois-crypto +/// @author KONFeature +/// @notice Wrapper arround the P256Verifier contract of @rdubois-crypto, using it to accept EIP-7212 compliant verification (p256 pre-compiled curve) +/// @dev This lib is only a wrapper around the P256Verifier contract. +/// It will call the verifySignature function of the P256Verifier contract. +/// Once the RIP-7212 will be deployed and effective, this contract will be useless. +/// Tracker on polygon: PR: https://github.com/maticnetwork/bor/pull/1069 +/// Now waiting on the Napoli hardfork to be deployed +contract P256VerifierWrapper { + /** + * Precompiles don't use a function signature. The first byte of callldata + * is the first byte of an input argument. In this case: + * + * input[ 0: 32] = signed data hash + * input[ 32: 64] = signature r + * input[ 64: 96] = signature s + * input[ 96:128] = public key x + * input[128:160] = public key y + * + * result[ 0: 32] = 0x00..00 (invalid) or 0x00..01 (valid) + * + * For details, see https://eips.ethereum.org/EIPS/eip-7212 + */ + fallback(bytes calldata input) external returns (bytes memory) { + if (input.length != 160) { + return abi.encodePacked(uint256(0)); + } + + bytes32 hash = bytes32(input[0:32]); + uint256 r = uint256(bytes32(input[32:64])); + uint256 s = uint256(bytes32(input[64:96])); + uint256 x = uint256(bytes32(input[96:128])); + uint256 y = uint256(bytes32(input[128:160])); + + uint256 ret = FCL_ecdsa.ecdsa_verify(hash, r, s, x, y) ? 1 : 0; + + return abi.encodePacked(ret); + } +} diff --git a/src/validator/webauthn/WebAuthnFclValidator.sol b/src/validator/webauthn/WebAuthnFclValidator.sol new file mode 100644 index 00000000..6169ed39 --- /dev/null +++ b/src/validator/webauthn/WebAuthnFclValidator.sol @@ -0,0 +1,124 @@ +// 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 {WebAuthnFclVerifier} from "./WebAuthnFclVerifier.sol"; + +/// @dev Storage layout for a kernel in the WebAuthnValidator contract. +struct WebAuthnFclValidatorStorage { + /// @dev The `x` coord of the secp256r1 public key used to sign the user operation. + uint256 x; + /// @dev The `y` coord of the secp256r1 public key used to sign the user operation. + uint256 y; +} + +/// @author @KONFeature +/// @title WebAuthnFclValidator +/// @notice Kernel validator used to validated user operations via WebAuthn signature (using P256 under the hood) +/// @notice Using the awesome FreshCryptoLib: https://github.com/rdubois-crypto/FreshCryptoLib/ +/// @notice Inspired by the cometh Gnosis Safe signer: https://github.com/cometh-game/p256-signer +contract WebAuthnFclValidator is IKernelValidator { + /// @dev Event emitted when the public key signing the WebAuthN user operation is changed for a given `kernel`. + event WebAuthnPublicKeyChanged(address indexed kernel, uint256 x, uint256 y); + + /// @dev Mapping of kernel address to each webAuthn specific storage + mapping(address kernel => WebAuthnFclValidatorStorage webAuthnStorage) private webAuthnValidatorStorage; + + /// @dev The address of the p256 verifier contract (should be 0x100 on the RIP-7212 compliant chains) + /// @dev To follow up for the deployment: https://forum.polygon.technology/t/pip-27-precompiled-for-secp256r1-curve-support/13049 + address public immutable P256_VERIFIER; + + /// @dev Simple constructor, setting the P256 verifier address + constructor(address _p256Verifier) { + P256_VERIFIER = _p256Verifier; + } + + /// @dev Disable this validator for a given `kernel` (msg.sender) + function disable(bytes calldata) external payable override { + delete webAuthnValidatorStorage[msg.sender]; + } + + /// @dev Enable this validator for a given `kernel` (msg.sender) + function enable(bytes calldata _data) external payable override { + // Extract the x & y coordinates of the public key from the `_data` bytes + (uint256 x, uint256 y) = abi.decode(_data, (uint256, uint256)); + // Update the pub key data + WebAuthnFclValidatorStorage storage kernelValidatorStorage = webAuthnValidatorStorage[msg.sender]; + kernelValidatorStorage.x = x; + kernelValidatorStorage.y = y; + // Emit the update event + emit WebAuthnPublicKeyChanged(msg.sender, x, y); + } + + /// @dev Validate a `_userOp` using a WebAuthn Signature for the kernel account who is the `_userOp` sender + function validateUserOp(UserOperation calldata _userOp, bytes32 _userOpHash, uint256) + external + payable + override + returns (ValidationData validationData) + { + WebAuthnFclValidatorStorage memory kernelValidatorStorage = webAuthnValidatorStorage[_userOp.sender]; + + // Perform a check against the direct userOpHash, if ok consider the user op as validated + if (_checkSignature(kernelValidatorStorage, _userOpHash, _userOp.signature)) { + return ValidationData.wrap(0); + } + + return SIG_VALIDATION_FAILED; + } + + /// @dev Validate a `_signature` of the `_hash` ofor the given `kernel` (msg.sender) + function validateSignature(bytes32 _hash, bytes calldata _signature) + public + view + override + returns (ValidationData) + { + WebAuthnFclValidatorStorage memory kernelValidatorStorage = webAuthnValidatorStorage[msg.sender]; + + // Check the validity againt the hash directly + if (_checkSignature(kernelValidatorStorage, _hash, _signature)) { + return ValidationData.wrap(0); + } + + // Otherwise, all good + return SIG_VALIDATION_FAILED; + } + + /// @notice Validates the given `_signature` againt the `_hash` for the given `kernel` (msg.sender) + /// @param _kernelValidatorStorage The kernel storage replication (helping us to fetch the X & Y points of the public key) + /// @param _hash The hash signed + /// @param _signature The signature + function _checkSignature( + WebAuthnFclValidatorStorage memory _kernelValidatorStorage, + bytes32 _hash, + bytes calldata _signature + ) private view returns (bool isValid) { + return WebAuthnFclVerifier._verifyWebAuthNSignature( + P256_VERIFIER, _hash, _signature, _kernelValidatorStorage.x, _kernelValidatorStorage.y + ); + } + + /// @dev Check if the caller is a valid signer, this don't apply to the WebAuthN validator, since it's using a public key + function validCaller(address, bytes calldata) external pure override returns (bool) { + revert NotImplemented(); + } + + /* -------------------------------------------------------------------------- */ + /* Public view methods */ + /* -------------------------------------------------------------------------- */ + + /// @dev Get the owner of a given `kernel` + function getPublicKey(address _kernel) public view returns (uint256 x, uint256 y) { + // Compute the storage slot + WebAuthnFclValidatorStorage storage kernelValidatorStorage = webAuthnValidatorStorage[_kernel]; + + // Access it for x and y + x = kernelValidatorStorage.x; + y = kernelValidatorStorage.y; + } +} diff --git a/src/validator/webauthn/WebAuthnFclVerifier.sol b/src/validator/webauthn/WebAuthnFclVerifier.sol new file mode 100644 index 00000000..1e1f3ebb --- /dev/null +++ b/src/validator/webauthn/WebAuthnFclVerifier.sol @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Base64} from "solady/utils/Base64.sol"; + +/// @title WebAuthnFclVerifier +/// @author rdubois-crypto +/// @author obatirou +/// @author KONFeature +/// @notice A library used to format webauthn stuff into verifiable p256 messages msg +/// From https://github.com/cometh-hq/p256-signer/blob/09319213276da69aad6d96fa75cd339726f78bb9/contracts/P256Signer.sol +/// And https://github.com/rdubois-crypto/FreshCryptoLib/blob/master/solidity/src/FCL_Webauthn.sol +library WebAuthnFclVerifier { + /// @dev Error thrown when the webauthn data is invalid + error InvalidWebAuthNData(); + + /// @dev 'bytes4(keccak256("InvalidWebAuthNData()"))' + uint256 private constant _INVALID_WEBAUTHN_DATA_SELECTOR = 0x81177746; + + /// @dev the data flag mask we will use to verify the signature + /// @dev Always 0x01 for user presence flag -> https://www.w3.org/TR/webauthn-2/#concept-user-present + bytes1 private constant AUTHENTICATOR_DATA_FLAG_MASK = 0x01; + + /// @dev layout of a signature (used to extract the reauired payload from the initial calldata) + struct FclSignatureLayout { + bytes authenticatorData; + bytes clientData; + uint256 challengeOffset; + uint256[2] rs; + } + + /// @dev Format the webauthn challenge into a p256 message + /// @dev return the raw message that has been signed by the user on the p256 curve + /// @dev Logic from https://github.com/rdubois-crypto/FreshCryptoLib/blob/master/solidity/src/FCL_Webauthn.sol + /// @param _hash The hash that has been signed via WebAuthN + /// @param _signature The signature that has been provided with the userOp + /// @return p256Message The message that has been signed on the p256 curve + function _formatWebAuthNChallenge(bytes32 _hash, FclSignatureLayout calldata _signature) + internal + pure + returns (bytes32 p256Message) + { + // Extract a few calldata pointer we will use to format / verify our msg + bytes calldata authenticatorData = _signature.authenticatorData; + bytes calldata clientData = _signature.clientData; + uint256 challengeOffset = _signature.challengeOffset; + + // If the challenge offset is uint256 max, it's mean that we are in the case of a dummy sig, so we can skip the check and just return the hash + if (challengeOffset == type(uint256).max) { + return _hash; + } + + // Otherwise, perform the complete format and checks of the data + { + // Let the caller check if User Presence (0x01) or User Verification (0x04) are set + if ((authenticatorData[32] & AUTHENTICATOR_DATA_FLAG_MASK) != AUTHENTICATOR_DATA_FLAG_MASK) { + revert InvalidWebAuthNData(); + } + // Verify that clientData commits to the expected client challenge + // Use the Base64Url encoding which omits padding characters to match WebAuthn Specification + bytes memory challengeEncoded = bytes(Base64.encode(abi.encodePacked(_hash), true, true)); + + // The part that will old the challenge extracted from the clientData + bytes memory challengeExtracted = new bytes(challengeEncoded.length); + + assembly { + // Extract the challenge from the clientData + calldatacopy( + add(challengeExtracted, 32), add(clientData.offset, challengeOffset), mload(challengeExtracted) + ) + + // Check that the challenge extracted from the clientData is the same as the one provided in the userOp + if iszero(eq( + // Hash of the challenge exracted from the `clientData` + keccak256(add(challengeExtracted, 32), mload(challengeExtracted)), + // Hash of the provided challenge, encoded in Base64Url (to match the clientData encoding) + keccak256(add(challengeEncoded, 32), mload(challengeEncoded)) + )) { + mstore(0x00, _INVALID_WEBAUTHN_DATA_SELECTOR) + revert(0x1c, 0x04) + } + } + } + + // Verify the signature over sha256(authenticatorData || sha256(clientData)) + bytes memory verifyData = new bytes(authenticatorData.length + 32); + + assembly { + // Add the authenticator data at the start of the verifyData + calldatacopy(add(verifyData, 32), authenticatorData.offset, authenticatorData.length) + } + + bytes32 clientDataHashed = sha256(clientData); + assembly { + // Add the client data hash at the end of the verifyData + mstore(add(verifyData, add(authenticatorData.length, 32)), clientDataHashed) + } + + // Return the sha256 of the verifyData + return sha256(verifyData); + } + + /// @dev Proceed to the full webauth verification + /// @param _p256Verifier The p256 verifier contract + /// @param _hash The hash that has been signed via WebAuthN + /// @param _signature The signature that has been provided with the userOp + /// @param _x The X point of the public key + /// @param _y The Y point of the public key + /// @return isValid True if the signature is valid, false otherwise + function _verifyWebAuthNSignature( + address _p256Verifier, + bytes32 _hash, + bytes calldata _signature, + uint256 _x, + uint256 _y + ) internal view returns (bool isValid) { + // Extract the signature + FclSignatureLayout calldata signature; + // This code should precalculate the offsets of variables as defined in the layout + // From: https://twitter.com/k06a/status/1706934230779883656 + assembly { + signature := _signature.offset + } + + // Format the webauthn challenge into a p256 message + bytes32 challenge = _formatWebAuthNChallenge(_hash, signature); + + // Prepare the argument we will use to verify the signature + bytes memory args = abi.encode(challenge, signature.rs[0], signature.rs[1], _x, _y); + + // Send the call the the p256 verifier + (bool success, bytes memory ret) = _p256Verifier.staticcall(args); + assert(success); // never reverts, always returns 0 or 1 + + // Ensure that it has returned 1 + return abi.decode(ret, (uint256)) == 1; + } +} diff --git a/test/foundry/validator/WebAuthnFclValidator.t.sol b/test/foundry/validator/WebAuthnFclValidator.t.sol new file mode 100644 index 00000000..8cd95139 --- /dev/null +++ b/test/foundry/validator/WebAuthnFclValidator.t.sol @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IEntryPoint} from "I4337/interfaces/IEntryPoint.sol"; +import "src/Kernel.sol"; +import "forge-std/Test.sol"; +import {ERC4337Utils} from "src/utils/ERC4337Utils.sol"; +import {KernelTestBase} from "src/utils/KernelTestBase.sol"; +import {TestExecutor} from "src/mock/TestExecutor.sol"; +import {TestValidator} from "src/mock/TestValidator.sol"; +import {WebAuthnFclVerifier} from "src/validator/webauthn/WebAuthnFclVerifier.sol"; +import {P256VerifierWrapper} from "src/utils/P256VerifierWrapper.sol"; +import {WebAuthnFclValidator} from "src/validator/webauthn/WebAuthnFclValidator.sol"; +import {FCL_ecdsa_utils} from "FreshCryptoLib/FCL_ecdsa_utils.sol"; +import {Base64Url} from "FreshCryptoLib/utils/Base64Url.sol"; +import {IKernel} from "src/interfaces/IKernel.sol"; + +using ERC4337Utils for IEntryPoint; + +contract WebAuthnFclValidatorTest is KernelTestBase { + WebAuthnFclValidator private webAuthNValidator; + WebAuthNTester private webAuthNTester; + P256VerifierWrapper private p256VerifierWrapper; + + // Curve order (number of points) + uint256 constant n = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551; + + // The public key of the owner + uint256 x; + uint256 y; + + function setUp() public { + // Deploy a RIP-7212 compliant P256Verifier contract + p256VerifierWrapper = new P256VerifierWrapper(); + // Deploy a WebAuthnFclValidator contract using that RIP-7212 compliant P256Verifier contract + webAuthNValidator = new WebAuthnFclValidator(address(p256VerifierWrapper)); + + // Deploy a webAuthNTester that will be used to format the signature during test + webAuthNTester = new WebAuthNTester(); + + _initialize(); + (x, y) = _getPublicKey(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); + bytes memory signature = _generateWebAuthnSignature(ownerKey, _hash); + return abi.encodePacked(bytes4(0x00000000), signature); + } + + 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, webAuthNValidator, abi.encode(x, y)); + } + + function test_default_validator_enable() external override { + UserOperation memory op = buildUserOperation( + abi.encodeWithSelector( + IKernel.execute.selector, + address(webAuthNValidator), + 0, + abi.encodeWithSelector(webAuthNValidator.enable.selector, abi.encode(x, y)), + Operation.Call + ) + ); + performUserOperationWithSig(op); + (uint256 x2, uint256 y2) = WebAuthnFclValidator(address(webAuthNValidator)).getPublicKey(address(kernel)); + _assertPublicKey(x2, y2, x, y); + } + + function test_default_validator_disable() external override { + UserOperation memory op = buildUserOperation( + abi.encodeWithSelector( + IKernel.execute.selector, + address(webAuthNValidator), + 0, + abi.encodeWithSelector(webAuthNValidator.disable.selector, ""), + Operation.Call + ) + ); + performUserOperationWithSig(op); + (uint256 x2, uint256 y2) = WebAuthnFclValidator(address(webAuthNValidator)).getPublicKey(address(kernel)); + _assertPublicKey(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 test_validate_signature() external override { + bytes32 _hash = keccak256(abi.encodePacked("hello world")); + + bytes32 digest = keccak256( + abi.encodePacked( + "\x19\x01", ERC4337Utils._buildDomainSeparator(KERNEL_NAME, KERNEL_VERSION, address(kernel)), _hash + ) + ); + + bytes memory signature = signHash(digest); + + assertEq(kernel.isValidSignature(_hash, signature), Kernel.isValidSignature.selector); + } + + function test_fail_validate_wrongsignature() external override { + // Prepare the hash to sign + bytes32 _hash = keccak256(abi.encodePacked("hello world")); + bytes32 digest = keccak256( + abi.encodePacked( + "\x19\x01", ERC4337Utils._buildDomainSeparator(KERNEL_NAME, KERNEL_VERSION, address(kernel)), _hash + ) + ); + + // Sign it (via a wrong signer) + bytes memory sig = getWrongSignature(digest); + assertEq(kernel.isValidSignature(_hash, sig), bytes4(0xffffffff)); + } + + function test_fail_validate_InvalidWebAuthnData() external { + // Prepare the data to sign + bytes32 _hash = keccak256(abi.encodePacked("hello world")); + bytes32 digest = keccak256( + abi.encodePacked( + "\x19\x01", ERC4337Utils._buildDomainSeparator(KERNEL_NAME, KERNEL_VERSION, address(kernel)), _hash + ) + ); + + bytes32 _wrongHash = keccak256(abi.encodePacked("bye world")); + + // Sign it + bytes memory sig = signHash(digest); + + // Ensure it's reverting + vm.expectRevert(WebAuthnFclVerifier.InvalidWebAuthNData.selector); + kernel.isValidSignature(_wrongHash, sig); + } + + function signUserOp(UserOperation memory op) internal view override returns (bytes memory) { + bytes32 _hash = entryPoint.getUserOpHash(op); + bytes memory signature = _generateWebAuthnSignature(ownerKey, _hash); + return abi.encodePacked(bytes4(0x00000000), signature); + } + + function getWrongSignature(UserOperation memory op) internal view override returns (bytes memory) { + bytes32 _hash = entryPoint.getUserOpHash(op); + bytes memory signature = _generateWebAuthnSignature(ownerKey + 1, _hash); + return abi.encodePacked(bytes4(0x00000000), signature); + } + + function signHash(bytes32 _hash) internal view override returns (bytes memory) { + return _generateWebAuthnSignature(ownerKey, _hash); + } + + function getWrongSignature(bytes32 _hash) internal view override returns (bytes memory) { + return _generateWebAuthnSignature(ownerKey + 1, _hash); + } + + function _assertPublicKey(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"); + } + + /// @dev Ensure that the validation won't revert when using the dummy signature bypass (challenge offset to uint256.max) + function test_dontRevertForDummySig() public { + // Build rly dummy data for authenticator data and client data + bytes memory authenticatorData = hex"1312"; + bytes memory clientData = hex"1312"; + // Set the client challenge data offset to the max value + uint256 clientChallengeDataOffset = type(uint256).max; + + // Build an incoherent signature + uint256[2] memory rs = [type(uint256).max, type(uint256).max]; + + // Encode all of that into a signature + bytes memory signature = abi.encode(authenticatorData, clientData, clientChallengeDataOffset, rs); + + // Check the sig (and ensure we didn't revert here) + bool isValid = webAuthNTester.verifySignature(address(p256VerifierWrapper), bytes32(0), signature, x, y); + assertEq(isValid, false); + + // Ensure we can go through the validator with that signature + ValidationData validationData = webAuthNValidator.validateSignature(bytes32(0), signature); + assertEq(ValidationData.unwrap(validationData), 1); + } + + /// @dev Ensure that our flow to generate a webauthn signature is working + function test_webAuthnSignatureGeneration(bytes32 _hash, uint256 _privateKey) public { + vm.assume(_privateKey > 0); + vm.assume(_privateKey < n); + (uint256 pubX, uint256 pubY) = _getPublicKey(_privateKey); + + // Build all the data required + (bytes32 msgToSign, bytes memory authenticatorData, bytes memory clientData, uint256 clientChallengeDataOffset) + = _prepapreWebAuthnMsg(_hash); + + // Then sign them + (uint256 r, uint256 s) = _getP256Signature(_privateKey, msgToSign); + uint256[2] memory rs = [r, s]; + + // Encode all of that into a signature + bytes memory signature = abi.encode(authenticatorData, clientData, clientChallengeDataOffset, rs); + + // Ensure the signature is valid + bool isValid = webAuthNTester.verifySignature(address(p256VerifierWrapper), _hash, signature, pubX, pubY); + assertEq(isValid, true); + } + + /// @dev Ensure that our flow to generate a webauthn signature is working + function test_webAuthnSignatureGeneration_solo() public { + uint256 _privateKey = 0x1; + bytes32 _hash = keccak256(abi.encodePacked("hello world")); + (uint256 pubX, uint256 pubY) = _getPublicKey(_privateKey); + + // Build all the data required + (bytes32 msgToSign, bytes memory authenticatorData, bytes memory clientData, uint256 clientChallengeDataOffset) + = _prepapreWebAuthnMsg(_hash); + + // Then sign them + (uint256 r, uint256 s) = _getP256Signature(_privateKey, msgToSign); + uint256[2] memory rs = [r, s]; + + // Encode all of that into a signature + bytes memory signature = abi.encode(authenticatorData, clientData, clientChallengeDataOffset, rs); + + // Ensure the signature is valid + bool isValid = webAuthNTester.verifySignature(address(p256VerifierWrapper), _hash, signature, pubX, pubY); + assertEq(isValid, true); + } + + /* -------------------------------------------------------------------------- */ + /* Signature & P256 helper functions */ + /* -------------------------------------------------------------------------- */ + + /// @dev Generate a webauthn signature for the given `_hash` using the given `_privateKey` + function _generateWebAuthnSignature(uint256 _privateKey, bytes32 _hash) + internal + view + returns (bytes memory signature) + { + (bytes32 msgToSign, bytes memory authenticatorData, bytes memory clientData, uint256 clientChallengeDataOffset) + = _prepapreWebAuthnMsg(_hash); + + // Get the signature + (uint256 r, uint256 s) = _getP256Signature(_privateKey, msgToSign); + uint256[2] memory rs = [r, s]; + + // Return the signature + return abi.encode(authenticatorData, clientData, clientChallengeDataOffset, rs); + } + + /// @dev Prepare all the base data needed to perform a webauthn signature o n the given `_hash` + function _prepapreWebAuthnMsg(bytes32 _hash) + internal + view + returns ( + bytes32 msgToSign, + bytes memory authenticatorData, + bytes memory clientData, + uint256 clientChallengeDataOffset + ) + { + // Base Mapping of the message + bytes memory encodedChallenge = bytes(Base64Url.encode(abi.encodePacked(_hash))); + + // Prepare the authenticator data (from a real webauthn challenge) + authenticatorData = hex"49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000"; + + // Prepare the client data (starting from a real webauthn challenge, then replacing only the bytes needed for the challenge) + bytes memory clientDataStart = hex"7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a22"; + bytes memory clientDataEnd = + hex"222c226f726967696e223a22687474703a2f2f6c6f63616c686f73743a33303032222c2263726f73734f726967696e223a66616c73657d"; + clientData = bytes.concat(clientDataStart, encodedChallenge, clientDataEnd); + clientChallengeDataOffset = 36; + + // Build the signature layout + WebAuthnFclVerifier.FclSignatureLayout memory sigLayout = WebAuthnFclVerifier.FclSignatureLayout({ + authenticatorData: authenticatorData, + clientData: clientData, + challengeOffset: clientChallengeDataOffset, + // R/S not needed since the formatter will only use the other data + rs: [uint256(0), uint256(0)] + }); + + // Format it + msgToSign = webAuthNTester.formatSigLayout(_hash, sigLayout); + } + + /// @dev Get a public key for a p256 user, from the given `_privateKey` + function _getPublicKey(uint256 _privateKey) internal view returns (uint256, uint256) { + return FCL_ecdsa_utils.ecdsa_derivKpub(_privateKey); + } + + /// P256 curve order n/2 for malleability check + uint256 constant P256_N_DIV_2 = 57896044605178124381348723474703786764998477612067880171211129530534256022184; + + /// @dev Generate a p256 signature, from the given `_privateKey` on the given `_hash` + function _getP256Signature(uint256 _privateKey, bytes32 _hash) internal view returns (uint256, uint256) { + // Generate the signature using the k value and the private key + (bytes32 r, bytes32 s) = vm.signP256(_privateKey, _hash); + return (uint256(r), uint256(s)); + } +} + +/// @dev simple contract to format a webauthn challenge (using to convert stuff in memory during test to calldata) +contract WebAuthNTester { + function formatSigLayout(bytes32 _hash, WebAuthnFclVerifier.FclSignatureLayout calldata signatureLayout) + public + view + returns (bytes32) + { + console.log("hash: %d", uint256(_hash)); + return WebAuthnFclVerifier._formatWebAuthNChallenge(_hash, signatureLayout); + } + + function verifySignature(address _p256Verifier, bytes32 _hash, bytes calldata _signature, uint256 _x, uint256 _y) + public + view + returns (bool) + { + return WebAuthnFclVerifier._verifyWebAuthNSignature(_p256Verifier, _hash, _signature, _x, _y); + } +}