From e2dca84fe3c5d84e10fee777ed0394e03de748cf Mon Sep 17 00:00:00 2001 From: yoavelmalem Date: Thu, 25 Apr 2024 18:17:26 +0300 Subject: [PATCH 001/103] Added first batch of setup confs --- .../AllowanceModule_builtin_assertions.conf | 25 ++++++ certora/confs/AllowanceModule_sanity.conf | 26 ++++++ ...ule_sanity_with_all_default_summaries.conf | 28 ++++++ .../AllowanceModule_sanity_with_erc20cvl.conf | 28 ++++++ ...nceModule_sanity_with_erc20dispatched.conf | 29 ++++++ .../Safe4337Module_builtin_assertions.conf | 26 ++++++ certora/confs/Safe4337Module_sanity.conf | 23 +++++ ...ule_sanity_with_all_default_summaries.conf | 28 ++++++ .../Safe4337Module_sanity_with_erc20cvl.conf | 26 ++++++ ...337Module_sanity_with_erc20dispatched.conf | 27 ++++++ .../SafeModuleSetup_builtin_assertions.conf | 25 ++++++ certora/confs/SafeModuleSetup_sanity.conf | 23 +++++ ...tup_sanity_with_all_default_summaries.conf | 26 ++++++ .../SafeModuleSetup_sanity_with_erc20cvl.conf | 26 ++++++ ...duleSetup_sanity_with_erc20dispatched.conf | 27 ++++++ ...afeSignerLaunchpad_builtin_assertions.conf | 24 +++++ certora/confs/SafeSignerLaunchpad_sanity.conf | 23 +++++ ...pad_sanity_with_all_default_summaries.conf | 26 ++++++ ...eSignerLaunchpad_sanity_with_erc20cvl.conf | 26 ++++++ ...Launchpad_sanity_with_erc20dispatched.conf | 27 ++++++ ...AuthnSignerFactory_builtin_assertions.conf | 26 ++++++ .../SafeWebAuthnSignerFactory_sanity.conf | 25 ++++++ ...ory_sanity_with_all_default_summaries.conf | 27 ++++++ ...thnSignerFactory_sanity_with_erc20cvl.conf | 26 ++++++ ...erFactory_sanity_with_erc20dispatched.conf | 27 ++++++ ...SafeWebAuthnSigner_builtin_assertions.conf | 28 ++++++ certora/confs/SafeWebAuthnSigner_sanity.conf | 25 ++++++ ...ner_sanity_with_all_default_summaries.conf | 27 ++++++ ...feWebAuthnSigner_sanity_with_erc20cvl.conf | 26 ++++++ ...thnSigner_sanity_with_erc20dispatched.conf | 27 ++++++ certora/harnesses/ERC20Like/DummyERC20A.sol | 55 ++++++++++++ certora/harnesses/ERC20Like/DummyERC20B.sol | 55 ++++++++++++ certora/harnesses/ERC20Like/DummyWeth.sol | 64 ++++++++++++++ certora/harnesses/Utilities.sol | 10 +++ certora/specs/ERC1967/erc1967.spec | 7 ++ certora/specs/ERC20/WETHcvl.spec | 35 ++++++++ certora/specs/ERC20/erc20cvl.spec | 56 ++++++++++++ certora/specs/ERC20/erc20dispatched.spec | 16 ++++ certora/specs/ERC721/erc721.spec | 9 ++ certora/specs/PriceAggregators/chainlink.spec | 4 + certora/specs/PriceAggregators/tellor.spec | 3 + certora/specs/generic.spec | 88 +++++++++++++++++++ certora/specs/optimizations.spec | 5 ++ certora/specs/problems.spec | 1 + certora/specs/setup/builtin_assertions.spec | 12 +++ certora/specs/setup/sanity.spec | 5 ++ .../sanity_with_all_default_summaries.spec | 23 +++++ certora/specs/setup/sanity_with_erc20cvl.spec | 8 ++ .../setup/sanity_with_erc20dispatched.spec | 7 ++ certora/specs/unresolved.spec | 4 + 50 files changed, 1250 insertions(+) create mode 100644 certora/confs/AllowanceModule_builtin_assertions.conf create mode 100644 certora/confs/AllowanceModule_sanity.conf create mode 100644 certora/confs/AllowanceModule_sanity_with_all_default_summaries.conf create mode 100644 certora/confs/AllowanceModule_sanity_with_erc20cvl.conf create mode 100644 certora/confs/AllowanceModule_sanity_with_erc20dispatched.conf create mode 100644 certora/confs/Safe4337Module_builtin_assertions.conf create mode 100644 certora/confs/Safe4337Module_sanity.conf create mode 100644 certora/confs/Safe4337Module_sanity_with_all_default_summaries.conf create mode 100644 certora/confs/Safe4337Module_sanity_with_erc20cvl.conf create mode 100644 certora/confs/Safe4337Module_sanity_with_erc20dispatched.conf create mode 100644 certora/confs/SafeModuleSetup_builtin_assertions.conf create mode 100644 certora/confs/SafeModuleSetup_sanity.conf create mode 100644 certora/confs/SafeModuleSetup_sanity_with_all_default_summaries.conf create mode 100644 certora/confs/SafeModuleSetup_sanity_with_erc20cvl.conf create mode 100644 certora/confs/SafeModuleSetup_sanity_with_erc20dispatched.conf create mode 100644 certora/confs/SafeSignerLaunchpad_builtin_assertions.conf create mode 100644 certora/confs/SafeSignerLaunchpad_sanity.conf create mode 100644 certora/confs/SafeSignerLaunchpad_sanity_with_all_default_summaries.conf create mode 100644 certora/confs/SafeSignerLaunchpad_sanity_with_erc20cvl.conf create mode 100644 certora/confs/SafeSignerLaunchpad_sanity_with_erc20dispatched.conf create mode 100644 certora/confs/SafeWebAuthnSignerFactory_builtin_assertions.conf create mode 100644 certora/confs/SafeWebAuthnSignerFactory_sanity.conf create mode 100644 certora/confs/SafeWebAuthnSignerFactory_sanity_with_all_default_summaries.conf create mode 100644 certora/confs/SafeWebAuthnSignerFactory_sanity_with_erc20cvl.conf create mode 100644 certora/confs/SafeWebAuthnSignerFactory_sanity_with_erc20dispatched.conf create mode 100644 certora/confs/SafeWebAuthnSigner_builtin_assertions.conf create mode 100644 certora/confs/SafeWebAuthnSigner_sanity.conf create mode 100644 certora/confs/SafeWebAuthnSigner_sanity_with_all_default_summaries.conf create mode 100644 certora/confs/SafeWebAuthnSigner_sanity_with_erc20cvl.conf create mode 100644 certora/confs/SafeWebAuthnSigner_sanity_with_erc20dispatched.conf create mode 100644 certora/harnesses/ERC20Like/DummyERC20A.sol create mode 100644 certora/harnesses/ERC20Like/DummyERC20B.sol create mode 100644 certora/harnesses/ERC20Like/DummyWeth.sol create mode 100644 certora/harnesses/Utilities.sol create mode 100644 certora/specs/ERC1967/erc1967.spec create mode 100644 certora/specs/ERC20/WETHcvl.spec create mode 100644 certora/specs/ERC20/erc20cvl.spec create mode 100644 certora/specs/ERC20/erc20dispatched.spec create mode 100644 certora/specs/ERC721/erc721.spec create mode 100644 certora/specs/PriceAggregators/chainlink.spec create mode 100644 certora/specs/PriceAggregators/tellor.spec create mode 100644 certora/specs/generic.spec create mode 100644 certora/specs/optimizations.spec create mode 100644 certora/specs/problems.spec create mode 100644 certora/specs/setup/builtin_assertions.spec create mode 100644 certora/specs/setup/sanity.spec create mode 100644 certora/specs/setup/sanity_with_all_default_summaries.spec create mode 100644 certora/specs/setup/sanity_with_erc20cvl.spec create mode 100644 certora/specs/setup/sanity_with_erc20dispatched.spec create mode 100644 certora/specs/unresolved.spec diff --git a/certora/confs/AllowanceModule_builtin_assertions.conf b/certora/confs/AllowanceModule_builtin_assertions.conf new file mode 100644 index 000000000..2b5c58654 --- /dev/null +++ b/certora/confs/AllowanceModule_builtin_assertions.conf @@ -0,0 +1,25 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/allowances/contracts/AllowanceModule.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "builtin_assertions", + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc7.6", + "solc_via_ir": false, + "optimistic_loop": true, + "loop_iter": "1", + "prover_version": "master", + "server": "production", + "verify": "AllowanceModule:certora/specs/setup/builtin_assertions.spec" +} \ No newline at end of file diff --git a/certora/confs/AllowanceModule_sanity.conf b/certora/confs/AllowanceModule_sanity.conf new file mode 100644 index 000000000..44a2a845d --- /dev/null +++ b/certora/confs/AllowanceModule_sanity.conf @@ -0,0 +1,26 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/allowances/contracts/AllowanceModule.sol", + "modules/allowances/contracts/SignatureDecoder.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity", + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc7.6", + "solc_via_ir": false, + "optimistic_loop": true, + "loop_iter": "5", + "prover_version": "master", + "server": "production", + "verify": "AllowanceModule:certora/specs/setup/sanity.spec" +} \ No newline at end of file diff --git a/certora/confs/AllowanceModule_sanity_with_all_default_summaries.conf b/certora/confs/AllowanceModule_sanity_with_all_default_summaries.conf new file mode 100644 index 000000000..d8d6a9bb3 --- /dev/null +++ b/certora/confs/AllowanceModule_sanity_with_all_default_summaries.conf @@ -0,0 +1,28 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/allowances/contracts/AllowanceModule.sol", + "certora/harnesses/ERC20Like/DummyWeth.sol", + "certora/harnesses/Utilities.sol", + "modules/allowances/contracts/SignatureDecoder.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity_with_all_default_summaries", + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc7.6", + "solc_via_ir": false, + "optimistic_loop": true, + "loop_iter": "5", + "prover_version": "master", + "server": "production", + "verify": "AllowanceModule:certora/specs/setup/sanity_with_all_default_summaries.spec" +} \ No newline at end of file diff --git a/certora/confs/AllowanceModule_sanity_with_erc20cvl.conf b/certora/confs/AllowanceModule_sanity_with_erc20cvl.conf new file mode 100644 index 000000000..d61318992 --- /dev/null +++ b/certora/confs/AllowanceModule_sanity_with_erc20cvl.conf @@ -0,0 +1,28 @@ +{ + "assert_autofinder_success": true, + "files": [ + "certora/harnesses/ERC20Like/DummyWeth.sol", + "certora/harnesses/Utilities.sol", + "modules/allowances/contracts/AllowanceModule.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity_with_erc20cvl", + "optimistic_fallback": true, + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc7.6", + "solc_via_ir": false, + "optimistic_loop": true, + "loop_iter": "5", + "prover_version": "master", + "server": "production", + "verify": "AllowanceModule:certora/specs/setup/sanity_with_erc20cvl.spec" +} \ No newline at end of file diff --git a/certora/confs/AllowanceModule_sanity_with_erc20dispatched.conf b/certora/confs/AllowanceModule_sanity_with_erc20dispatched.conf new file mode 100644 index 000000000..79ad56da9 --- /dev/null +++ b/certora/confs/AllowanceModule_sanity_with_erc20dispatched.conf @@ -0,0 +1,29 @@ +{ + "assert_autofinder_success": true, + "files": [ + "certora/harnesses/ERC20Like/DummyERC20A.sol", + "certora/harnesses/ERC20Like/DummyERC20B.sol", + "certora/harnesses/ERC20Like/DummyWeth.sol", + "modules/allowances/contracts/AllowanceModule.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity_with_erc20dispatched", + "optimistic_fallback": true, + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc7.6", + "solc_via_ir": false, + "optimistic_loop": true, + "loop_iter": "5", + "prover_version": "master", + "server": "production", + "verify": "AllowanceModule:certora/specs/setup/sanity_with_erc20dispatched.spec" +} \ No newline at end of file diff --git a/certora/confs/Safe4337Module_builtin_assertions.conf b/certora/confs/Safe4337Module_builtin_assertions.conf new file mode 100644 index 000000000..9fc0e9f39 --- /dev/null +++ b/certora/confs/Safe4337Module_builtin_assertions.conf @@ -0,0 +1,26 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/4337/contracts/Safe4337Module.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "builtin_assertions", + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "optimistic_hashing": true, + "optimistic_loop": true, + "loop_iter": "1", + "prover_version": "master", + "server": "production", + "verify": "Safe4337Module:certora/specs/setup/builtin_assertions.spec" +} \ No newline at end of file diff --git a/certora/confs/Safe4337Module_sanity.conf b/certora/confs/Safe4337Module_sanity.conf new file mode 100644 index 000000000..97bcc12a7 --- /dev/null +++ b/certora/confs/Safe4337Module_sanity.conf @@ -0,0 +1,23 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/4337/contracts/Safe4337Module.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity", + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "prover_version": "master", + "server": "production", + "verify": "Safe4337Module:certora/specs/setup/sanity.spec" +} \ No newline at end of file diff --git a/certora/confs/Safe4337Module_sanity_with_all_default_summaries.conf b/certora/confs/Safe4337Module_sanity_with_all_default_summaries.conf new file mode 100644 index 000000000..b9753aa5e --- /dev/null +++ b/certora/confs/Safe4337Module_sanity_with_all_default_summaries.conf @@ -0,0 +1,28 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/4337/contracts/Safe4337Module.sol", + "certora/harnesses/ERC20Like/DummyWeth.sol", + "certora/harnesses/Utilities.sol", + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity_with_all_default_summaries", + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "optimistic_hashing": true, + "optimistic_loop": true, + "optimistic_fallback": true, + "prover_version": "master", + "server": "production", + "verify": "Safe4337Module:certora/specs/setup/sanity_with_all_default_summaries.spec" +} \ No newline at end of file diff --git a/certora/confs/Safe4337Module_sanity_with_erc20cvl.conf b/certora/confs/Safe4337Module_sanity_with_erc20cvl.conf new file mode 100644 index 000000000..9c208e711 --- /dev/null +++ b/certora/confs/Safe4337Module_sanity_with_erc20cvl.conf @@ -0,0 +1,26 @@ +{ + "assert_autofinder_success": true, + "files": [ + "certora/harnesses/ERC20Like/DummyWeth.sol", + "certora/harnesses/Utilities.sol", + "modules/4337/contracts/Safe4337Module.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity_with_erc20cvl", + "optimistic_fallback": true, + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "prover_version": "master", + "server": "production", + "verify": "Safe4337Module:certora/specs/setup/sanity_with_erc20cvl.spec" +} \ No newline at end of file diff --git a/certora/confs/Safe4337Module_sanity_with_erc20dispatched.conf b/certora/confs/Safe4337Module_sanity_with_erc20dispatched.conf new file mode 100644 index 000000000..3bc0f8556 --- /dev/null +++ b/certora/confs/Safe4337Module_sanity_with_erc20dispatched.conf @@ -0,0 +1,27 @@ +{ + "assert_autofinder_success": true, + "files": [ + "certora/harnesses/ERC20Like/DummyERC20A.sol", + "certora/harnesses/ERC20Like/DummyERC20B.sol", + "certora/harnesses/ERC20Like/DummyWeth.sol", + "modules/4337/contracts/Safe4337Module.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity_with_erc20dispatched", + "optimistic_fallback": true, + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "prover_version": "master", + "server": "production", + "verify": "Safe4337Module:certora/specs/setup/sanity_with_erc20dispatched.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeModuleSetup_builtin_assertions.conf b/certora/confs/SafeModuleSetup_builtin_assertions.conf new file mode 100644 index 000000000..e98d54d55 --- /dev/null +++ b/certora/confs/SafeModuleSetup_builtin_assertions.conf @@ -0,0 +1,25 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/4337/contracts/SafeModuleSetup.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "builtin_assertions", + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "optimistic_loop": true, + "loop_iter": "1", + "prover_version": "master", + "server": "production", + "verify": "SafeModuleSetup:certora/specs/setup/builtin_assertions.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeModuleSetup_sanity.conf b/certora/confs/SafeModuleSetup_sanity.conf new file mode 100644 index 000000000..7a952d232 --- /dev/null +++ b/certora/confs/SafeModuleSetup_sanity.conf @@ -0,0 +1,23 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/4337/contracts/SafeModuleSetup.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity", + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "prover_version": "master", + "server": "production", + "verify": "SafeModuleSetup:certora/specs/setup/sanity.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeModuleSetup_sanity_with_all_default_summaries.conf b/certora/confs/SafeModuleSetup_sanity_with_all_default_summaries.conf new file mode 100644 index 000000000..e874c3a20 --- /dev/null +++ b/certora/confs/SafeModuleSetup_sanity_with_all_default_summaries.conf @@ -0,0 +1,26 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/4337/contracts/SafeModuleSetup.sol", + "certora/harnesses/ERC20Like/DummyWeth.sol", + "certora/harnesses/Utilities.sol", + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity_with_all_default_summaries", + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "optimistic_loop": true, + "prover_version": "master", + "server": "production", + "verify": "SafeModuleSetup:certora/specs/setup/sanity_with_all_default_summaries.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeModuleSetup_sanity_with_erc20cvl.conf b/certora/confs/SafeModuleSetup_sanity_with_erc20cvl.conf new file mode 100644 index 000000000..6b02ab451 --- /dev/null +++ b/certora/confs/SafeModuleSetup_sanity_with_erc20cvl.conf @@ -0,0 +1,26 @@ +{ + "assert_autofinder_success": true, + "files": [ + "certora/harnesses/ERC20Like/DummyWeth.sol", + "certora/harnesses/Utilities.sol", + "modules/4337/contracts/SafeModuleSetup.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity_with_erc20cvl", + "optimistic_fallback": true, + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "prover_version": "master", + "server": "production", + "verify": "SafeModuleSetup:certora/specs/setup/sanity_with_erc20cvl.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeModuleSetup_sanity_with_erc20dispatched.conf b/certora/confs/SafeModuleSetup_sanity_with_erc20dispatched.conf new file mode 100644 index 000000000..0272940c7 --- /dev/null +++ b/certora/confs/SafeModuleSetup_sanity_with_erc20dispatched.conf @@ -0,0 +1,27 @@ +{ + "assert_autofinder_success": true, + "files": [ + "certora/harnesses/ERC20Like/DummyERC20A.sol", + "certora/harnesses/ERC20Like/DummyERC20B.sol", + "certora/harnesses/ERC20Like/DummyWeth.sol", + "modules/4337/contracts/SafeModuleSetup.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity_with_erc20dispatched", + "optimistic_fallback": true, + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "prover_version": "master", + "server": "production", + "verify": "SafeModuleSetup:certora/specs/setup/sanity_with_erc20dispatched.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeSignerLaunchpad_builtin_assertions.conf b/certora/confs/SafeSignerLaunchpad_builtin_assertions.conf new file mode 100644 index 000000000..2d643ebb4 --- /dev/null +++ b/certora/confs/SafeSignerLaunchpad_builtin_assertions.conf @@ -0,0 +1,24 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/passkey/contracts/4337/SafeSignerLaunchpad.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "builtin_assertions", + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "optimistic_hashing": true, + "prover_version": "master", + "server": "production", + "verify": "SafeSignerLaunchpad:certora/specs/setup/builtin_assertions.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeSignerLaunchpad_sanity.conf b/certora/confs/SafeSignerLaunchpad_sanity.conf new file mode 100644 index 000000000..dc0d4a8bb --- /dev/null +++ b/certora/confs/SafeSignerLaunchpad_sanity.conf @@ -0,0 +1,23 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/passkey/contracts/4337/SafeSignerLaunchpad.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity", + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "prover_version": "master", + "server": "production", + "verify": "SafeSignerLaunchpad:certora/specs/setup/sanity.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeSignerLaunchpad_sanity_with_all_default_summaries.conf b/certora/confs/SafeSignerLaunchpad_sanity_with_all_default_summaries.conf new file mode 100644 index 000000000..e26e4d86d --- /dev/null +++ b/certora/confs/SafeSignerLaunchpad_sanity_with_all_default_summaries.conf @@ -0,0 +1,26 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/passkey/contracts/4337/SafeSignerLaunchpad.sol", + "certora/harnesses/ERC20Like/DummyWeth.sol", + "certora/harnesses/Utilities.sol", + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity_with_all_default_summaries", + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "optimistic_hashing": true, + "prover_version": "master", + "server": "production", + "verify": "SafeSignerLaunchpad:certora/specs/setup/sanity_with_all_default_summaries.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeSignerLaunchpad_sanity_with_erc20cvl.conf b/certora/confs/SafeSignerLaunchpad_sanity_with_erc20cvl.conf new file mode 100644 index 000000000..4d46f612e --- /dev/null +++ b/certora/confs/SafeSignerLaunchpad_sanity_with_erc20cvl.conf @@ -0,0 +1,26 @@ +{ + "assert_autofinder_success": true, + "files": [ + "certora/harnesses/ERC20Like/DummyWeth.sol", + "certora/harnesses/Utilities.sol", + "modules/passkey/contracts/4337/SafeSignerLaunchpad.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity_with_erc20cvl", + "optimistic_fallback": true, + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "prover_version": "master", + "server": "production", + "verify": "SafeSignerLaunchpad:certora/specs/setup/sanity_with_erc20cvl.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeSignerLaunchpad_sanity_with_erc20dispatched.conf b/certora/confs/SafeSignerLaunchpad_sanity_with_erc20dispatched.conf new file mode 100644 index 000000000..f0ea64181 --- /dev/null +++ b/certora/confs/SafeSignerLaunchpad_sanity_with_erc20dispatched.conf @@ -0,0 +1,27 @@ +{ + "assert_autofinder_success": true, + "files": [ + "certora/harnesses/ERC20Like/DummyERC20A.sol", + "certora/harnesses/ERC20Like/DummyERC20B.sol", + "certora/harnesses/ERC20Like/DummyWeth.sol", + "modules/passkey/contracts/4337/SafeSignerLaunchpad.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity_with_erc20dispatched", + "optimistic_fallback": true, + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "prover_version": "master", + "server": "production", + "verify": "SafeSignerLaunchpad:certora/specs/setup/sanity_with_erc20dispatched.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeWebAuthnSignerFactory_builtin_assertions.conf b/certora/confs/SafeWebAuthnSignerFactory_builtin_assertions.conf new file mode 100644 index 000000000..3005f1650 --- /dev/null +++ b/certora/confs/SafeWebAuthnSignerFactory_builtin_assertions.conf @@ -0,0 +1,26 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/passkey/contracts/SafeWebAuthnSignerFactory.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "builtin_assertions", + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "optimistic_hashing": true, + "optimistic_loop": true, + "loop_iter": "1", + "prover_version": "master", + "server": "production", + "verify": "SafeWebAuthnSignerFactory:certora/specs/setup/builtin_assertions.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeWebAuthnSignerFactory_sanity.conf b/certora/confs/SafeWebAuthnSignerFactory_sanity.conf new file mode 100644 index 000000000..81e53fb85 --- /dev/null +++ b/certora/confs/SafeWebAuthnSignerFactory_sanity.conf @@ -0,0 +1,25 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/passkey/contracts/SafeWebAuthnSignerFactory.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity", + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "optimistic_loop": true, + "loop_iter": "6", + "prover_version": "master", + "server": "production", + "verify": "SafeWebAuthnSignerFactory:certora/specs/setup/sanity.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeWebAuthnSignerFactory_sanity_with_all_default_summaries.conf b/certora/confs/SafeWebAuthnSignerFactory_sanity_with_all_default_summaries.conf new file mode 100644 index 000000000..fda18c771 --- /dev/null +++ b/certora/confs/SafeWebAuthnSignerFactory_sanity_with_all_default_summaries.conf @@ -0,0 +1,27 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/passkey/contracts/SafeWebAuthnSignerFactory.sol", + "certora/harnesses/ERC20Like/DummyWeth.sol", + "certora/harnesses/Utilities.sol", + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity_with_all_default_summaries", + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "optimistic_loop": true, + "optimistic_hashing": true, + "prover_version": "master", + "server": "production", + "verify": "SafeWebAuthnSignerFactory:certora/specs/setup/sanity_with_all_default_summaries.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeWebAuthnSignerFactory_sanity_with_erc20cvl.conf b/certora/confs/SafeWebAuthnSignerFactory_sanity_with_erc20cvl.conf new file mode 100644 index 000000000..c35f40bc0 --- /dev/null +++ b/certora/confs/SafeWebAuthnSignerFactory_sanity_with_erc20cvl.conf @@ -0,0 +1,26 @@ +{ + "assert_autofinder_success": true, + "files": [ + "certora/harnesses/ERC20Like/DummyWeth.sol", + "certora/harnesses/Utilities.sol", + "modules/passkey/contracts/SafeWebAuthnSignerFactory.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity_with_erc20cvl", + "optimistic_fallback": true, + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "prover_version": "master", + "server": "production", + "verify": "SafeWebAuthnSignerFactory:certora/specs/setup/sanity_with_erc20cvl.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeWebAuthnSignerFactory_sanity_with_erc20dispatched.conf b/certora/confs/SafeWebAuthnSignerFactory_sanity_with_erc20dispatched.conf new file mode 100644 index 000000000..b7c1f658f --- /dev/null +++ b/certora/confs/SafeWebAuthnSignerFactory_sanity_with_erc20dispatched.conf @@ -0,0 +1,27 @@ +{ + "assert_autofinder_success": true, + "files": [ + "certora/harnesses/ERC20Like/DummyERC20A.sol", + "certora/harnesses/ERC20Like/DummyERC20B.sol", + "certora/harnesses/ERC20Like/DummyWeth.sol", + "modules/passkey/contracts/SafeWebAuthnSignerFactory.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity_with_erc20dispatched", + "optimistic_fallback": true, + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "prover_version": "master", + "server": "production", + "verify": "SafeWebAuthnSignerFactory:certora/specs/setup/sanity_with_erc20dispatched.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeWebAuthnSigner_builtin_assertions.conf b/certora/confs/SafeWebAuthnSigner_builtin_assertions.conf new file mode 100644 index 000000000..570da61c9 --- /dev/null +++ b/certora/confs/SafeWebAuthnSigner_builtin_assertions.conf @@ -0,0 +1,28 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/passkey/contracts/SafeWebAuthnSigner.sol", + "certora/harnesses/ERC20Like/DummyWeth.sol", + "certora/harnesses/Utilities.sol", + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "builtin_assertions", + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "optimistic_hashing": true, + "optimistic_loop": true, + "loop_iter": "1", + "prover_version": "master", + "server": "production", + "verify": "SafeWebAuthnSigner:certora/specs/setup/builtin_assertions.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeWebAuthnSigner_sanity.conf b/certora/confs/SafeWebAuthnSigner_sanity.conf new file mode 100644 index 000000000..3a003c293 --- /dev/null +++ b/certora/confs/SafeWebAuthnSigner_sanity.conf @@ -0,0 +1,25 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/passkey/contracts/SafeWebAuthnSigner.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity", + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "optimistic_loop": true, + "loop_iter": "15", + "prover_version": "master", + "server": "production", + "verify": "SafeWebAuthnSigner:certora/specs/setup/sanity.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeWebAuthnSigner_sanity_with_all_default_summaries.conf b/certora/confs/SafeWebAuthnSigner_sanity_with_all_default_summaries.conf new file mode 100644 index 000000000..c73b8a3d2 --- /dev/null +++ b/certora/confs/SafeWebAuthnSigner_sanity_with_all_default_summaries.conf @@ -0,0 +1,27 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/passkey/contracts/SafeWebAuthnSigner.sol", + "certora/harnesses/ERC20Like/DummyWeth.sol", + "certora/harnesses/Utilities.sol", + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity_with_all_default_summaries", + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "optimistic_loop": true, + "optimistic_hashing": true, + "prover_version": "master", + "server": "production", + "verify": "SafeWebAuthnSigner:certora/specs/setup/sanity_with_all_default_summaries.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeWebAuthnSigner_sanity_with_erc20cvl.conf b/certora/confs/SafeWebAuthnSigner_sanity_with_erc20cvl.conf new file mode 100644 index 000000000..da1bff5fd --- /dev/null +++ b/certora/confs/SafeWebAuthnSigner_sanity_with_erc20cvl.conf @@ -0,0 +1,26 @@ +{ + "assert_autofinder_success": true, + "files": [ + "certora/harnesses/ERC20Like/DummyWeth.sol", + "certora/harnesses/Utilities.sol", + "modules/passkey/contracts/SafeWebAuthnSigner.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity_with_erc20cvl", + "optimistic_fallback": true, + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "prover_version": "master", + "server": "production", + "verify": "SafeWebAuthnSigner:certora/specs/setup/sanity_with_erc20cvl.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeWebAuthnSigner_sanity_with_erc20dispatched.conf b/certora/confs/SafeWebAuthnSigner_sanity_with_erc20dispatched.conf new file mode 100644 index 000000000..e2b142a8c --- /dev/null +++ b/certora/confs/SafeWebAuthnSigner_sanity_with_erc20dispatched.conf @@ -0,0 +1,27 @@ +{ + "assert_autofinder_success": true, + "files": [ + "certora/harnesses/ERC20Like/DummyERC20A.sol", + "certora/harnesses/ERC20Like/DummyERC20B.sol", + "certora/harnesses/ERC20Like/DummyWeth.sol", + "modules/passkey/contracts/SafeWebAuthnSigner.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity_with_erc20dispatched", + "optimistic_fallback": true, + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "prover_version": "master", + "server": "production", + "verify": "SafeWebAuthnSigner:certora/specs/setup/sanity_with_erc20dispatched.spec" +} \ No newline at end of file diff --git a/certora/harnesses/ERC20Like/DummyERC20A.sol b/certora/harnesses/ERC20Like/DummyERC20A.sol new file mode 100644 index 000000000..de50d9b1d --- /dev/null +++ b/certora/harnesses/ERC20Like/DummyERC20A.sol @@ -0,0 +1,55 @@ +// Represents a symbolic/dummy ERC20 token + +// SPDX-License-Identifier: agpl-3.0 +pragma solidity ^0.7.6; + +contract DummyERC20A { + uint256 t; + mapping(address => uint256) b; + mapping(address => mapping(address => uint256)) a; + + string public name; + string public symbol; + uint public decimals; + + function myAddress() external view returns (address) { + return address(this); + } + + function totalSupply() external view returns (uint256) { + return t; + } + + function balanceOf(address account) external view returns (uint256) { + return b[account]; + } + + function transfer(address recipient, uint256 amount) external returns (bool) { + b[msg.sender] -= amount; + b[recipient] += amount; + + return true; + } + + function allowance(address owner, address spender) external view returns (uint256) { + return a[owner][spender]; + } + + function approve(address spender, uint256 amount) external returns (bool) { + a[msg.sender][spender] = amount; + + return true; + } + + function transferFrom( + address sender, + address recipient, + uint256 amount + ) external returns (bool) { + b[sender] -= amount; + b[recipient] += amount; + a[sender][msg.sender] -= amount; + + return true; + } +} diff --git a/certora/harnesses/ERC20Like/DummyERC20B.sol b/certora/harnesses/ERC20Like/DummyERC20B.sol new file mode 100644 index 000000000..7b5cb9f1f --- /dev/null +++ b/certora/harnesses/ERC20Like/DummyERC20B.sol @@ -0,0 +1,55 @@ +// Represents a symbolic/dummy ERC20 token + +// SPDX-License-Identifier: agpl-3.0 +pragma solidity ^0.7.6; + +contract DummyERC20B { + uint256 t; + mapping(address => uint256) b; + mapping(address => mapping(address => uint256)) a; + + string public name; + string public symbol; + uint public decimals; + + function myAddress() external view returns (address) { + return address(this); + } + + function totalSupply() external view returns (uint256) { + return t; + } + + function balanceOf(address account) external view returns (uint256) { + return b[account]; + } + + function transfer(address recipient, uint256 amount) external returns (bool) { + b[msg.sender] -= amount; + b[recipient] += amount; + + return true; + } + + function allowance(address owner, address spender) external view returns (uint256) { + return a[owner][spender]; + } + + function approve(address spender, uint256 amount) external returns (bool) { + a[msg.sender][spender] = amount; + + return true; + } + + function transferFrom( + address sender, + address recipient, + uint256 amount + ) external returns (bool) { + b[sender] -= amount; + b[recipient] += amount; + a[sender][msg.sender] -= amount; + + return true; + } +} diff --git a/certora/harnesses/ERC20Like/DummyWeth.sol b/certora/harnesses/ERC20Like/DummyWeth.sol new file mode 100644 index 000000000..23f6befa0 --- /dev/null +++ b/certora/harnesses/ERC20Like/DummyWeth.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity >=0.7.0; + +/** + * Dummy Weth token. + */ +contract DummyWeth { + uint256 t; + + mapping(address => uint256) b; + mapping(address => mapping(address => uint256)) a; + + string public name; + string public symbol; + uint public decimals; + + function myAddress() external view returns (address) { + return address(this); + } + + function totalSupply() external view returns (uint256) { + return t; + } + + function balanceOf(address account) external view returns (uint256) { + return b[account]; + } + + function transfer(address recipient, uint256 amount) external returns (bool) { + b[msg.sender] -= amount; + b[recipient] += amount; + return true; + } + + function allowance(address owner, address spender) external view returns (uint256) { + return a[owner][spender]; + } + + function approve(address spender, uint256 amount) external returns (bool) { + a[msg.sender][spender] = amount; + return true; + } + + function transferFrom( + address sender, + address recipient, + uint256 amount + ) external returns (bool) { + b[sender] -= amount; + b[recipient] += amount; + a[sender][msg.sender] -= amount; + return true; + } + + // WETH + function deposit() external payable { + b[msg.sender] += msg.value; + } + + function withdraw(uint256 amt) external { + b[msg.sender] -= amt; + payable(msg.sender).transfer(amt); // use optimistic_fallback here + } +} diff --git a/certora/harnesses/Utilities.sol b/certora/harnesses/Utilities.sol new file mode 100644 index 000000000..608595af7 --- /dev/null +++ b/certora/harnesses/Utilities.sol @@ -0,0 +1,10 @@ +contract Utilities { + function havocAll() external { + (bool success, ) = address(0xdeadbeef).call(abi.encodeWithSelector(0x12345678)); + require(success); + } + + function justRevert() external { + revert(); + } +} \ No newline at end of file diff --git a/certora/specs/ERC1967/erc1967.spec b/certora/specs/ERC1967/erc1967.spec new file mode 100644 index 000000000..8c96499cc --- /dev/null +++ b/certora/specs/ERC1967/erc1967.spec @@ -0,0 +1,7 @@ +methods { + // avoids linking messages upon upgradeToAndCall + function _._upgradeToAndCall(address,bytes memory,bool) internal => NONDET; + function _._upgradeToAndCallUUPS(address,bytes memory,bool) internal => NONDET; + // view function + function _.proxiableUUID() external => NONDET; // expect bytes32 +} \ No newline at end of file diff --git a/certora/specs/ERC20/WETHcvl.spec b/certora/specs/ERC20/WETHcvl.spec new file mode 100644 index 000000000..8b1d8b70e --- /dev/null +++ b/certora/specs/ERC20/WETHcvl.spec @@ -0,0 +1,35 @@ +using DummyWeth as weth; // we are limited by the fact that we cannot do transfers from CVL +using Utilities as utils; + +methods { + // Utilities + function Utilities.justRevert() external envfree; + + // WETH + function _.deposit() external with (env e) => wethDeposit(calledContract, e.msg.sender, e.msg.value) expect void; + function _.withdraw(uint256 amount) external with (env e) => wethWithdraw(calledContract, e.msg.sender, amount) expect void; +} + +function wethDeposit(address target, address caller, uint256 value) { + // should be reverting if target != weth. Instead, we will use a contract to revert + if (target != weth) { + utils.justRevert(); // check this works xxx + } else { + // money will be transferred because of the payability of deposit + env e2; + require e2.msg.sender == caller; + require e2.msg.value == value; + weth.deposit(e2); + } +} + +function wethWithdraw(address target, address caller, uint256 amount) { + // should be reverting if target != weth. Instead, we will use a contract to revert + if (target != weth) { + utils.justRevert(); // check this works xxx + } else { + env e2; + require e2.msg.sender == caller; + weth.withdraw(e2, amount); + } +} \ No newline at end of file diff --git a/certora/specs/ERC20/erc20cvl.spec b/certora/specs/ERC20/erc20cvl.spec new file mode 100644 index 000000000..cacf0ce3c --- /dev/null +++ b/certora/specs/ERC20/erc20cvl.spec @@ -0,0 +1,56 @@ +methods { + // ERC20 standard + function _.name() external => NONDET; // can we use PER_CALLEE_CONSTANT? + function _.symbol() external => NONDET; // can we use PER_CALLEE_CONSTANT? + function _.decimals() external => PER_CALLEE_CONSTANT; + function _.totalSupply() external => totalSupplyByToken[calledContract] expect uint256; + function _.balanceOf(address a) external => balanceByToken[calledContract][a] expect uint256; + function _.allowance(address a, address b) external => allowanceByToken[calledContract][a][b] expect uint256; + function _.approve(address a, uint256 x) external with (env e) => approveCVL(calledContract, e.msg.sender, a, x) expect bool; + function _.transfer(address a, uint256 x) external with (env e) => transferCVL(calledContract, e.msg.sender, a, x) expect bool; + function _.transferFrom(address a, address b, uint256 x) external with (env e) => transferFromCVL(calledContract, e.msg.sender, a, b, x) expect bool; + +} + + +/// CVL simple implementations of IERC20: +/// token => totalSupply +ghost mapping(address => uint256) totalSupplyByToken; +/// token => account => balance +ghost mapping(address => mapping(address => uint256)) balanceByToken; +/// token => owner => spender => allowance +ghost mapping(address => mapping(address => mapping(address => uint256))) allowanceByToken; + +// function tokenBalanceOf(address token, address account) returns uint256 { +// return balanceByToken[token][account]; +// } + +function approveCVL(address token, address approver, address spender, uint256 amount) returns bool { + // should be randomly reverting xxx + bool nondetSuccess; + if (!nondetSuccess) return false; + + allowanceByToken[token][approver][spender] = amount; + return true; +} + +function transferFromCVL(address token, address spender, address from, address to, uint256 amount) returns bool { + // should be randomly reverting xxx + bool nondetSuccess; + if (!nondetSuccess) return false; + + if (allowanceByToken[token][from][spender] < amount) return false; + allowanceByToken[token][from][spender] = assert_uint256(allowanceByToken[token][from][spender] - amount); + return transferCVL(token, from, to, amount); +} + +function transferCVL(address token, address from, address to, uint256 amount) returns bool { + // should be randomly reverting xxx + bool nondetSuccess; + if (!nondetSuccess) return false; + + if(balanceByToken[token][from] < amount) return false; + balanceByToken[token][from] = assert_uint256(balanceByToken[token][from] - amount); + balanceByToken[token][to] = require_uint256(balanceByToken[token][to] + amount); // We neglect overflows. + return true; +} \ No newline at end of file diff --git a/certora/specs/ERC20/erc20dispatched.spec b/certora/specs/ERC20/erc20dispatched.spec new file mode 100644 index 000000000..c40394bbd --- /dev/null +++ b/certora/specs/ERC20/erc20dispatched.spec @@ -0,0 +1,16 @@ +methods { + // ERC20 standard + function _.name() external => DISPATCHER(true); + function _.symbol() external => DISPATCHER(true); + function _.decimals() external => DISPATCHER(true); + function _.totalSupply() external => DISPATCHER(true); + function _.balanceOf(address) external => DISPATCHER(true); + function _.allowance(address,address) external => DISPATCHER(true); + function _.approve(address,uint256) external => DISPATCHER(true); + function _.transfer(address,uint256) external => DISPATCHER(true); + function _.transferFrom(address,address,uint256) external => DISPATCHER(true); + + // WETH + function _.deposit() external => DISPATCHER(true); + function _.withdraw(uint256) external => DISPATCHER(true); +} diff --git a/certora/specs/ERC721/erc721.spec b/certora/specs/ERC721/erc721.spec new file mode 100644 index 000000000..474b69f3a --- /dev/null +++ b/certora/specs/ERC721/erc721.spec @@ -0,0 +1,9 @@ +methods { + // likely unsound, but assumes no callback + function _.onERC721Received( + address operator, + address from, + uint256 tokenId, + bytes data + ) external => NONDET; /* expects bytes4 */ +} \ No newline at end of file diff --git a/certora/specs/PriceAggregators/chainlink.spec b/certora/specs/PriceAggregators/chainlink.spec new file mode 100644 index 000000000..889e39e6f --- /dev/null +++ b/certora/specs/PriceAggregators/chainlink.spec @@ -0,0 +1,4 @@ +methods { + function _.getRoundData(uint80) external => NONDET; + function _.latestRoundData() external => NONDET; +} \ No newline at end of file diff --git a/certora/specs/PriceAggregators/tellor.spec b/certora/specs/PriceAggregators/tellor.spec new file mode 100644 index 000000000..03f90c779 --- /dev/null +++ b/certora/specs/PriceAggregators/tellor.spec @@ -0,0 +1,3 @@ +methods { + function _.getTellorCurrentValue(uint256) external => NONDET; +} \ No newline at end of file diff --git a/certora/specs/generic.spec b/certora/specs/generic.spec new file mode 100644 index 000000000..b6234ffd3 --- /dev/null +++ b/certora/specs/generic.spec @@ -0,0 +1,88 @@ +/* +This rule find which functions are privileged. +A function is privileged if there is only one address that can call it. + +The rules finds this by finding which functions can be called by two different users. +*/ +rule privilegedOperation(method f, address privileged) { + env e1; + calldataarg arg; + require e1.msg.sender == privileged; + + storage initialStorage = lastStorage; + f@withrevert(e1, arg); // privileged succeeds executing candidate privileged operation. + bool firstSucceeded = !lastReverted; + + env e2; + calldataarg arg2; + require e2.msg.sender != privileged; + f@withrevert(e2, arg2) at initialStorage; // unprivileged + bool secondSucceeded = !lastReverted; + + assert !(firstSucceeded && secondSucceeded); +} + +rule timeoutChecker(method f) { + storage before = lastStorage; + env e; calldataarg arg; + f(e,arg); + assert before == lastStorage; +} + +/* +This rule find which functions that can be called, may fail due to someone else calling a function right before. + +This is n expensive rule - might fail on the demo site on big contracts +*/ +rule simpleFrontRunning(method f, address privileged) filtered { f-> !f.isView } { + env e1; + calldataarg arg; + require e1.msg.sender == privileged; + storage initialStorage = lastStorage; + f(e1, arg); + bool firstSucceeded = !lastReverted; + env e2; + calldataarg arg2; + require e2.msg.sender != e1.msg.sender; + f(e2, arg2) at initialStorage; + f@withrevert(e1, arg); + bool succeeded = !lastReverted; + assert succeeded; +} + +rule noRevert(method f) { + env e; + calldataarg arg; + require e.msg.value == 0; + f@withrevert(e, arg); + assert !lastReverted; +} + + +rule alwaysRevert(method f) { + env e; + calldataarg arg; + f@withrevert(e, arg); + assert lastReverted; +} + +use builtin rule sanity; +use builtin rule hasDelegateCalls; +use builtin rule msgValueInLoopRule; +use builtin rule viewReentrancy; + +/** + +// Integrate rules from generic.spec in importing specs like this: + +use builtin rule sanity filtered { f -> f.contract == currentContract } +use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } +use builtin rule msgValueInLoopRule; +use builtin rule viewReentrancy; +use rule privilegedOperation filtered { f -> f.contract == currentContract } +use rule timeoutChecker filtered { f -> f.contract == currentContract } +use rule simpleFrontRunning filtered { f -> f.contract == currentContract } +use rule noRevert filtered { f -> f.contract == currentContract } +use rule alwaysRevert filtered { f -> f.contract == currentContract } + + **/ \ No newline at end of file diff --git a/certora/specs/optimizations.spec b/certora/specs/optimizations.spec new file mode 100644 index 000000000..e92485dc9 --- /dev/null +++ b/certora/specs/optimizations.spec @@ -0,0 +1,5 @@ +// optimizing summaries +methods { + +} + diff --git a/certora/specs/problems.spec b/certora/specs/problems.spec new file mode 100644 index 000000000..3ebe9e371 --- /dev/null +++ b/certora/specs/problems.spec @@ -0,0 +1 @@ +// workarounds for crashes \ No newline at end of file diff --git a/certora/specs/setup/builtin_assertions.spec b/certora/specs/setup/builtin_assertions.spec new file mode 100644 index 000000000..477d9680b --- /dev/null +++ b/certora/specs/setup/builtin_assertions.spec @@ -0,0 +1,12 @@ +import "../problems.spec"; +import "../unresolved.spec"; +import "../optimizations.spec"; + +rule check_builtin_assertions(method f) + filtered { f -> f.contract == currentContract } +{ + env e; + calldataarg arg; + f(e, arg); + assert true; +} diff --git a/certora/specs/setup/sanity.spec b/certora/specs/setup/sanity.spec new file mode 100644 index 000000000..b44a649b6 --- /dev/null +++ b/certora/specs/setup/sanity.spec @@ -0,0 +1,5 @@ +import "../problems.spec"; +import "../unresolved.spec"; +import "../optimizations.spec"; + +use builtin rule sanity; \ No newline at end of file diff --git a/certora/specs/setup/sanity_with_all_default_summaries.spec b/certora/specs/setup/sanity_with_all_default_summaries.spec new file mode 100644 index 000000000..19a15bfa6 --- /dev/null +++ b/certora/specs/setup/sanity_with_all_default_summaries.spec @@ -0,0 +1,23 @@ +import "../ERC20/erc20cvl.spec"; +import "../ERC20/WETHcvl.spec"; +import "../ERC721/erc721.spec"; +import "../ERC1967/erc1967.spec"; +import "../PriceAggregators/chainlink.spec"; +import "../PriceAggregators/tellor.spec"; + +import "../problems.spec"; +import "../unresolved.spec"; +import "../optimizations.spec"; + +import "../generic.spec"; // pick additional rules from here + +use builtin rule sanity filtered { f -> f.contract == currentContract } + +use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } +use builtin rule msgValueInLoopRule; +use builtin rule viewReentrancy; +use rule privilegedOperation filtered { f -> f.contract == currentContract } +use rule timeoutChecker filtered { f -> f.contract == currentContract } +use rule simpleFrontRunning filtered { f -> f.contract == currentContract } +use rule noRevert filtered { f -> f.contract == currentContract } +use rule alwaysRevert filtered { f -> f.contract == currentContract } \ No newline at end of file diff --git a/certora/specs/setup/sanity_with_erc20cvl.spec b/certora/specs/setup/sanity_with_erc20cvl.spec new file mode 100644 index 000000000..1a932499c --- /dev/null +++ b/certora/specs/setup/sanity_with_erc20cvl.spec @@ -0,0 +1,8 @@ +import "../ERC20/erc20cvl.spec"; +import "../ERC20/WETHcvl.spec"; + +import "../problems.spec"; +import "../unresolved.spec"; +import "../optimizations.spec"; + +use builtin rule sanity filtered { f -> f.contract == currentContract } \ No newline at end of file diff --git a/certora/specs/setup/sanity_with_erc20dispatched.spec b/certora/specs/setup/sanity_with_erc20dispatched.spec new file mode 100644 index 000000000..75340d9af --- /dev/null +++ b/certora/specs/setup/sanity_with_erc20dispatched.spec @@ -0,0 +1,7 @@ +import "../ERC20/erc20dispatched.spec"; + +import "../problems.spec"; +import "../unresolved.spec"; +import "../optimizations.spec"; + +use builtin rule sanity filtered { f -> f.contract == currentContract } \ No newline at end of file diff --git a/certora/specs/unresolved.spec b/certora/specs/unresolved.spec new file mode 100644 index 000000000..6bae1d482 --- /dev/null +++ b/certora/specs/unresolved.spec @@ -0,0 +1,4 @@ +// summaries for unresolved calls +methods { + +} \ No newline at end of file From 512eb512f0e6e3fed0401d7e8bb7012bffa61f50 Mon Sep 17 00:00:00 2001 From: yoavelmalem Date: Thu, 25 Apr 2024 18:53:49 +0300 Subject: [PATCH 002/103] Small fixes --- certora/confs/SafeWebAuthnSigner_sanity.conf | 2 +- certora/harnesses/ERC20Like/DummyERC20A.sol | 2 +- certora/harnesses/ERC20Like/DummyERC20B.sol | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/certora/confs/SafeWebAuthnSigner_sanity.conf b/certora/confs/SafeWebAuthnSigner_sanity.conf index 3a003c293..f75d289d7 100644 --- a/certora/confs/SafeWebAuthnSigner_sanity.conf +++ b/certora/confs/SafeWebAuthnSigner_sanity.conf @@ -18,7 +18,7 @@ "solc": "solc8.23", "solc_via_ir": false, "optimistic_loop": true, - "loop_iter": "15", + "loop_iter": "6", "prover_version": "master", "server": "production", "verify": "SafeWebAuthnSigner:certora/specs/setup/sanity.spec" diff --git a/certora/harnesses/ERC20Like/DummyERC20A.sol b/certora/harnesses/ERC20Like/DummyERC20A.sol index de50d9b1d..f6138eb04 100644 --- a/certora/harnesses/ERC20Like/DummyERC20A.sol +++ b/certora/harnesses/ERC20Like/DummyERC20A.sol @@ -1,7 +1,7 @@ // Represents a symbolic/dummy ERC20 token // SPDX-License-Identifier: agpl-3.0 -pragma solidity ^0.7.6; +pragma solidity >=0.7.6; contract DummyERC20A { uint256 t; diff --git a/certora/harnesses/ERC20Like/DummyERC20B.sol b/certora/harnesses/ERC20Like/DummyERC20B.sol index 7b5cb9f1f..42e2c201b 100644 --- a/certora/harnesses/ERC20Like/DummyERC20B.sol +++ b/certora/harnesses/ERC20Like/DummyERC20B.sol @@ -1,7 +1,7 @@ // Represents a symbolic/dummy ERC20 token // SPDX-License-Identifier: agpl-3.0 -pragma solidity ^0.7.6; +pragma solidity >=0.7.6; contract DummyERC20B { uint256 t; From ff3c3250c7c2838e0ee613a165e1e1583f6da789 Mon Sep 17 00:00:00 2001 From: yoavelmalem Date: Thu, 2 May 2024 17:28:57 +0300 Subject: [PATCH 003/103] Reorganize all folders, Added specific spec file for each contract, Added summarizations. --- certora/confs/AllowanceModule.conf | 28 ++++++++++++++ certora/confs/Safe4337Module.conf | 29 ++++++++++++++ certora/confs/SafeModuleSetup.conf | 26 +++++++++++++ certora/confs/SafeSignerLaunchpad.conf | 28 ++++++++++++++ certora/confs/SafeWebAuthnSigner.conf | 32 ++++++++++++++++ certora/confs/SafeWebAuthnSignerFactory.conf | 27 +++++++++++++ .../AllowanceModule_builtin_assertions.conf | 0 .../AllowanceModule_sanity.conf | 0 ...ule_sanity_with_all_default_summaries.conf | 0 .../AllowanceModule_sanity_with_erc20cvl.conf | 0 ...nceModule_sanity_with_erc20dispatched.conf | 0 .../Safe4337Module_builtin_assertions.conf | 0 .../Safe4337Module_sanity.conf | 0 ...ule_sanity_with_all_default_summaries.conf | 0 .../Safe4337Module_sanity_with_erc20cvl.conf | 0 ...337Module_sanity_with_erc20dispatched.conf | 0 .../SafeModuleSetup_builtin_assertions.conf | 0 .../SafeModuleSetup_sanity.conf | 0 ...tup_sanity_with_all_default_summaries.conf | 0 .../SafeModuleSetup_sanity_with_erc20cvl.conf | 0 ...duleSetup_sanity_with_erc20dispatched.conf | 0 ...afeSignerLaunchpad_builtin_assertions.conf | 0 .../SafeSignerLaunchpad_sanity.conf | 0 ...pad_sanity_with_all_default_summaries.conf | 0 ...eSignerLaunchpad_sanity_with_erc20cvl.conf | 0 ...Launchpad_sanity_with_erc20dispatched.conf | 0 ...AuthnSignerFactory_builtin_assertions.conf | 0 .../SafeWebAuthnSignerFactory_sanity.conf | 0 ...ory_sanity_with_all_default_summaries.conf | 0 ...thnSignerFactory_sanity_with_erc20cvl.conf | 0 ...erFactory_sanity_with_erc20dispatched.conf | 0 ...SafeWebAuthnSigner_builtin_assertions.conf | 0 .../SafeWebAuthnSigner_sanity.conf | 0 ...ner_sanity_with_all_default_summaries.conf | 0 ...feWebAuthnSigner_sanity_with_erc20cvl.conf | 0 ...thnSigner_sanity_with_erc20dispatched.conf | 0 certora/specs/AllowanceModule.spec | 32 ++++++++++++++++ certora/specs/Safe4337Module.spec | 33 ++++++++++++++++ certora/specs/SafeModuleSetup.spec | 27 +++++++++++++ certora/specs/SafeSignerLaunchpad.spec | 38 +++++++++++++++++++ certora/specs/SafeWebAuthnSigner.spec | 23 +++++++++++ certora/specs/SafeWebAuthnSignerFactory.spec | 23 +++++++++++ certora/specs/setup/sanity.spec | 2 +- .../sanity_with_all_default_summaries.spec | 11 +----- 44 files changed, 348 insertions(+), 11 deletions(-) create mode 100644 certora/confs/AllowanceModule.conf create mode 100644 certora/confs/Safe4337Module.conf create mode 100644 certora/confs/SafeModuleSetup.conf create mode 100644 certora/confs/SafeSignerLaunchpad.conf create mode 100644 certora/confs/SafeWebAuthnSigner.conf create mode 100644 certora/confs/SafeWebAuthnSignerFactory.conf rename certora/confs/{ => historical_confs}/AllowanceModule_builtin_assertions.conf (100%) rename certora/confs/{ => historical_confs}/AllowanceModule_sanity.conf (100%) rename certora/confs/{ => historical_confs}/AllowanceModule_sanity_with_all_default_summaries.conf (100%) rename certora/confs/{ => historical_confs}/AllowanceModule_sanity_with_erc20cvl.conf (100%) rename certora/confs/{ => historical_confs}/AllowanceModule_sanity_with_erc20dispatched.conf (100%) rename certora/confs/{ => historical_confs}/Safe4337Module_builtin_assertions.conf (100%) rename certora/confs/{ => historical_confs}/Safe4337Module_sanity.conf (100%) rename certora/confs/{ => historical_confs}/Safe4337Module_sanity_with_all_default_summaries.conf (100%) rename certora/confs/{ => historical_confs}/Safe4337Module_sanity_with_erc20cvl.conf (100%) rename certora/confs/{ => historical_confs}/Safe4337Module_sanity_with_erc20dispatched.conf (100%) rename certora/confs/{ => historical_confs}/SafeModuleSetup_builtin_assertions.conf (100%) rename certora/confs/{ => historical_confs}/SafeModuleSetup_sanity.conf (100%) rename certora/confs/{ => historical_confs}/SafeModuleSetup_sanity_with_all_default_summaries.conf (100%) rename certora/confs/{ => historical_confs}/SafeModuleSetup_sanity_with_erc20cvl.conf (100%) rename certora/confs/{ => historical_confs}/SafeModuleSetup_sanity_with_erc20dispatched.conf (100%) rename certora/confs/{ => historical_confs}/SafeSignerLaunchpad_builtin_assertions.conf (100%) rename certora/confs/{ => historical_confs}/SafeSignerLaunchpad_sanity.conf (100%) rename certora/confs/{ => historical_confs}/SafeSignerLaunchpad_sanity_with_all_default_summaries.conf (100%) rename certora/confs/{ => historical_confs}/SafeSignerLaunchpad_sanity_with_erc20cvl.conf (100%) rename certora/confs/{ => historical_confs}/SafeSignerLaunchpad_sanity_with_erc20dispatched.conf (100%) rename certora/confs/{ => historical_confs}/SafeWebAuthnSignerFactory_builtin_assertions.conf (100%) rename certora/confs/{ => historical_confs}/SafeWebAuthnSignerFactory_sanity.conf (100%) rename certora/confs/{ => historical_confs}/SafeWebAuthnSignerFactory_sanity_with_all_default_summaries.conf (100%) rename certora/confs/{ => historical_confs}/SafeWebAuthnSignerFactory_sanity_with_erc20cvl.conf (100%) rename certora/confs/{ => historical_confs}/SafeWebAuthnSignerFactory_sanity_with_erc20dispatched.conf (100%) rename certora/confs/{ => historical_confs}/SafeWebAuthnSigner_builtin_assertions.conf (100%) rename certora/confs/{ => historical_confs}/SafeWebAuthnSigner_sanity.conf (100%) rename certora/confs/{ => historical_confs}/SafeWebAuthnSigner_sanity_with_all_default_summaries.conf (100%) rename certora/confs/{ => historical_confs}/SafeWebAuthnSigner_sanity_with_erc20cvl.conf (100%) rename certora/confs/{ => historical_confs}/SafeWebAuthnSigner_sanity_with_erc20dispatched.conf (100%) create mode 100644 certora/specs/AllowanceModule.spec create mode 100644 certora/specs/Safe4337Module.spec create mode 100644 certora/specs/SafeModuleSetup.spec create mode 100644 certora/specs/SafeSignerLaunchpad.spec create mode 100644 certora/specs/SafeWebAuthnSigner.spec create mode 100644 certora/specs/SafeWebAuthnSignerFactory.spec diff --git a/certora/confs/AllowanceModule.conf b/certora/confs/AllowanceModule.conf new file mode 100644 index 000000000..1cc779d55 --- /dev/null +++ b/certora/confs/AllowanceModule.conf @@ -0,0 +1,28 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/allowances/contracts/AllowanceModule.sol", + "certora/harnesses/ERC20Like/DummyWeth.sol", + "certora/harnesses/Utilities.sol", + "modules/allowances/contracts/SignatureDecoder.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity_with_all_default_summaries", + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc7.6", + "solc_via_ir": false, + "optimistic_loop": true, + "loop_iter": "5", + "prover_version": "master", + "server": "production", + "verify": "AllowanceModule:certora/specs/AllowanceModule.spec" +} \ No newline at end of file diff --git a/certora/confs/Safe4337Module.conf b/certora/confs/Safe4337Module.conf new file mode 100644 index 000000000..e0cee6c09 --- /dev/null +++ b/certora/confs/Safe4337Module.conf @@ -0,0 +1,29 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/4337/contracts/Safe4337Module.sol", + "certora/harnesses/ERC20Like/DummyWeth.sol", + "certora/harnesses/Utilities.sol", + "modules/4337/contracts/test/TestSingletonSigner.sol", + "modules/4337/certora/harnesses/Account.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity", + "process": "emv", + "prover_args": [ + " -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "optimistic_hashing": true, + "optimistic_loop": true, + "prover_version": "master", + "server": "production", + "verify": "Safe4337Module:certora/specs/Safe4337Module.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeModuleSetup.conf b/certora/confs/SafeModuleSetup.conf new file mode 100644 index 000000000..e59dce6c1 --- /dev/null +++ b/certora/confs/SafeModuleSetup.conf @@ -0,0 +1,26 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/4337/contracts/SafeModuleSetup.sol", + "certora/harnesses/ERC20Like/DummyWeth.sol", + "certora/harnesses/Utilities.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity", + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "optimistic_loop": true, + "prover_version": "master", + "server": "production", + "verify": "SafeModuleSetup:certora/specs/SafeModuleSetup.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeSignerLaunchpad.conf b/certora/confs/SafeSignerLaunchpad.conf new file mode 100644 index 000000000..d22eb8f5e --- /dev/null +++ b/certora/confs/SafeSignerLaunchpad.conf @@ -0,0 +1,28 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/passkey/contracts/4337/SafeSignerLaunchpad.sol", + "certora/harnesses/ERC20Like/DummyWeth.sol", + "certora/harnesses/Utilities.sol", + "modules/passkey/contracts/SafeWebAuthnSignerFactory.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity_with_all_default_summaries", + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "optimistic_hashing": true, + "optimistic_fallback": true, + "prover_version": "master", + "server": "production", + "verify": "SafeSignerLaunchpad:certora/specs/SafeSignerLaunchpad.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeWebAuthnSigner.conf b/certora/confs/SafeWebAuthnSigner.conf new file mode 100644 index 000000000..b59b03b21 --- /dev/null +++ b/certora/confs/SafeWebAuthnSigner.conf @@ -0,0 +1,32 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/passkey/contracts/SafeWebAuthnSigner.sol", + "certora/harnesses/ERC20Like/DummyWeth.sol", + "certora/harnesses/Utilities.sol", + "modules/passkey/contracts/libraries/P256.sol" + ], + "link": [ + "SafeWebAuthnSigner:VERIFIERS=P256" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity_with_all_default_summaries", + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "optimistic_loop": true, + "loop_iter": "6", + "optimistic_hashing": true, + "prover_version": "master", + "server": "production", + "verify": "SafeWebAuthnSigner:certora/specs/SafeWebAuthnSigner.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeWebAuthnSignerFactory.conf b/certora/confs/SafeWebAuthnSignerFactory.conf new file mode 100644 index 000000000..56d2c40fd --- /dev/null +++ b/certora/confs/SafeWebAuthnSignerFactory.conf @@ -0,0 +1,27 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/passkey/contracts/SafeWebAuthnSignerFactory.sol", + "certora/harnesses/ERC20Like/DummyWeth.sol", + "certora/harnesses/Utilities.sol", + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity_with_all_default_summaries", + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "optimistic_loop": true, + "optimistic_hashing": true, + "prover_version": "master", + "server": "production", + "verify": "SafeWebAuthnSignerFactory:certora/specs/SafeWebAuthnSignerFactory.spec" +} \ No newline at end of file diff --git a/certora/confs/AllowanceModule_builtin_assertions.conf b/certora/confs/historical_confs/AllowanceModule_builtin_assertions.conf similarity index 100% rename from certora/confs/AllowanceModule_builtin_assertions.conf rename to certora/confs/historical_confs/AllowanceModule_builtin_assertions.conf diff --git a/certora/confs/AllowanceModule_sanity.conf b/certora/confs/historical_confs/AllowanceModule_sanity.conf similarity index 100% rename from certora/confs/AllowanceModule_sanity.conf rename to certora/confs/historical_confs/AllowanceModule_sanity.conf diff --git a/certora/confs/AllowanceModule_sanity_with_all_default_summaries.conf b/certora/confs/historical_confs/AllowanceModule_sanity_with_all_default_summaries.conf similarity index 100% rename from certora/confs/AllowanceModule_sanity_with_all_default_summaries.conf rename to certora/confs/historical_confs/AllowanceModule_sanity_with_all_default_summaries.conf diff --git a/certora/confs/AllowanceModule_sanity_with_erc20cvl.conf b/certora/confs/historical_confs/AllowanceModule_sanity_with_erc20cvl.conf similarity index 100% rename from certora/confs/AllowanceModule_sanity_with_erc20cvl.conf rename to certora/confs/historical_confs/AllowanceModule_sanity_with_erc20cvl.conf diff --git a/certora/confs/AllowanceModule_sanity_with_erc20dispatched.conf b/certora/confs/historical_confs/AllowanceModule_sanity_with_erc20dispatched.conf similarity index 100% rename from certora/confs/AllowanceModule_sanity_with_erc20dispatched.conf rename to certora/confs/historical_confs/AllowanceModule_sanity_with_erc20dispatched.conf diff --git a/certora/confs/Safe4337Module_builtin_assertions.conf b/certora/confs/historical_confs/Safe4337Module_builtin_assertions.conf similarity index 100% rename from certora/confs/Safe4337Module_builtin_assertions.conf rename to certora/confs/historical_confs/Safe4337Module_builtin_assertions.conf diff --git a/certora/confs/Safe4337Module_sanity.conf b/certora/confs/historical_confs/Safe4337Module_sanity.conf similarity index 100% rename from certora/confs/Safe4337Module_sanity.conf rename to certora/confs/historical_confs/Safe4337Module_sanity.conf diff --git a/certora/confs/Safe4337Module_sanity_with_all_default_summaries.conf b/certora/confs/historical_confs/Safe4337Module_sanity_with_all_default_summaries.conf similarity index 100% rename from certora/confs/Safe4337Module_sanity_with_all_default_summaries.conf rename to certora/confs/historical_confs/Safe4337Module_sanity_with_all_default_summaries.conf diff --git a/certora/confs/Safe4337Module_sanity_with_erc20cvl.conf b/certora/confs/historical_confs/Safe4337Module_sanity_with_erc20cvl.conf similarity index 100% rename from certora/confs/Safe4337Module_sanity_with_erc20cvl.conf rename to certora/confs/historical_confs/Safe4337Module_sanity_with_erc20cvl.conf diff --git a/certora/confs/Safe4337Module_sanity_with_erc20dispatched.conf b/certora/confs/historical_confs/Safe4337Module_sanity_with_erc20dispatched.conf similarity index 100% rename from certora/confs/Safe4337Module_sanity_with_erc20dispatched.conf rename to certora/confs/historical_confs/Safe4337Module_sanity_with_erc20dispatched.conf diff --git a/certora/confs/SafeModuleSetup_builtin_assertions.conf b/certora/confs/historical_confs/SafeModuleSetup_builtin_assertions.conf similarity index 100% rename from certora/confs/SafeModuleSetup_builtin_assertions.conf rename to certora/confs/historical_confs/SafeModuleSetup_builtin_assertions.conf diff --git a/certora/confs/SafeModuleSetup_sanity.conf b/certora/confs/historical_confs/SafeModuleSetup_sanity.conf similarity index 100% rename from certora/confs/SafeModuleSetup_sanity.conf rename to certora/confs/historical_confs/SafeModuleSetup_sanity.conf diff --git a/certora/confs/SafeModuleSetup_sanity_with_all_default_summaries.conf b/certora/confs/historical_confs/SafeModuleSetup_sanity_with_all_default_summaries.conf similarity index 100% rename from certora/confs/SafeModuleSetup_sanity_with_all_default_summaries.conf rename to certora/confs/historical_confs/SafeModuleSetup_sanity_with_all_default_summaries.conf diff --git a/certora/confs/SafeModuleSetup_sanity_with_erc20cvl.conf b/certora/confs/historical_confs/SafeModuleSetup_sanity_with_erc20cvl.conf similarity index 100% rename from certora/confs/SafeModuleSetup_sanity_with_erc20cvl.conf rename to certora/confs/historical_confs/SafeModuleSetup_sanity_with_erc20cvl.conf diff --git a/certora/confs/SafeModuleSetup_sanity_with_erc20dispatched.conf b/certora/confs/historical_confs/SafeModuleSetup_sanity_with_erc20dispatched.conf similarity index 100% rename from certora/confs/SafeModuleSetup_sanity_with_erc20dispatched.conf rename to certora/confs/historical_confs/SafeModuleSetup_sanity_with_erc20dispatched.conf diff --git a/certora/confs/SafeSignerLaunchpad_builtin_assertions.conf b/certora/confs/historical_confs/SafeSignerLaunchpad_builtin_assertions.conf similarity index 100% rename from certora/confs/SafeSignerLaunchpad_builtin_assertions.conf rename to certora/confs/historical_confs/SafeSignerLaunchpad_builtin_assertions.conf diff --git a/certora/confs/SafeSignerLaunchpad_sanity.conf b/certora/confs/historical_confs/SafeSignerLaunchpad_sanity.conf similarity index 100% rename from certora/confs/SafeSignerLaunchpad_sanity.conf rename to certora/confs/historical_confs/SafeSignerLaunchpad_sanity.conf diff --git a/certora/confs/SafeSignerLaunchpad_sanity_with_all_default_summaries.conf b/certora/confs/historical_confs/SafeSignerLaunchpad_sanity_with_all_default_summaries.conf similarity index 100% rename from certora/confs/SafeSignerLaunchpad_sanity_with_all_default_summaries.conf rename to certora/confs/historical_confs/SafeSignerLaunchpad_sanity_with_all_default_summaries.conf diff --git a/certora/confs/SafeSignerLaunchpad_sanity_with_erc20cvl.conf b/certora/confs/historical_confs/SafeSignerLaunchpad_sanity_with_erc20cvl.conf similarity index 100% rename from certora/confs/SafeSignerLaunchpad_sanity_with_erc20cvl.conf rename to certora/confs/historical_confs/SafeSignerLaunchpad_sanity_with_erc20cvl.conf diff --git a/certora/confs/SafeSignerLaunchpad_sanity_with_erc20dispatched.conf b/certora/confs/historical_confs/SafeSignerLaunchpad_sanity_with_erc20dispatched.conf similarity index 100% rename from certora/confs/SafeSignerLaunchpad_sanity_with_erc20dispatched.conf rename to certora/confs/historical_confs/SafeSignerLaunchpad_sanity_with_erc20dispatched.conf diff --git a/certora/confs/SafeWebAuthnSignerFactory_builtin_assertions.conf b/certora/confs/historical_confs/SafeWebAuthnSignerFactory_builtin_assertions.conf similarity index 100% rename from certora/confs/SafeWebAuthnSignerFactory_builtin_assertions.conf rename to certora/confs/historical_confs/SafeWebAuthnSignerFactory_builtin_assertions.conf diff --git a/certora/confs/SafeWebAuthnSignerFactory_sanity.conf b/certora/confs/historical_confs/SafeWebAuthnSignerFactory_sanity.conf similarity index 100% rename from certora/confs/SafeWebAuthnSignerFactory_sanity.conf rename to certora/confs/historical_confs/SafeWebAuthnSignerFactory_sanity.conf diff --git a/certora/confs/SafeWebAuthnSignerFactory_sanity_with_all_default_summaries.conf b/certora/confs/historical_confs/SafeWebAuthnSignerFactory_sanity_with_all_default_summaries.conf similarity index 100% rename from certora/confs/SafeWebAuthnSignerFactory_sanity_with_all_default_summaries.conf rename to certora/confs/historical_confs/SafeWebAuthnSignerFactory_sanity_with_all_default_summaries.conf diff --git a/certora/confs/SafeWebAuthnSignerFactory_sanity_with_erc20cvl.conf b/certora/confs/historical_confs/SafeWebAuthnSignerFactory_sanity_with_erc20cvl.conf similarity index 100% rename from certora/confs/SafeWebAuthnSignerFactory_sanity_with_erc20cvl.conf rename to certora/confs/historical_confs/SafeWebAuthnSignerFactory_sanity_with_erc20cvl.conf diff --git a/certora/confs/SafeWebAuthnSignerFactory_sanity_with_erc20dispatched.conf b/certora/confs/historical_confs/SafeWebAuthnSignerFactory_sanity_with_erc20dispatched.conf similarity index 100% rename from certora/confs/SafeWebAuthnSignerFactory_sanity_with_erc20dispatched.conf rename to certora/confs/historical_confs/SafeWebAuthnSignerFactory_sanity_with_erc20dispatched.conf diff --git a/certora/confs/SafeWebAuthnSigner_builtin_assertions.conf b/certora/confs/historical_confs/SafeWebAuthnSigner_builtin_assertions.conf similarity index 100% rename from certora/confs/SafeWebAuthnSigner_builtin_assertions.conf rename to certora/confs/historical_confs/SafeWebAuthnSigner_builtin_assertions.conf diff --git a/certora/confs/SafeWebAuthnSigner_sanity.conf b/certora/confs/historical_confs/SafeWebAuthnSigner_sanity.conf similarity index 100% rename from certora/confs/SafeWebAuthnSigner_sanity.conf rename to certora/confs/historical_confs/SafeWebAuthnSigner_sanity.conf diff --git a/certora/confs/SafeWebAuthnSigner_sanity_with_all_default_summaries.conf b/certora/confs/historical_confs/SafeWebAuthnSigner_sanity_with_all_default_summaries.conf similarity index 100% rename from certora/confs/SafeWebAuthnSigner_sanity_with_all_default_summaries.conf rename to certora/confs/historical_confs/SafeWebAuthnSigner_sanity_with_all_default_summaries.conf diff --git a/certora/confs/SafeWebAuthnSigner_sanity_with_erc20cvl.conf b/certora/confs/historical_confs/SafeWebAuthnSigner_sanity_with_erc20cvl.conf similarity index 100% rename from certora/confs/SafeWebAuthnSigner_sanity_with_erc20cvl.conf rename to certora/confs/historical_confs/SafeWebAuthnSigner_sanity_with_erc20cvl.conf diff --git a/certora/confs/SafeWebAuthnSigner_sanity_with_erc20dispatched.conf b/certora/confs/historical_confs/SafeWebAuthnSigner_sanity_with_erc20dispatched.conf similarity index 100% rename from certora/confs/SafeWebAuthnSigner_sanity_with_erc20dispatched.conf rename to certora/confs/historical_confs/SafeWebAuthnSigner_sanity_with_erc20dispatched.conf diff --git a/certora/specs/AllowanceModule.spec b/certora/specs/AllowanceModule.spec new file mode 100644 index 000000000..d0c84dd8c --- /dev/null +++ b/certora/specs/AllowanceModule.spec @@ -0,0 +1,32 @@ +import "ERC20/erc20cvl.spec"; +import "ERC20/WETHcvl.spec"; +import "ERC721/erc721.spec"; +import "ERC1967/erc1967.spec"; +import "PriceAggregators/chainlink.spec"; +import "PriceAggregators/tellor.spec"; + +import "problems.spec"; +import "unresolved.spec"; +import "optimizations.spec"; + +import "generic.spec"; // pick additional rules from here + +methods { + function _.execTransactionFromModule( + address to, + uint256 value, + bytes data, + Enum.Operation operation + ) external => HAVOC_ECF; +} + +use builtin rule sanity filtered { f -> f.contract == currentContract } + +use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } +use builtin rule msgValueInLoopRule; +use builtin rule viewReentrancy; +use rule privilegedOperation filtered { f -> f.contract == currentContract } +use rule timeoutChecker filtered { f -> f.contract == currentContract } +use rule simpleFrontRunning filtered { f -> f.contract == currentContract } +use rule noRevert filtered { f -> f.contract == currentContract } +use rule alwaysRevert filtered { f -> f.contract == currentContract } \ No newline at end of file diff --git a/certora/specs/Safe4337Module.spec b/certora/specs/Safe4337Module.spec new file mode 100644 index 000000000..0e41df273 --- /dev/null +++ b/certora/specs/Safe4337Module.spec @@ -0,0 +1,33 @@ +import "ERC20/erc20cvl.spec"; +import "ERC20/WETHcvl.spec"; +import "ERC721/erc721.spec"; +import "ERC1967/erc1967.spec"; +import "PriceAggregators/chainlink.spec"; +import "PriceAggregators/tellor.spec"; + +import "problems.spec"; +import "unresolved.spec"; +import "optimizations.spec"; + +import "generic.spec"; // pick additional rules from here + +methods { + function _.isValidSignature(bytes data, bytes signatureData) external => DISPATCHER(true); + function _.execTransactionFromModuleReturnData(address to, uint256 value, bytes data, uint8 operation) external => HAVOC_ECF; + function _.execTransactionFromModule(address to, uint256 value, bytes data, uint8 operation) external => HAVOC_ECF; + function _.checkSignatures(bytes32 dataHash, bytes data, bytes signatures) external => DISPATCHER(true); + function _.domainSeparator() external => DISPATCHER(true); + function _.getModulesPaginated(address start, uint256 pageSize) external => HAVOC_ECF; +} + + +use builtin rule sanity filtered { f -> f.contract == currentContract } + +use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } +use builtin rule msgValueInLoopRule; +use builtin rule viewReentrancy; +use rule privilegedOperation filtered { f -> f.contract == currentContract } +use rule timeoutChecker filtered { f -> f.contract == currentContract } +use rule simpleFrontRunning filtered { f -> f.contract == currentContract } +use rule noRevert filtered { f -> f.contract == currentContract } +use rule alwaysRevert filtered { f -> f.contract == currentContract } \ No newline at end of file diff --git a/certora/specs/SafeModuleSetup.spec b/certora/specs/SafeModuleSetup.spec new file mode 100644 index 000000000..a2925b0ec --- /dev/null +++ b/certora/specs/SafeModuleSetup.spec @@ -0,0 +1,27 @@ +import "ERC20/erc20cvl.spec"; +import "ERC20/WETHcvl.spec"; +import "ERC721/erc721.spec"; +import "ERC1967/erc1967.spec"; +import "PriceAggregators/chainlink.spec"; +import "PriceAggregators/tellor.spec"; + +import "problems.spec"; +import "unresolved.spec"; +import "optimizations.spec"; + +import "generic.spec"; // pick additional rules from here + +methods { + function _.enableModule(address module) external => DISPATCHER(true); +} + +use builtin rule sanity filtered { f -> f.contract == currentContract } + +use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } +use builtin rule msgValueInLoopRule; +use builtin rule viewReentrancy; +use rule privilegedOperation filtered { f -> f.contract == currentContract } +use rule timeoutChecker filtered { f -> f.contract == currentContract } +use rule simpleFrontRunning filtered { f -> f.contract == currentContract } +use rule noRevert filtered { f -> f.contract == currentContract } +use rule alwaysRevert filtered { f -> f.contract == currentContract } \ No newline at end of file diff --git a/certora/specs/SafeSignerLaunchpad.spec b/certora/specs/SafeSignerLaunchpad.spec new file mode 100644 index 000000000..9510f0338 --- /dev/null +++ b/certora/specs/SafeSignerLaunchpad.spec @@ -0,0 +1,38 @@ +import "ERC20/erc20cvl.spec"; +import "ERC20/WETHcvl.spec"; +import "ERC721/erc721.spec"; +import "ERC1967/erc1967.spec"; +import "PriceAggregators/chainlink.spec"; +import "PriceAggregators/tellor.spec"; + +import "problems.spec"; +import "unresolved.spec"; +import "optimizations.spec"; + +import "generic.spec"; // pick additional rules from here + +methods { + function _.createSigner(uint256 x, uint256 y, P256.Verifiers verifiers) external => HAVOC_ECF; + function _.isValidSignatureForSigner(bytes32 message, bytes signature, uint256 x, uint256 y, P256.Verifiers verifiers) external => HAVOC_ECF; + function _.setup( + address[] _owners, + uint256 _threshold, + address to, + bytes data, + address fallbackHandler, + address paymentToken, + uint256 payment, + address paymentReceiver + ) external => HAVOC_ECF; +} + +use builtin rule sanity filtered { f -> f.contract == currentContract } + +use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } +use builtin rule msgValueInLoopRule; +use builtin rule viewReentrancy; +use rule privilegedOperation filtered { f -> f.contract == currentContract } +use rule timeoutChecker filtered { f -> f.contract == currentContract } +use rule simpleFrontRunning filtered { f -> f.contract == currentContract } +use rule noRevert filtered { f -> f.contract == currentContract } +use rule alwaysRevert filtered { f -> f.contract == currentContract } \ No newline at end of file diff --git a/certora/specs/SafeWebAuthnSigner.spec b/certora/specs/SafeWebAuthnSigner.spec new file mode 100644 index 000000000..389234a1c --- /dev/null +++ b/certora/specs/SafeWebAuthnSigner.spec @@ -0,0 +1,23 @@ +import "ERC20/erc20cvl.spec"; +import "ERC20/WETHcvl.spec"; +import "ERC721/erc721.spec"; +import "ERC1967/erc1967.spec"; +import "PriceAggregators/chainlink.spec"; +import "PriceAggregators/tellor.spec"; + +import "problems.spec"; +import "unresolved.spec"; +import "optimizations.spec"; + +import "generic.spec"; // pick additional rules from here + +use builtin rule sanity filtered { f -> f.contract == currentContract } + +use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } +use builtin rule msgValueInLoopRule; +use builtin rule viewReentrancy; +use rule privilegedOperation filtered { f -> f.contract == currentContract } +use rule timeoutChecker filtered { f -> f.contract == currentContract } +use rule simpleFrontRunning filtered { f -> f.contract == currentContract } +use rule noRevert filtered { f -> f.contract == currentContract } +use rule alwaysRevert filtered { f -> f.contract == currentContract } \ No newline at end of file diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec new file mode 100644 index 000000000..389234a1c --- /dev/null +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -0,0 +1,23 @@ +import "ERC20/erc20cvl.spec"; +import "ERC20/WETHcvl.spec"; +import "ERC721/erc721.spec"; +import "ERC1967/erc1967.spec"; +import "PriceAggregators/chainlink.spec"; +import "PriceAggregators/tellor.spec"; + +import "problems.spec"; +import "unresolved.spec"; +import "optimizations.spec"; + +import "generic.spec"; // pick additional rules from here + +use builtin rule sanity filtered { f -> f.contract == currentContract } + +use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } +use builtin rule msgValueInLoopRule; +use builtin rule viewReentrancy; +use rule privilegedOperation filtered { f -> f.contract == currentContract } +use rule timeoutChecker filtered { f -> f.contract == currentContract } +use rule simpleFrontRunning filtered { f -> f.contract == currentContract } +use rule noRevert filtered { f -> f.contract == currentContract } +use rule alwaysRevert filtered { f -> f.contract == currentContract } \ No newline at end of file diff --git a/certora/specs/setup/sanity.spec b/certora/specs/setup/sanity.spec index b44a649b6..f5b8619a7 100644 --- a/certora/specs/setup/sanity.spec +++ b/certora/specs/setup/sanity.spec @@ -2,4 +2,4 @@ import "../problems.spec"; import "../unresolved.spec"; import "../optimizations.spec"; -use builtin rule sanity; \ No newline at end of file +use builtin rule sanity filtered { f -> f.contract == currentContract } \ No newline at end of file diff --git a/certora/specs/setup/sanity_with_all_default_summaries.spec b/certora/specs/setup/sanity_with_all_default_summaries.spec index 19a15bfa6..96b994078 100644 --- a/certora/specs/setup/sanity_with_all_default_summaries.spec +++ b/certora/specs/setup/sanity_with_all_default_summaries.spec @@ -11,13 +11,4 @@ import "../optimizations.spec"; import "../generic.spec"; // pick additional rules from here -use builtin rule sanity filtered { f -> f.contract == currentContract } - -use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } -use builtin rule msgValueInLoopRule; -use builtin rule viewReentrancy; -use rule privilegedOperation filtered { f -> f.contract == currentContract } -use rule timeoutChecker filtered { f -> f.contract == currentContract } -use rule simpleFrontRunning filtered { f -> f.contract == currentContract } -use rule noRevert filtered { f -> f.contract == currentContract } -use rule alwaysRevert filtered { f -> f.contract == currentContract } \ No newline at end of file +use builtin rule sanity filtered { f -> f.contract == currentContract } \ No newline at end of file From da5921267574edb7d7e679550fbbc69c9b238092 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Wed, 15 May 2024 11:27:06 +0300 Subject: [PATCH 004/103] . --- certora/confs/SafeWebAuthnSignerProxy.conf | 34 +++++++++++++++++++ ....conf => SafeWebAuthnSignerSingleton.conf} | 9 ++--- .../AllowanceModule.conf | 0 .../Safe4337Module.conf | 0 .../SafeModuleSetup.conf | 0 .../SafeSignerLaunchpad.conf | 0 certora/specs/SafeWebAuthnSignerFactory.spec | 8 ++--- ...gner.spec => SafeWebAuthnSignerProxy.spec} | 8 ++--- .../specs/SafeWebAuthnSignerSingleton.spec | 23 +++++++++++++ .../{ => spec_utils}/AllowanceModule.spec | 0 .../{ => spec_utils}/Safe4337Module.spec | 0 .../{ => spec_utils}/SafeModuleSetup.spec | 0 .../{ => spec_utils}/SafeSignerLaunchpad.spec | 0 certora/specs/{ => spec_utils}/generic.spec | 0 .../specs/{ => spec_utils}/optimizations.spec | 0 certora/specs/{ => spec_utils}/problems.spec | 0 .../specs/{ => spec_utils}/unresolved.spec | 0 17 files changed, 67 insertions(+), 15 deletions(-) create mode 100644 certora/confs/SafeWebAuthnSignerProxy.conf rename certora/confs/{SafeWebAuthnSigner.conf => SafeWebAuthnSignerSingleton.conf} (72%) rename certora/confs/{ => historical_confs}/AllowanceModule.conf (100%) rename certora/confs/{ => historical_confs}/Safe4337Module.conf (100%) rename certora/confs/{ => historical_confs}/SafeModuleSetup.conf (100%) rename certora/confs/{ => historical_confs}/SafeSignerLaunchpad.conf (100%) rename certora/specs/{SafeWebAuthnSigner.spec => SafeWebAuthnSignerProxy.spec} (81%) create mode 100644 certora/specs/SafeWebAuthnSignerSingleton.spec rename certora/specs/{ => spec_utils}/AllowanceModule.spec (100%) rename certora/specs/{ => spec_utils}/Safe4337Module.spec (100%) rename certora/specs/{ => spec_utils}/SafeModuleSetup.spec (100%) rename certora/specs/{ => spec_utils}/SafeSignerLaunchpad.spec (100%) rename certora/specs/{ => spec_utils}/generic.spec (100%) rename certora/specs/{ => spec_utils}/optimizations.spec (100%) rename certora/specs/{ => spec_utils}/problems.spec (100%) rename certora/specs/{ => spec_utils}/unresolved.spec (100%) diff --git a/certora/confs/SafeWebAuthnSignerProxy.conf b/certora/confs/SafeWebAuthnSignerProxy.conf new file mode 100644 index 000000000..80b4979ff --- /dev/null +++ b/certora/confs/SafeWebAuthnSignerProxy.conf @@ -0,0 +1,34 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol", + "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", + "certora/harnesses/ERC20Like/DummyWeth.sol", + "certora/harnesses/Utilities.sol", + "modules/passkey/contracts/libraries/P256.sol" + ], + "link": [ + "SafeWebAuthnSignerProxy:_VERIFIERS=P256", + "SafeWebAuthnSignerProxy:_SINGLETON=SafeWebAuthnSignerSingleton" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity_with_all_default_summaries", + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "optimistic_loop": true, + "loop_iter": "6", + "optimistic_hashing": true, + "prover_version": "master", + "server": "production", + "verify": "SafeWebAuthnSignerProxy:certora/specs/SafeWebAuthnSignerProxy.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeWebAuthnSigner.conf b/certora/confs/SafeWebAuthnSignerSingleton.conf similarity index 72% rename from certora/confs/SafeWebAuthnSigner.conf rename to certora/confs/SafeWebAuthnSignerSingleton.conf index b59b03b21..b43b06da5 100644 --- a/certora/confs/SafeWebAuthnSigner.conf +++ b/certora/confs/SafeWebAuthnSignerSingleton.conf @@ -1,13 +1,9 @@ { "assert_autofinder_success": true, "files": [ - "modules/passkey/contracts/SafeWebAuthnSigner.sol", + "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", "certora/harnesses/ERC20Like/DummyWeth.sol", "certora/harnesses/Utilities.sol", - "modules/passkey/contracts/libraries/P256.sol" - ], - "link": [ - "SafeWebAuthnSigner:VERIFIERS=P256" ], "java_args": [ " -ea -Dlevel.setup.helpers=info" @@ -24,9 +20,8 @@ "solc": "solc8.23", "solc_via_ir": false, "optimistic_loop": true, - "loop_iter": "6", "optimistic_hashing": true, "prover_version": "master", "server": "production", - "verify": "SafeWebAuthnSigner:certora/specs/SafeWebAuthnSigner.spec" + "verify": "SafeWebAuthnSignerSingleton:certora/specs/SafeWebAuthnSignerSingleton.spec" } \ No newline at end of file diff --git a/certora/confs/AllowanceModule.conf b/certora/confs/historical_confs/AllowanceModule.conf similarity index 100% rename from certora/confs/AllowanceModule.conf rename to certora/confs/historical_confs/AllowanceModule.conf diff --git a/certora/confs/Safe4337Module.conf b/certora/confs/historical_confs/Safe4337Module.conf similarity index 100% rename from certora/confs/Safe4337Module.conf rename to certora/confs/historical_confs/Safe4337Module.conf diff --git a/certora/confs/SafeModuleSetup.conf b/certora/confs/historical_confs/SafeModuleSetup.conf similarity index 100% rename from certora/confs/SafeModuleSetup.conf rename to certora/confs/historical_confs/SafeModuleSetup.conf diff --git a/certora/confs/SafeSignerLaunchpad.conf b/certora/confs/historical_confs/SafeSignerLaunchpad.conf similarity index 100% rename from certora/confs/SafeSignerLaunchpad.conf rename to certora/confs/historical_confs/SafeSignerLaunchpad.conf diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index 389234a1c..938aba94a 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -5,11 +5,11 @@ import "ERC1967/erc1967.spec"; import "PriceAggregators/chainlink.spec"; import "PriceAggregators/tellor.spec"; -import "problems.spec"; -import "unresolved.spec"; -import "optimizations.spec"; +import "spec_utils/problems.spec"; +import "spec_utils/unresolved.spec"; +import "spec_utils/optimizations.spec"; -import "generic.spec"; // pick additional rules from here +import "spec_utils/generic.spec"; // pick additional rules from here use builtin rule sanity filtered { f -> f.contract == currentContract } diff --git a/certora/specs/SafeWebAuthnSigner.spec b/certora/specs/SafeWebAuthnSignerProxy.spec similarity index 81% rename from certora/specs/SafeWebAuthnSigner.spec rename to certora/specs/SafeWebAuthnSignerProxy.spec index 389234a1c..938aba94a 100644 --- a/certora/specs/SafeWebAuthnSigner.spec +++ b/certora/specs/SafeWebAuthnSignerProxy.spec @@ -5,11 +5,11 @@ import "ERC1967/erc1967.spec"; import "PriceAggregators/chainlink.spec"; import "PriceAggregators/tellor.spec"; -import "problems.spec"; -import "unresolved.spec"; -import "optimizations.spec"; +import "spec_utils/problems.spec"; +import "spec_utils/unresolved.spec"; +import "spec_utils/optimizations.spec"; -import "generic.spec"; // pick additional rules from here +import "spec_utils/generic.spec"; // pick additional rules from here use builtin rule sanity filtered { f -> f.contract == currentContract } diff --git a/certora/specs/SafeWebAuthnSignerSingleton.spec b/certora/specs/SafeWebAuthnSignerSingleton.spec new file mode 100644 index 000000000..938aba94a --- /dev/null +++ b/certora/specs/SafeWebAuthnSignerSingleton.spec @@ -0,0 +1,23 @@ +import "ERC20/erc20cvl.spec"; +import "ERC20/WETHcvl.spec"; +import "ERC721/erc721.spec"; +import "ERC1967/erc1967.spec"; +import "PriceAggregators/chainlink.spec"; +import "PriceAggregators/tellor.spec"; + +import "spec_utils/problems.spec"; +import "spec_utils/unresolved.spec"; +import "spec_utils/optimizations.spec"; + +import "spec_utils/generic.spec"; // pick additional rules from here + +use builtin rule sanity filtered { f -> f.contract == currentContract } + +use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } +use builtin rule msgValueInLoopRule; +use builtin rule viewReentrancy; +use rule privilegedOperation filtered { f -> f.contract == currentContract } +use rule timeoutChecker filtered { f -> f.contract == currentContract } +use rule simpleFrontRunning filtered { f -> f.contract == currentContract } +use rule noRevert filtered { f -> f.contract == currentContract } +use rule alwaysRevert filtered { f -> f.contract == currentContract } \ No newline at end of file diff --git a/certora/specs/AllowanceModule.spec b/certora/specs/spec_utils/AllowanceModule.spec similarity index 100% rename from certora/specs/AllowanceModule.spec rename to certora/specs/spec_utils/AllowanceModule.spec diff --git a/certora/specs/Safe4337Module.spec b/certora/specs/spec_utils/Safe4337Module.spec similarity index 100% rename from certora/specs/Safe4337Module.spec rename to certora/specs/spec_utils/Safe4337Module.spec diff --git a/certora/specs/SafeModuleSetup.spec b/certora/specs/spec_utils/SafeModuleSetup.spec similarity index 100% rename from certora/specs/SafeModuleSetup.spec rename to certora/specs/spec_utils/SafeModuleSetup.spec diff --git a/certora/specs/SafeSignerLaunchpad.spec b/certora/specs/spec_utils/SafeSignerLaunchpad.spec similarity index 100% rename from certora/specs/SafeSignerLaunchpad.spec rename to certora/specs/spec_utils/SafeSignerLaunchpad.spec diff --git a/certora/specs/generic.spec b/certora/specs/spec_utils/generic.spec similarity index 100% rename from certora/specs/generic.spec rename to certora/specs/spec_utils/generic.spec diff --git a/certora/specs/optimizations.spec b/certora/specs/spec_utils/optimizations.spec similarity index 100% rename from certora/specs/optimizations.spec rename to certora/specs/spec_utils/optimizations.spec diff --git a/certora/specs/problems.spec b/certora/specs/spec_utils/problems.spec similarity index 100% rename from certora/specs/problems.spec rename to certora/specs/spec_utils/problems.spec diff --git a/certora/specs/unresolved.spec b/certora/specs/spec_utils/unresolved.spec similarity index 100% rename from certora/specs/unresolved.spec rename to certora/specs/spec_utils/unresolved.spec From 9488e45abb6ce7560f3584e2c48cd3cb603b41f3 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Wed, 15 May 2024 12:56:09 +0300 Subject: [PATCH 005/103] Add webAuthn conf --- certora/confs/WebAuthn.conf | 27 +++++++++++++ certora/harnesses/WebAuthnHarness.sol | 56 +++++++++++++++++++++++++++ certora/specs/WebAuthn.spec | 23 +++++++++++ 3 files changed, 106 insertions(+) create mode 100644 certora/confs/WebAuthn.conf create mode 100644 certora/harnesses/WebAuthnHarness.sol create mode 100644 certora/specs/WebAuthn.spec diff --git a/certora/confs/WebAuthn.conf b/certora/confs/WebAuthn.conf new file mode 100644 index 000000000..d69fc6128 --- /dev/null +++ b/certora/confs/WebAuthn.conf @@ -0,0 +1,27 @@ +{ + "assert_autofinder_success": true, + "files": [ + "certora/harnesses/WebAuthnHarness.sol", + "certora/harnesses/ERC20Like/DummyWeth.sol", + "certora/harnesses/Utilities.sol", + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity_with_all_default_summaries", + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "optimistic_loop": true, + "optimistic_hashing": true, + "prover_version": "master", + "server": "production", + "verify": "WebAuthnHarness:certora/specs/WebAuthn.spec" +} \ No newline at end of file diff --git a/certora/harnesses/WebAuthnHarness.sol b/certora/harnesses/WebAuthnHarness.sol new file mode 100644 index 000000000..e214cdfb4 --- /dev/null +++ b/certora/harnesses/WebAuthnHarness.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +import {P256, WebAuthn} from "../../modules/passkey/contracts/libraries/WebAuthn.sol"; + +contract WebAuthnHarness { + + function castSignature(bytes calldata signature) internal pure returns (bool isValid, WebAuthn.Signature calldata data){ + return WebAuthn.castSignature(signature); + } + + function encodeClientDataJson( + bytes32 challenge, + string calldata clientDataFields + ) public pure returns (string memory clientDataJson){ + return WebAuthn.encodeClientDataJson(challenge, clientDataFields); + } + + function encodeSigningMessage( + bytes32 challenge, + bytes calldata authenticatorData, + string calldata clientDataFields + ) public view returns (bytes memory message) { + return WebAuthn.encodeSigningMessage(challenge, authenticatorData, clientDataFields); + } + + function checkAuthenticatorFlags( + bytes calldata authenticatorData, + WebAuthn.AuthenticatorFlags authenticatorFlags + ) public pure returns (bool success){ + return WebAuthn.checkAuthenticatorFlags(authenticatorData, authenticatorFlags); + } + + function verifySignature( + bytes32 challenge, + bytes calldata signature, + WebAuthn.AuthenticatorFlags authenticatorFlags, + uint256 x, + uint256 y, + P256.Verifiers verifiers + ) public view returns (bool success) { + return WebAuthn.verifySignature(challenge, signature, authenticatorFlags, x, y, verifiers); + } + + function verifySignature( + bytes32 challenge, + WebAuthn.Signature calldata signature, + WebAuthn.AuthenticatorFlags authenticatorFlags, + uint256 x, + uint256 y, + P256.Verifiers verifiers + ) public view returns (bool success) { + return WebAuthn.verifySignature(challenge, signature, authenticatorFlags, x, y, verifiers); + } + +} diff --git a/certora/specs/WebAuthn.spec b/certora/specs/WebAuthn.spec new file mode 100644 index 000000000..938aba94a --- /dev/null +++ b/certora/specs/WebAuthn.spec @@ -0,0 +1,23 @@ +import "ERC20/erc20cvl.spec"; +import "ERC20/WETHcvl.spec"; +import "ERC721/erc721.spec"; +import "ERC1967/erc1967.spec"; +import "PriceAggregators/chainlink.spec"; +import "PriceAggregators/tellor.spec"; + +import "spec_utils/problems.spec"; +import "spec_utils/unresolved.spec"; +import "spec_utils/optimizations.spec"; + +import "spec_utils/generic.spec"; // pick additional rules from here + +use builtin rule sanity filtered { f -> f.contract == currentContract } + +use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } +use builtin rule msgValueInLoopRule; +use builtin rule viewReentrancy; +use rule privilegedOperation filtered { f -> f.contract == currentContract } +use rule timeoutChecker filtered { f -> f.contract == currentContract } +use rule simpleFrontRunning filtered { f -> f.contract == currentContract } +use rule noRevert filtered { f -> f.contract == currentContract } +use rule alwaysRevert filtered { f -> f.contract == currentContract } \ No newline at end of file From 8ba383109f57089d94af2de809bd81e5ff94315e Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Wed, 15 May 2024 14:13:54 +0300 Subject: [PATCH 006/103] Fix sanity using alex parameters --- certora/confs/SafeWebAuthnSignerFactory.conf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/certora/confs/SafeWebAuthnSignerFactory.conf b/certora/confs/SafeWebAuthnSignerFactory.conf index 56d2c40fd..e8b8cbc16 100644 --- a/certora/confs/SafeWebAuthnSignerFactory.conf +++ b/certora/confs/SafeWebAuthnSignerFactory.conf @@ -21,6 +21,8 @@ "solc_via_ir": false, "optimistic_loop": true, "optimistic_hashing": true, + "hashing_length_bound": "4694", + "loop_iter": "144", "prover_version": "master", "server": "production", "verify": "SafeWebAuthnSignerFactory:certora/specs/SafeWebAuthnSignerFactory.spec" From b8989c370df500e50740d0763f32c7c25c273ff1 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Wed, 15 May 2024 18:30:25 +0300 Subject: [PATCH 007/103] Add property list --- certora/confs/SafeWebAuthnSignerFactory.conf | 4 ++ certora/properties.txt | 11 +++ certora/specs/SafeWebAuthnSignerFactory.spec | 75 ++++++++++++++++++-- 3 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 certora/properties.txt diff --git a/certora/confs/SafeWebAuthnSignerFactory.conf b/certora/confs/SafeWebAuthnSignerFactory.conf index e8b8cbc16..5784ee1ed 100644 --- a/certora/confs/SafeWebAuthnSignerFactory.conf +++ b/certora/confs/SafeWebAuthnSignerFactory.conf @@ -2,12 +2,16 @@ "assert_autofinder_success": true, "files": [ "modules/passkey/contracts/SafeWebAuthnSignerFactory.sol", + "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", "certora/harnesses/ERC20Like/DummyWeth.sol", "certora/harnesses/Utilities.sol", ], "java_args": [ " -ea -Dlevel.setup.helpers=info" ], + "link": [ + "SafeWebAuthnSignerFactory:SINGLETON=SafeWebAuthnSignerSingleton" + ], "msg": "sanity_with_all_default_summaries", "process": "emv", "prover_args": [ diff --git a/certora/properties.txt b/certora/properties.txt new file mode 100644 index 000000000..aa4abb442 --- /dev/null +++ b/certora/properties.txt @@ -0,0 +1,11 @@ +1. Factory - isValidSignaure will return the same as create signer +2. Integrity for Proxy <- this is not a standard implementation +3. WebAuthn - encodeSigningMessage compute presage which needs to get hashed + Given data should be able to to produce only 1 valid message. +4. Factory - Immutability of Singleton Contract. +5. Factory - getSigner is unique for every x,y and verifier combination (need to make sure it is required) +6. Factory - createSigner and getSigner always returns the same address. +7. Factory - Deterministic Address Calculation for Signers. +8. Factory - Correctness of Signer Creation. (Cant called twice, override) +9. Factory - Signature Validation (isValidSignatureForSigner Integrity) +10. Factory - Code Presence Check (_hasNoCode Integrity) \ No newline at end of file diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index 938aba94a..b55f199b2 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -4,15 +4,18 @@ import "ERC721/erc721.spec"; import "ERC1967/erc1967.spec"; import "PriceAggregators/chainlink.spec"; import "PriceAggregators/tellor.spec"; - import "spec_utils/problems.spec"; import "spec_utils/unresolved.spec"; import "spec_utils/optimizations.spec"; - import "spec_utils/generic.spec"; // pick additional rules from here -use builtin rule sanity filtered { f -> f.contract == currentContract } +methods{ + function getSigner(uint256, uint256, P256.Verifiers) external returns (address) envfree; + function createSigner(uint256, uint256, P256.Verifiers) external returns (address) envfree; +} + +use builtin rule sanity filtered { f -> f.contract == currentContract } use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } use builtin rule msgValueInLoopRule; use builtin rule viewReentrancy; @@ -20,4 +23,68 @@ use rule privilegedOperation filtered { f -> f.contract == currentContract } use rule timeoutChecker filtered { f -> f.contract == currentContract } use rule simpleFrontRunning filtered { f -> f.contract == currentContract } use rule noRevert filtered { f -> f.contract == currentContract } -use rule alwaysRevert filtered { f -> f.contract == currentContract } \ No newline at end of file +use rule alwaysRevert filtered { f -> f.contract == currentContract } + + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Singleton implementation never change │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ +rule singletonNeverChanges() +{ + env e; + method f; + calldataarg args; + address currentSingleton = currentContract.SINGLETON; + + f(e, args); + + assert currentSingleton == currentContract.SINGLETON; +} + + + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ getSigner is unique for every x,y and verifier combination │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ + +rule uniqueSigner(){ + uint256 firstX; + uint256 firstY; + P256.Verifiers firstVerifier; + + address firstSigner = getSigner(firstX, firstY, firstVerifier); + + uint256 secondX; + uint256 secondY; + P256.Verifiers secondVerifier; + + address secondSigner = getSigner(secondX, secondY, secondVerifier); + + assert firstSigner == secondSigner <=> (firstX == secondX && firstY == secondY && firstVerifier == secondVerifier); +} + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ createSigner and getSigner always returns the same address │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ + +rule createAndGetSignerEquivalence(){ + uint256 createX; + uint256 createY; + P256.Verifiers createVerifier; + + address signer1 = createSigner(createX, createY, createVerifier); + + uint256 getX; + uint256 getY; + P256.Verifiers getVerifier; + + address signer2 = getSigner(getX, getY, getVerifier); + + assert signer1 == signer2 <=> (createX == getX && createY == getY && createVerifier == getVerifier); +} \ No newline at end of file From d71c9a851f9b2ad760d51ff419e25b322904a300 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Wed, 15 May 2024 18:59:34 +0300 Subject: [PATCH 008/103] Add property list --- certora/properties.txt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/certora/properties.txt b/certora/properties.txt index aa4abb442..3a1a7d116 100644 --- a/certora/properties.txt +++ b/certora/properties.txt @@ -8,4 +8,14 @@ 7. Factory - Deterministic Address Calculation for Signers. 8. Factory - Correctness of Signer Creation. (Cant called twice, override) 9. Factory - Signature Validation (isValidSignatureForSigner Integrity) -10. Factory - Code Presence Check (_hasNoCode Integrity) \ No newline at end of file +10. Factory - Code Presence Check (_hasNoCode Integrity) +11. Proxy - Immutability of Configuration Parameters (x, y, Singleton, verifier) +12. Proxy - Delegate Call Integrity (calls the _verifySignature implementation in the Singleton) +13. Proxy - Fallback data corruption (uses data appending that needed to be verified) +14. Proxy - verify return data from Delegatecall. +15. Singleton - Implementation of _verifySignature Function (Integrity) +16. Singleton - getConfiguration Function (Integrity) +17. WebAuthn - castSignature Integrity +18. WebAuthn - encodeClientDataJson Integrity +19. WebAuthn - encodeSigningMessage Integrity +20. WebAuthn - verifySignature Integrity \ No newline at end of file From d1327934f23e2fe3c955ee95d95fb6a419826c4c Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Wed, 15 May 2024 19:02:09 +0300 Subject: [PATCH 009/103] Add property list --- certora/properties.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/certora/properties.txt b/certora/properties.txt index 3a1a7d116..4e4c790a4 100644 --- a/certora/properties.txt +++ b/certora/properties.txt @@ -18,4 +18,5 @@ 17. WebAuthn - castSignature Integrity 18. WebAuthn - encodeClientDataJson Integrity 19. WebAuthn - encodeSigningMessage Integrity -20. WebAuthn - verifySignature Integrity \ No newline at end of file +20. WebAuthn - verifySignature Integrity +21. DDOS on verifySignature \ No newline at end of file From dc842ad3173dd0440a71995d63825b7f4a885977 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Thu, 16 May 2024 16:38:09 +0300 Subject: [PATCH 010/103] Add property list --- certora/properties.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/certora/properties.txt b/certora/properties.txt index 4e4c790a4..1a6ea311a 100644 --- a/certora/properties.txt +++ b/certora/properties.txt @@ -13,6 +13,7 @@ 12. Proxy - Delegate Call Integrity (calls the _verifySignature implementation in the Singleton) 13. Proxy - Fallback data corruption (uses data appending that needed to be verified) 14. Proxy - verify return data from Delegatecall. +15. Proxy - No buffer overflow when appending Parameters. 15. Singleton - Implementation of _verifySignature Function (Integrity) 16. Singleton - getConfiguration Function (Integrity) 17. WebAuthn - castSignature Integrity From f247607ba4e4e325032e4d2cb9a0b38b0ecf9abc Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Thu, 16 May 2024 17:41:25 +0300 Subject: [PATCH 011/103] Add property list --- certora/properties.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certora/properties.txt b/certora/properties.txt index 1a6ea311a..83855eb21 100644 --- a/certora/properties.txt +++ b/certora/properties.txt @@ -13,7 +13,7 @@ 12. Proxy - Delegate Call Integrity (calls the _verifySignature implementation in the Singleton) 13. Proxy - Fallback data corruption (uses data appending that needed to be verified) 14. Proxy - verify return data from Delegatecall. -15. Proxy - No buffer overflow when appending Parameters. +15. Proxy - No buffer overflow when appending Parameters. //(Maybe for Dravee) 15. Singleton - Implementation of _verifySignature Function (Integrity) 16. Singleton - getConfiguration Function (Integrity) 17. WebAuthn - castSignature Integrity From 5aa3bac4a7ce6ba01e404d0229c6fc088e43c0dd Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Thu, 16 May 2024 17:45:09 +0300 Subject: [PATCH 012/103] Add property list --- certora/properties.txt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/certora/properties.txt b/certora/properties.txt index 83855eb21..35e592a94 100644 --- a/certora/properties.txt +++ b/certora/properties.txt @@ -15,9 +15,10 @@ 14. Proxy - verify return data from Delegatecall. 15. Proxy - No buffer overflow when appending Parameters. //(Maybe for Dravee) 15. Singleton - Implementation of _verifySignature Function (Integrity) -16. Singleton - getConfiguration Function (Integrity) -17. WebAuthn - castSignature Integrity -18. WebAuthn - encodeClientDataJson Integrity -19. WebAuthn - encodeSigningMessage Integrity -20. WebAuthn - verifySignature Integrity -21. DDOS on verifySignature \ No newline at end of file +16. Singleton - getConfiguration Function (Integrity). +17. Singleton - Both is valid Signature behave the same way. +18. WebAuthn - castSignature Integrity +19. WebAuthn - encodeClientDataJson Integrity +20. WebAuthn - encodeSigningMessage Integrity +21. WebAuthn - verifySignature Integrity +22. DDOS on verifySignature \ No newline at end of file From 39e51598dadc0b213cec49c1a4d127a99ce40be2 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Thu, 16 May 2024 17:47:17 +0300 Subject: [PATCH 013/103] Add property list --- certora/properties.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/certora/properties.txt b/certora/properties.txt index 35e592a94..492a5037e 100644 --- a/certora/properties.txt +++ b/certora/properties.txt @@ -21,4 +21,5 @@ 19. WebAuthn - encodeClientDataJson Integrity 20. WebAuthn - encodeSigningMessage Integrity 21. WebAuthn - verifySignature Integrity -22. DDOS on verifySignature \ No newline at end of file +22. WebAuthn - Both verifySignature behave the same way. +23. DDOS on verifySignature. \ No newline at end of file From f50532ff4db56857190cf966f518adb1b7c7317c Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Thu, 16 May 2024 17:51:56 +0300 Subject: [PATCH 014/103] Add property list --- certora/properties.txt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/certora/properties.txt b/certora/properties.txt index 492a5037e..aa7abb72d 100644 --- a/certora/properties.txt +++ b/certora/properties.txt @@ -17,9 +17,10 @@ 15. Singleton - Implementation of _verifySignature Function (Integrity) 16. Singleton - getConfiguration Function (Integrity). 17. Singleton - Both is valid Signature behave the same way. -18. WebAuthn - castSignature Integrity -19. WebAuthn - encodeClientDataJson Integrity -20. WebAuthn - encodeSigningMessage Integrity -21. WebAuthn - verifySignature Integrity -22. WebAuthn - Both verifySignature behave the same way. -23. DDOS on verifySignature. \ No newline at end of file +19. Singleton - Once signer passed is isValidSignaure it will never fail on it after any call. +20. WebAuthn - castSignature Integrity +21. WebAuthn - encodeClientDataJson Integrity +22. WebAuthn - encodeSigningMessage Integrity +23. WebAuthn - verifySignature Integrity +24. WebAuthn - Both verifySignature behave the same way. +25. DDOS on verifySignature. \ No newline at end of file From 896c24b6c7c63a4e6b9d479436c5ca2decec54bd Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Thu, 16 May 2024 17:56:02 +0300 Subject: [PATCH 015/103] Add property list --- certora/properties.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/certora/properties.txt b/certora/properties.txt index aa7abb72d..e8f2a674f 100644 --- a/certora/properties.txt +++ b/certora/properties.txt @@ -23,4 +23,8 @@ 22. WebAuthn - encodeSigningMessage Integrity 23. WebAuthn - verifySignature Integrity 24. WebAuthn - Both verifySignature behave the same way. -25. DDOS on verifySignature. \ No newline at end of file +25. DDOS on verifySignature. + +// Optional +Checks for Revert condition on critical Functions (isValidSignaure, + verifySignature, Proxy Fallback, createSigner, getSigner) \ No newline at end of file From 64a60831ab0029ecf3a6ad7ed09bf3cf8417be2c Mon Sep 17 00:00:00 2001 From: yoavelmalem Date: Sun, 19 May 2024 12:48:10 +0300 Subject: [PATCH 016/103] Updated properties and priorities. --- certora/properties.txt | 59 ++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/certora/properties.txt b/certora/properties.txt index e8f2a674f..6fa8941ca 100644 --- a/certora/properties.txt +++ b/certora/properties.txt @@ -1,30 +1,33 @@ -1. Factory - isValidSignaure will return the same as create signer -2. Integrity for Proxy <- this is not a standard implementation -3. WebAuthn - encodeSigningMessage compute presage which needs to get hashed - Given data should be able to to produce only 1 valid message. -4. Factory - Immutability of Singleton Contract. -5. Factory - getSigner is unique for every x,y and verifier combination (need to make sure it is required) -6. Factory - createSigner and getSigner always returns the same address. -7. Factory - Deterministic Address Calculation for Signers. -8. Factory - Correctness of Signer Creation. (Cant called twice, override) -9. Factory - Signature Validation (isValidSignatureForSigner Integrity) -10. Factory - Code Presence Check (_hasNoCode Integrity) -11. Proxy - Immutability of Configuration Parameters (x, y, Singleton, verifier) -12. Proxy - Delegate Call Integrity (calls the _verifySignature implementation in the Singleton) -13. Proxy - Fallback data corruption (uses data appending that needed to be verified) -14. Proxy - verify return data from Delegatecall. -15. Proxy - No buffer overflow when appending Parameters. //(Maybe for Dravee) -15. Singleton - Implementation of _verifySignature Function (Integrity) -16. Singleton - getConfiguration Function (Integrity). -17. Singleton - Both is valid Signature behave the same way. -19. Singleton - Once signer passed is isValidSignaure it will never fail on it after any call. -20. WebAuthn - castSignature Integrity -21. WebAuthn - encodeClientDataJson Integrity -22. WebAuthn - encodeSigningMessage Integrity -23. WebAuthn - verifySignature Integrity -24. WebAuthn - Both verifySignature behave the same way. -25. DDOS on verifySignature. +1. Factory - isValidSignatureForSigner() should return the same result as createSigner() [Duplication] +2. Proxy - Integrity, this is not a standard implementation [Duplication] +4. Factory - Immutability of Singleton Contract. [Critical] +5. Factory - getSigner is unique for every x,y and verifier combination (need to make sure it is required) [High] +6. Factory - createSigner and getSigner always returns the same address. [Medium] +7. Factory - Deterministic Address Calculation for Signers. [High] +8. Factory - Correctness of Signer Creation. (Cant called twice, override) [Cannot understand the risk] +9. Factory - Signature Validation (isValidSignatureForSigner Integrity) [Critical] +10. Factory - Code Presence Check (_hasNoCode Integrity) [Deprecated - internal code] +11. Proxy - Immutability of Configuration Parameters (x, y, Singleton, verifier) [Critical] +12. Proxy - Delegate Call Integrity (calls the _verifySignature implementation in the Singleton) [Low] +13. Proxy - Fallback data corruption (uses data appending that needed to be verified) [Low] +14. Proxy - verify return data from Delegate call. [Low] +15. Proxy - No buffer overflow when appending Parameters. //(Maybe for Dravee) [Low] +15. Singleton - Implementation of _verifySignature Function (Integrity) [High/Medium] +16. Singleton - getConfiguration Function (Integrity). [High/Medium] +17. Singleton - Both is valid Signature behave the same way. [Low] +19. Singleton - Once signer passed isValidSignature it will never fail on it after any call. [Low] +20. Singleton - Once isValidSignature failed, it will never pass before createSigner called. [High] +21. WebAuthn - castSignature Integrity [Low/Medium] +22. WebAuthn - encodeClientDataJson Integrity [Medium] +23. WebAuthn - encodeSigningMessage Integrity [Medium] +24. WebAuthn - verifySignature Integrity [Medium] +25. WebAuthn - Both verifySignature behave the same way. [Low] +3. WebAuthn - given input in encodeSigningMessage(), one should produce only one valid output. [Medium - Maybe duplication] + i.e., one cannot reuse a signature valid for one challenge to be valid for another challenge // Optional -Checks for Revert condition on critical Functions (isValidSignaure, - verifySignature, Proxy Fallback, createSigner, getSigner) \ No newline at end of file +Checks for Revert condition on critical Functions (isValidSignature, + verifySignature, Proxy Fallback, createSigner, getSigner) [Low] + + // Open Question for Dravee: + What is the user protection against hacks? \ No newline at end of file From 20f659b6701ea71cc6260400ddea0db8f0f1cbbd Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Sun, 19 May 2024 14:23:20 +0300 Subject: [PATCH 017/103] . --- certora/properties.txt | 48 ++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/certora/properties.txt b/certora/properties.txt index 6fa8941ca..56b63c666 100644 --- a/certora/properties.txt +++ b/certora/properties.txt @@ -1,28 +1,26 @@ -1. Factory - isValidSignatureForSigner() should return the same result as createSigner() [Duplication] -2. Proxy - Integrity, this is not a standard implementation [Duplication] -4. Factory - Immutability of Singleton Contract. [Critical] -5. Factory - getSigner is unique for every x,y and verifier combination (need to make sure it is required) [High] -6. Factory - createSigner and getSigner always returns the same address. [Medium] -7. Factory - Deterministic Address Calculation for Signers. [High] -8. Factory - Correctness of Signer Creation. (Cant called twice, override) [Cannot understand the risk] -9. Factory - Signature Validation (isValidSignatureForSigner Integrity) [Critical] -10. Factory - Code Presence Check (_hasNoCode Integrity) [Deprecated - internal code] -11. Proxy - Immutability of Configuration Parameters (x, y, Singleton, verifier) [Critical] -12. Proxy - Delegate Call Integrity (calls the _verifySignature implementation in the Singleton) [Low] -13. Proxy - Fallback data corruption (uses data appending that needed to be verified) [Low] -14. Proxy - verify return data from Delegate call. [Low] -15. Proxy - No buffer overflow when appending Parameters. //(Maybe for Dravee) [Low] -15. Singleton - Implementation of _verifySignature Function (Integrity) [High/Medium] -16. Singleton - getConfiguration Function (Integrity). [High/Medium] -17. Singleton - Both is valid Signature behave the same way. [Low] -19. Singleton - Once signer passed isValidSignature it will never fail on it after any call. [Low] -20. Singleton - Once isValidSignature failed, it will never pass before createSigner called. [High] -21. WebAuthn - castSignature Integrity [Low/Medium] -22. WebAuthn - encodeClientDataJson Integrity [Medium] -23. WebAuthn - encodeSigningMessage Integrity [Medium] -24. WebAuthn - verifySignature Integrity [Medium] -25. WebAuthn - Both verifySignature behave the same way. [Low] -3. WebAuthn - given input in encodeSigningMessage(), one should produce only one valid output. [Medium - Maybe duplication] +Factory - Immutability of Singleton Contract. [Critical] +Factory - getSigner is unique for every x,y and verifier combination (need to make sure it is required) [High] +Factory - createSigner and getSigner always returns the same address. [Medium] +Factory - Deterministic Address Calculation for Signers. [High] +Factory - Correctness of Signer Creation. (Cant called twice, override) [Cannot understand the risk] +Factory - Signature Validation (isValidSignatureForSigner Integrity) [Critical] +Factory - Code Presence Check (_hasNoCode Integrity) [Deprecated - internal code] +Proxy - Immutability of Configuration Parameters (x, y, Singleton, verifier) [Critical] +Proxy - Delegate Call Integrity (calls the _verifySignature implementation in the Singleton) [Low] +Proxy - Fallback data corruption (uses data appending that needed to be verified) [Low] +Proxy - verify return data from Delegate call. [Low] +Proxy - No buffer overflow when appending Parameters. //(Maybe for Dravee) [Low] +Singleton - Implementation of _verifySignature Function (Integrity) [High/Medium] +Singleton - getConfiguration Function (Integrity). [High/Medium] +Singleton - Both is valid Signature behave the same way. [Low] +Singleton - Once signer passed isValidSignature it will never fail on it after any call. [Low] +Singleton - Once isValidSignature failed, it will never pass before createSigner called. [High] +WebAuthn - castSignature Integrity [Low/Medium] +WebAuthn - encodeClientDataJson Integrity [Medium] +WebAuthn - encodeSigningMessage Integrity [Medium] +WebAuthn - verifySignature Integrity [Medium] +WebAuthn - Both verifySignature behave the same way. [Low] +WebAuthn - given input in encodeSigningMessage(), one should produce only one valid output. [Medium - Maybe duplication] i.e., one cannot reuse a signature valid for one challenge to be valid for another challenge // Optional From 589a626cd79cd17a216291eb2f488d922b7cb24a Mon Sep 17 00:00:00 2001 From: liav-certora Date: Sun, 19 May 2024 16:30:55 +0300 Subject: [PATCH 018/103] Update SafeWebAuthnSignerProxy.spec --- certora/specs/SafeWebAuthnSignerProxy.spec | 26 +++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/certora/specs/SafeWebAuthnSignerProxy.spec b/certora/specs/SafeWebAuthnSignerProxy.spec index 938aba94a..dcff4d932 100644 --- a/certora/specs/SafeWebAuthnSignerProxy.spec +++ b/certora/specs/SafeWebAuthnSignerProxy.spec @@ -20,4 +20,28 @@ use rule privilegedOperation filtered { f -> f.contract == currentContract } use rule timeoutChecker filtered { f -> f.contract == currentContract } use rule simpleFrontRunning filtered { f -> f.contract == currentContract } use rule noRevert filtered { f -> f.contract == currentContract } -use rule alwaysRevert filtered { f -> f.contract == currentContract } \ No newline at end of file +use rule alwaysRevert filtered { f -> f.contract == currentContract } + + +rule configParametersImmutability { + env e; + method f; + calldataarg args; + + address singletonBefore = currentContract._SINGLETON; + uint256 xBefore = currentContract._X; + uint256 yBefore = currentContract._Y; + P256.Verifiers verifiersBefore = currentContract._VERIFIERS; + + f(e, args); + + address singletonAfter = currentContract._SINGLETON; + uint256 xAfter = currentContract._X; + uint256 yAfter = currentContract._Y; + P256.Verifiers verifiersAfter = currentContract._VERIFIERS; + + assert singletonBefore == singletonAfter && + xBefore == xAfter && + yBefore == yAfter && + verifiersBefore == verifiersAfter; +} From 66b5fcb35411213ae532b20751346d1f31f82684 Mon Sep 17 00:00:00 2001 From: liav-certora Date: Mon, 20 May 2024 11:02:34 +0300 Subject: [PATCH 019/103] Update SafeWebAuthnSignerProxy.spec --- certora/specs/SafeWebAuthnSignerProxy.spec | 3 +++ 1 file changed, 3 insertions(+) diff --git a/certora/specs/SafeWebAuthnSignerProxy.spec b/certora/specs/SafeWebAuthnSignerProxy.spec index dcff4d932..3e33ea015 100644 --- a/certora/specs/SafeWebAuthnSignerProxy.spec +++ b/certora/specs/SafeWebAuthnSignerProxy.spec @@ -23,6 +23,9 @@ use rule noRevert filtered { f -> f.contract == currentContract } use rule alwaysRevert filtered { f -> f.contract == currentContract } +/* +Rule: Proxy Configuration Paramateres Never Change -- Passed +*/ rule configParametersImmutability { env e; method f; From 6242d4856b94be345f965e4fac26f081d7da5524 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Mon, 20 May 2024 14:33:33 +0300 Subject: [PATCH 020/103] . --- certora/confs/SafeWebAuthnSignerFactory.conf | 6 +- .../SafeWebAuthnSignerFactoryHarness.sol | 16 +++++ certora/specs/SafeWebAuthnSignerFactory.spec | 60 ++++++++++++++++++- .../contracts/SafeWebAuthnSignerFactory.sol | 1 + 4 files changed, 77 insertions(+), 6 deletions(-) create mode 100644 certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol diff --git a/certora/confs/SafeWebAuthnSignerFactory.conf b/certora/confs/SafeWebAuthnSignerFactory.conf index 5784ee1ed..84f666779 100644 --- a/certora/confs/SafeWebAuthnSignerFactory.conf +++ b/certora/confs/SafeWebAuthnSignerFactory.conf @@ -1,7 +1,7 @@ { "assert_autofinder_success": true, "files": [ - "modules/passkey/contracts/SafeWebAuthnSignerFactory.sol", + "certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol", "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", "certora/harnesses/ERC20Like/DummyWeth.sol", "certora/harnesses/Utilities.sol", @@ -10,7 +10,7 @@ " -ea -Dlevel.setup.helpers=info" ], "link": [ - "SafeWebAuthnSignerFactory:SINGLETON=SafeWebAuthnSignerSingleton" + "SafeWebAuthnSignerFactoryHarness:SINGLETON=SafeWebAuthnSignerSingleton" ], "msg": "sanity_with_all_default_summaries", "process": "emv", @@ -29,5 +29,5 @@ "loop_iter": "144", "prover_version": "master", "server": "production", - "verify": "SafeWebAuthnSignerFactory:certora/specs/SafeWebAuthnSignerFactory.spec" + "verify": "SafeWebAuthnSignerFactoryHarness:certora/specs/SafeWebAuthnSignerFactory.spec" } \ No newline at end of file diff --git a/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol b/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol new file mode 100644 index 000000000..092f1a51b --- /dev/null +++ b/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity >=0.8.0; + +import {SafeWebAuthnSignerFactory} from "../../modules/passkey/contracts/SafeWebAuthnSignerFactory.sol"; +import {P256} from "../../modules/passkey/contracts/libraries/P256.sol"; +import {SafeWebAuthnSignerProxy} from "../../modules/passkey/contracts/SafeWebAuthnSignerProxy.sol"; + + +contract SafeWebAuthnSignerFactoryHarness is SafeWebAuthnSignerFactory { + + //Harness + function hasNoCode(address account) external view returns (bool result) { + // solhint-disable-next-line no-inline-assembly + return SafeWebAuthnSignerFactory._hasNoCode(account); + } +} diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index b55f199b2..678bc2136 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -12,6 +12,7 @@ import "spec_utils/generic.spec"; // pick additional rules from here methods{ function getSigner(uint256, uint256, P256.Verifiers) external returns (address) envfree; function createSigner(uint256, uint256, P256.Verifiers) external returns (address) envfree; + function hasNoCode(address) external returns (bool) envfree; } @@ -26,9 +27,20 @@ use rule noRevert filtered { f -> f.contract == currentContract } use rule alwaysRevert filtered { f -> f.contract == currentContract } +/* +Factory - Immutability of Singleton Contract. (Proved) [Critical] +Factory - getSigner is unique for every x,y and verifier combination (Bug in prover CERT-6182) [High] +Factory - createSigner and getSigner always returns the same address. (Bug in prover CERT-6182) [Medium] +Factory - Deterministic Address Calculation for Signers. [High] +Factory - Correctness of Signer Creation. (Cant called twice, override) [Cannot understand the risk] +Factory - Code Presence Check (_hasNoCode Integrity) [Low] +Factory - Signature Validation (isValidSignatureForSigner Integrity) [Critical] + +*/ + /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ Singleton implementation never change │ +│ Singleton implementation never change (Proved) │ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ rule singletonNeverChanges() @@ -47,7 +59,7 @@ rule singletonNeverChanges() /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ getSigner is unique for every x,y and verifier combination │ +│ getSigner is unique for every x,y and verifier combination (Bug in prover CERT-6182) │ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ @@ -69,7 +81,7 @@ rule uniqueSigner(){ /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ createSigner and getSigner always returns the same address │ +│ createSigner and getSigner always returns the same address (Bug in prover CERT-6182) │ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ @@ -87,4 +99,46 @@ rule createAndGetSignerEquivalence(){ address signer2 = getSigner(getX, getY, getVerifier); assert signer1 == signer2 <=> (createX == getX && createY == getY && createVerifier == getVerifier); +} + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Deterministic address in get signer (Proved) │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ +rule deterministicSigner() +{ + uint x; + uint y; + P256.Verifiers verifier; + + address signer = getSigner(x, y, verifier); + + assert signer == getSigner(x, y, verifier); +} + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Correctness of Signer Creation. (Cant called twice and override) (Proved) │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ + +ghost mathint numOfCreation; + +hook CREATE2(uint value, uint offset, uint length, bytes32 salt) address v{ + require numOfCreation == numOfCreation + 1; +} + +rule SignerCreationCantOverride() +{ + require numOfCreation == 0; + + uint x; + uint y; + P256.Verifiers verifier; + + createSigner(x, y, verifier); + createSigner(x, y, verifier); + + assert numOfCreation < 2; } \ No newline at end of file diff --git a/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol b/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol index 26d9f54c0..a8933d369 100644 --- a/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol +++ b/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol @@ -36,6 +36,7 @@ contract SafeWebAuthnSignerFactory is ISafeSignerFactory { bytes32 codeHash = keccak256( abi.encodePacked( type(SafeWebAuthnSignerProxy).creationCode, + "01234567891011121314152546", // Munging uint256(uint160(address(SINGLETON))), x, y, From 4714c04967ed98b712af206ae8d99edfd0b1a38d Mon Sep 17 00:00:00 2001 From: yoavelmalem Date: Mon, 20 May 2024 17:51:14 +0300 Subject: [PATCH 021/103] Added all properties. getConfiguration Integrity not done yet. --- .../confs/SafeWebAuthnSignerSingleton.conf | 8 +- .../specs/SafeWebAuthnSignerSingleton.spec | 157 +++++++++++++++--- 2 files changed, 138 insertions(+), 27 deletions(-) diff --git a/certora/confs/SafeWebAuthnSignerSingleton.conf b/certora/confs/SafeWebAuthnSignerSingleton.conf index b43b06da5..ccea556a2 100644 --- a/certora/confs/SafeWebAuthnSignerSingleton.conf +++ b/certora/confs/SafeWebAuthnSignerSingleton.conf @@ -1,14 +1,14 @@ { "assert_autofinder_success": true, "files": [ + "modules/passkey/contracts/SafeWebAuthnSignerFactory.sol", "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", - "certora/harnesses/ERC20Like/DummyWeth.sol", - "certora/harnesses/Utilities.sol", + "certora/harnesses/SafeWebAuthnSignerSingletonHarness.sol", ], "java_args": [ " -ea -Dlevel.setup.helpers=info" ], - "msg": "sanity_with_all_default_summaries", + "msg": "", "process": "emv", "prover_args": [ " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" @@ -23,5 +23,5 @@ "optimistic_hashing": true, "prover_version": "master", "server": "production", - "verify": "SafeWebAuthnSignerSingleton:certora/specs/SafeWebAuthnSignerSingleton.spec" + "verify": "SafeWebAuthnSignerSingletonHarness:certora/specs/SafeWebAuthnSignerSingleton.spec" } \ No newline at end of file diff --git a/certora/specs/SafeWebAuthnSignerSingleton.spec b/certora/specs/SafeWebAuthnSignerSingleton.spec index 938aba94a..6f71beea0 100644 --- a/certora/specs/SafeWebAuthnSignerSingleton.spec +++ b/certora/specs/SafeWebAuthnSignerSingleton.spec @@ -1,23 +1,134 @@ -import "ERC20/erc20cvl.spec"; -import "ERC20/WETHcvl.spec"; -import "ERC721/erc721.spec"; -import "ERC1967/erc1967.spec"; -import "PriceAggregators/chainlink.spec"; -import "PriceAggregators/tellor.spec"; - -import "spec_utils/problems.spec"; -import "spec_utils/unresolved.spec"; -import "spec_utils/optimizations.spec"; - -import "spec_utils/generic.spec"; // pick additional rules from here - -use builtin rule sanity filtered { f -> f.contract == currentContract } - -use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } -use builtin rule msgValueInLoopRule; -use builtin rule viewReentrancy; -use rule privilegedOperation filtered { f -> f.contract == currentContract } -use rule timeoutChecker filtered { f -> f.contract == currentContract } -use rule simpleFrontRunning filtered { f -> f.contract == currentContract } -use rule noRevert filtered { f -> f.contract == currentContract } -use rule alwaysRevert filtered { f -> f.contract == currentContract } \ No newline at end of file +// import "ERC20/erc20cvl.spec"; +// import "ERC20/WETHcvl.spec"; +// import "ERC721/erc721.spec"; +// import "ERC1967/erc1967.spec"; +// import "PriceAggregators/chainlink.spec"; +// import "PriceAggregators/tellor.spec"; + +// import "spec_utils/problems.spec"; +// import "spec_utils/unresolved.spec"; +// import "spec_utils/optimizations.spec"; + +// import "spec_utils/generic.spec"; // pick additional rules from here + +// use builtin rule sanity filtered { f -> f.contract == currentContract } + +// use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } +// use builtin rule msgValueInLoopRule; +// use builtin rule viewReentrancy; +// use rule privilegedOperation filtered { f -> f.contract == currentContract } +// use rule timeoutChecker filtered { f -> f.contract == currentContract } +// use rule simpleFrontRunning filtered { f -> f.contract == currentContract } +// use rule noRevert filtered { f -> f.contract == currentContract } +// use rule alwaysRevert filtered { f -> f.contract == currentContract } + + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Implementation of _verifySignature Function (Integrity) │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ + +rule verifySignatureIntegrity(env e){ + bytes32 first_message; + bytes signature; + + bool first_message_verified = verifySignatureHarnessed(e, first_message, signature); + + bytes32 second_message; + bool second_message_verified = verifySignatureHarnessed(e, second_message, signature); + + assert (first_message_verified == true && second_message_verified == true) => first_message == second_message; + assert first_message == second_message => first_message_verified == second_message_verified; +} + + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ getConfiguration Function (Integrity) │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ +// TODO Not Completed Yet +// rule verifyGetConfigurationIntegrity(env e){ + +// uint256 x; +// uint256 y; +// P256.Verifiers verifiers; +// uint256 new_x; uint256 new_y; P256.Verifiers new_verifiers; +// bytes32 message; + +// // bytes data = assignValues(e, x, y, verifiers); +// (new_x, new_y, new_verifiers) = temp(e, x, y, verifiers); + +// // (x, y, verifiers) = getConfiguration(e); +// // (new_x, new_y, new_verifiers) = getConfigurationHarnessed(e, data); + +// assert x == new_x; +// assert y == new_y; +// assert verifiers == new_verifiers; +// satisfy true; +// } + + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Both is valid Signature behave the same way │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ + +rule verifyIsValidSignatureAreEqual(env e){ + bytes data; + bytes first_signature; + + bool hashed_data_verified = verifySignatureHarnessed(e, keccak256(data), first_signature); + + bytes second_signature; + bytes32 message; + bool message_verified = verifySignatureHarnessed(e, message, second_signature); + + assert (hashed_data_verified == true && message_verified == true) => message == keccak256(data); + assert message == keccak256(data) => hashed_data_verified == message_verified; +} + + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Once signer passed isValidSignature it will never fail on it after any call │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ + +rule verifyIsValidSignatureWillContinueToSucceed(env e){ + bytes32 message; + bytes signature; + + bool first_verified = verifySignatureHarnessed(e, message, signature); + require first_verified == true; + + bool second_verified = verifySignatureHarnessed(e, message, signature); + assert second_verified; +} + + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Once isValidSignature failed, it will never pass before createSigner called │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ + +rule IsValidSignatureMustSucceedAfterCreation(env e){ + method f; + calldataarg args; + + bytes32 message; + bytes signature; + + bool first_verified = verifySignatureHarnessed(e, message, signature); + require !first_verified; + + f(e, args); + + + bool second_verified = verifySignatureHarnessed(e, message, signature); + assert second_verified => f.selector == sig:SafeWebAuthnSignerFactory.createSigner(uint256, uint256, P256.Verifiers).selector; +} + From c9d1532ee387dbf16ceb8f37914115ab147a6023 Mon Sep 17 00:00:00 2001 From: yoavelmalem Date: Tue, 21 May 2024 11:04:45 +0300 Subject: [PATCH 022/103] Added the harness file --- .../SafeWebAuthnSignerSingletonHarness.sol | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 certora/harnesses/SafeWebAuthnSignerSingletonHarness.sol diff --git a/certora/harnesses/SafeWebAuthnSignerSingletonHarness.sol b/certora/harnesses/SafeWebAuthnSignerSingletonHarness.sol new file mode 100644 index 000000000..b47960248 --- /dev/null +++ b/certora/harnesses/SafeWebAuthnSignerSingletonHarness.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity >=0.8.0; + +import {SafeWebAuthnSignerSingleton} from "../../modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol"; +import {SignatureValidator} from "../../modules/passkey/contracts/base/SignatureValidator.sol"; +import {P256, WebAuthn} from "../../modules/passkey/contracts/libraries/WebAuthn.sol"; + +/** + * @title Safe WebAuthn Signer Singleton + * @dev A singleton contract that implements WebAuthn signature verification. This singleton + * contract must be used with the specialized proxy {SafeWebAuthnSignerProxy}, as it encodes the + * credential configuration (public key coordinates and P-256 verifier to use) in calldata, which is + * required by this implementation. + * @custom:security-contact bounty@safe.global + */ +contract SafeWebAuthnSignerSingletonHarness is SafeWebAuthnSignerSingleton { + + function verifySignatureHarnessed(bytes32 message, bytes calldata signature) public view virtual returns (bool success) { + (uint256 x, uint256 y, P256.Verifiers verifiers) = getConfiguration(); + success = WebAuthn.verifySignature(message, signature, WebAuthn.USER_VERIFICATION, x, y, verifiers); + } +} From 958f5085d4b67f9f17fb9ee31877ebc988480dd6 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Tue, 21 May 2024 11:50:23 +0300 Subject: [PATCH 023/103] . --- certora/confs/SafeWebAuthnSignerFactory.conf | 1 + .../SafeWebAuthnSignerFactoryHarness.sol | 18 +++++- certora/specs/SafeWebAuthnSignerFactory.spec | 57 ++++++++++++++----- 3 files changed, 62 insertions(+), 14 deletions(-) diff --git a/certora/confs/SafeWebAuthnSignerFactory.conf b/certora/confs/SafeWebAuthnSignerFactory.conf index 84f666779..d09b1225a 100644 --- a/certora/confs/SafeWebAuthnSignerFactory.conf +++ b/certora/confs/SafeWebAuthnSignerFactory.conf @@ -3,6 +3,7 @@ "files": [ "certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol", "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", + "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol", "certora/harnesses/ERC20Like/DummyWeth.sol", "certora/harnesses/Utilities.sol", ], diff --git a/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol b/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol index 092f1a51b..8c9fd02f2 100644 --- a/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol +++ b/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol @@ -5,7 +5,6 @@ import {SafeWebAuthnSignerFactory} from "../../modules/passkey/contracts/SafeWeb import {P256} from "../../modules/passkey/contracts/libraries/P256.sol"; import {SafeWebAuthnSignerProxy} from "../../modules/passkey/contracts/SafeWebAuthnSignerProxy.sol"; - contract SafeWebAuthnSignerFactoryHarness is SafeWebAuthnSignerFactory { //Harness @@ -13,4 +12,21 @@ contract SafeWebAuthnSignerFactoryHarness is SafeWebAuthnSignerFactory { // solhint-disable-next-line no-inline-assembly return SafeWebAuthnSignerFactory._hasNoCode(account); } + + function createAndVerify( + bytes32 message, + bytes calldata signature, + uint256 x, + uint256 y, + P256.Verifiers verifiers + ) external returns (bytes4 magicValue) { + address signer = this.createSigner(x, y, verifiers); + + bytes memory data = abi.encodeWithSignature("isValidSignature(bytes32,bytes)", message, signature); + + // Use low-level call to invoke isValidSignature on the signer address + (bool success, bytes memory result) = signer.staticcall(data); + require(success); + magicValue = abi.decode(result, (bytes4)); + } } diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index 678bc2136..62c13ea43 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -9,10 +9,17 @@ import "spec_utils/unresolved.spec"; import "spec_utils/optimizations.spec"; import "spec_utils/generic.spec"; // pick additional rules from here +using SafeWebAuthnSignerProxy as proxy; +using SafeWebAuthnSignerSingleton as singleton; + methods{ function getSigner(uint256, uint256, P256.Verifiers) external returns (address) envfree; function createSigner(uint256, uint256, P256.Verifiers) external returns (address) envfree; function hasNoCode(address) external returns (bool) envfree; + function _._ external => DISPATCH [ + proxy._, + singleton.isValidSignature(bytes32, bytes) + ] default NONDET; } @@ -27,17 +34,6 @@ use rule noRevert filtered { f -> f.contract == currentContract } use rule alwaysRevert filtered { f -> f.contract == currentContract } -/* -Factory - Immutability of Singleton Contract. (Proved) [Critical] -Factory - getSigner is unique for every x,y and verifier combination (Bug in prover CERT-6182) [High] -Factory - createSigner and getSigner always returns the same address. (Bug in prover CERT-6182) [Medium] -Factory - Deterministic Address Calculation for Signers. [High] -Factory - Correctness of Signer Creation. (Cant called twice, override) [Cannot understand the risk] -Factory - Code Presence Check (_hasNoCode Integrity) [Low] -Factory - Signature Validation (isValidSignatureForSigner Integrity) [Critical] - -*/ - /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Singleton implementation never change (Proved) │ @@ -59,7 +55,7 @@ rule singletonNeverChanges() /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ getSigner is unique for every x,y and verifier combination (Bug in prover CERT-6182) │ +│ getSigner is unique for every x,y and verifier combination (Violated but low prob) │ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ @@ -81,7 +77,7 @@ rule uniqueSigner(){ /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ createSigner and getSigner always returns the same address (Bug in prover CERT-6182) │ +│ createSigner and getSigner always returns the same address (Violated but low prob) │ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ @@ -141,4 +137,39 @@ rule SignerCreationCantOverride() createSigner(x, y, verifier); assert numOfCreation < 2; +} + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Has no code integrity (Proved) │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ +rule hasNoCodeIntegrity() +{ + address a; + assert (a == proxy) => !hasNoCode(a); +} + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ isValidSignatureForSigner equiv to first deploying the signer with the factory, and then | +| verifying the signature with it directly (CERT-6221) │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ +rule createAndVerifyEQtoIsValidSignatureForSigner() +{ + env e; + uint x; + uint y; + P256.Verifiers verifier; + bytes signature; + bytes32 message; + + storage s = lastStorage; + + bytes4 magic1 = isValidSignatureForSigner(e, message, signature, x, y, verifier); + + bytes4 magic2 = createAndVerify(e, message, signature, x, y, verifier) at s; + + assert magic1 == magic2; } \ No newline at end of file From db36204bd314c1cb5b5550b9f131a87690a0862c Mon Sep 17 00:00:00 2001 From: yoavelmalem Date: Tue, 21 May 2024 12:07:46 +0300 Subject: [PATCH 024/103] Removed commented code. --- .../specs/SafeWebAuthnSignerSingleton.spec | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/certora/specs/SafeWebAuthnSignerSingleton.spec b/certora/specs/SafeWebAuthnSignerSingleton.spec index 6f71beea0..54cb25549 100644 --- a/certora/specs/SafeWebAuthnSignerSingleton.spec +++ b/certora/specs/SafeWebAuthnSignerSingleton.spec @@ -1,27 +1,3 @@ -// import "ERC20/erc20cvl.spec"; -// import "ERC20/WETHcvl.spec"; -// import "ERC721/erc721.spec"; -// import "ERC1967/erc1967.spec"; -// import "PriceAggregators/chainlink.spec"; -// import "PriceAggregators/tellor.spec"; - -// import "spec_utils/problems.spec"; -// import "spec_utils/unresolved.spec"; -// import "spec_utils/optimizations.spec"; - -// import "spec_utils/generic.spec"; // pick additional rules from here - -// use builtin rule sanity filtered { f -> f.contract == currentContract } - -// use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } -// use builtin rule msgValueInLoopRule; -// use builtin rule viewReentrancy; -// use rule privilegedOperation filtered { f -> f.contract == currentContract } -// use rule timeoutChecker filtered { f -> f.contract == currentContract } -// use rule simpleFrontRunning filtered { f -> f.contract == currentContract } -// use rule noRevert filtered { f -> f.contract == currentContract } -// use rule alwaysRevert filtered { f -> f.contract == currentContract } - /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ From 8dd8ec39e58f3a660c29ab4abbe03f6068460b94 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Tue, 21 May 2024 15:54:18 +0300 Subject: [PATCH 025/103] . --- certora/specs/SafeWebAuthnSignerFactory.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index 62c13ea43..0f376cb3c 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -115,14 +115,14 @@ rule deterministicSigner() /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ Correctness of Signer Creation. (Cant called twice and override) (Proved) │ +│ Correctness of Signer Creation. (Cant called twice and override) (Violated) │ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ ghost mathint numOfCreation; hook CREATE2(uint value, uint offset, uint length, bytes32 salt) address v{ - require numOfCreation == numOfCreation + 1; + numOfCreation = numOfCreation + 1; } rule SignerCreationCantOverride() From 6dbea479ecbec4e9fcf91631db28658af1cccab5 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Tue, 21 May 2024 15:57:05 +0300 Subject: [PATCH 026/103] Clean munging --- certora/specs/SafeWebAuthnSignerFactory.spec | 1 + modules/passkey/contracts/SafeWebAuthnSignerFactory.sol | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index 0f376cb3c..7f89e1f9e 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -58,6 +58,7 @@ rule singletonNeverChanges() │ getSigner is unique for every x,y and verifier combination (Violated but low prob) │ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ +// consider adding the following munging after the creationcode to get a more clear dump 01234567891011121314152546 rule uniqueSigner(){ uint256 firstX; diff --git a/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol b/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol index a8933d369..26d9f54c0 100644 --- a/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol +++ b/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol @@ -36,7 +36,6 @@ contract SafeWebAuthnSignerFactory is ISafeSignerFactory { bytes32 codeHash = keccak256( abi.encodePacked( type(SafeWebAuthnSignerProxy).creationCode, - "01234567891011121314152546", // Munging uint256(uint160(address(SINGLETON))), x, y, From 985860891346e1734d913042d2a924d8def775b3 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Tue, 21 May 2024 17:37:59 +0300 Subject: [PATCH 027/103] Add sanity basic --- certora/confs/SafeWebAuthnSignerFactory.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/certora/confs/SafeWebAuthnSignerFactory.conf b/certora/confs/SafeWebAuthnSignerFactory.conf index d09b1225a..603c81b88 100644 --- a/certora/confs/SafeWebAuthnSignerFactory.conf +++ b/certora/confs/SafeWebAuthnSignerFactory.conf @@ -22,6 +22,7 @@ "@safe-global=node_modules/@safe-global", "@account-abstraction=node_modules/@account-abstraction" ], + "rule_sanity": "basic", "solc": "solc8.23", "solc_via_ir": false, "optimistic_loop": true, From a8f1793c4f554bd197025c7605242c4d7bff13a4 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Wed, 22 May 2024 10:20:07 +0300 Subject: [PATCH 028/103] Comment out --- certora/specs/SafeWebAuthnSignerFactory.spec | 26 ++++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index 7f89e1f9e..1925e8715 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -16,22 +16,22 @@ methods{ function getSigner(uint256, uint256, P256.Verifiers) external returns (address) envfree; function createSigner(uint256, uint256, P256.Verifiers) external returns (address) envfree; function hasNoCode(address) external returns (bool) envfree; - function _._ external => DISPATCH [ - proxy._, - singleton.isValidSignature(bytes32, bytes) - ] default NONDET; +// function _._ external => DISPATCH [ +// proxy._, +// singleton.isValidSignature(bytes32, bytes) +// ] default NONDET; } -use builtin rule sanity filtered { f -> f.contract == currentContract } -use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } -use builtin rule msgValueInLoopRule; -use builtin rule viewReentrancy; -use rule privilegedOperation filtered { f -> f.contract == currentContract } -use rule timeoutChecker filtered { f -> f.contract == currentContract } -use rule simpleFrontRunning filtered { f -> f.contract == currentContract } -use rule noRevert filtered { f -> f.contract == currentContract } -use rule alwaysRevert filtered { f -> f.contract == currentContract } +// use builtin rule sanity filtered { f -> f.contract == currentContract } +// use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } +// use builtin rule msgValueInLoopRule; +// use builtin rule viewReentrancy; +// use rule privilegedOperation filtered { f -> f.contract == currentContract } +// use rule timeoutChecker filtered { f -> f.contract == currentContract } +// use rule simpleFrontRunning filtered { f -> f.contract == currentContract } +// use rule noRevert filtered { f -> f.contract == currentContract } +// use rule alwaysRevert filtered { f -> f.contract == currentContract } /* From 657f41328d80a5a6a9060097c2fb8a5a690cf31f Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Wed, 22 May 2024 11:36:18 +0300 Subject: [PATCH 029/103] Try to fix --- certora/specs/SafeWebAuthnSignerFactory.spec | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index 1925e8715..da77aeebe 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -121,9 +121,15 @@ rule deterministicSigner() */ ghost mathint numOfCreation; +ghost mapping(address => uint) address_map; + +hook EXTCODESIZE(address addr) uint v{ + require address_map[addr] == v; +} hook CREATE2(uint value, uint offset, uint length, bytes32 salt) address v{ numOfCreation = numOfCreation + 1; + address_map[v] = length; } rule SignerCreationCantOverride() @@ -134,6 +140,9 @@ rule SignerCreationCantOverride() uint y; P256.Verifiers verifier; + address a = getSigner(x, y, verifier); + require address_map[a] == 0; + createSigner(x, y, verifier); createSigner(x, y, verifier); From 4ad1bf0cfd0aaf3401401f8b4c72abbdb8cfd614 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Wed, 22 May 2024 13:55:18 +0300 Subject: [PATCH 030/103] Try to fix --- certora/specs/SafeWebAuthnSignerFactory.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index da77aeebe..d1282e5f4 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -124,7 +124,7 @@ ghost mathint numOfCreation; ghost mapping(address => uint) address_map; hook EXTCODESIZE(address addr) uint v{ - require address_map[addr] == v; + require address_map[addr] == v && addr <= max_uint160; } hook CREATE2(uint value, uint offset, uint length, bytes32 salt) address v{ From 761f58b6b3d67b548e1b761d63973f5371cd354c Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Thu, 23 May 2024 14:05:49 +0300 Subject: [PATCH 031/103] Add ticket --- certora/confs/SafeWebAuthnSignerFactory.conf | 3 --- certora/specs/SafeWebAuthnSignerFactory.spec | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/certora/confs/SafeWebAuthnSignerFactory.conf b/certora/confs/SafeWebAuthnSignerFactory.conf index 603c81b88..6dfe5b5f5 100644 --- a/certora/confs/SafeWebAuthnSignerFactory.conf +++ b/certora/confs/SafeWebAuthnSignerFactory.conf @@ -15,9 +15,6 @@ ], "msg": "sanity_with_all_default_summaries", "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], "packages":[ "@safe-global=node_modules/@safe-global", "@account-abstraction=node_modules/@account-abstraction" diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index d1282e5f4..354c89ee7 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -116,7 +116,7 @@ rule deterministicSigner() /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ Correctness of Signer Creation. (Cant called twice and override) (Violated) │ +│ Correctness of Signer Creation. (Cant called twice and override) (Bug CERT-6252) │ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ @@ -144,7 +144,7 @@ rule SignerCreationCantOverride() require address_map[a] == 0; createSigner(x, y, verifier); - createSigner(x, y, verifier); + createSigner@withrevert(x, y, verifier); assert numOfCreation < 2; } From 24cf1df194ab50063fe1310b08357b7fbb86e973 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Thu, 23 May 2024 22:28:06 +0300 Subject: [PATCH 032/103] For nurit --- certora/confs/WebAuthn.conf | 5 +- certora/harnesses/WebAuthnHarness.sol | 7 ++- certora/specs/SafeWebAuthnSignerFactory.spec | 47 ++++++++++++++- certora/specs/WebAuthn.spec | 57 +++++++++++++++---- .../passkey/contracts/libraries/WebAuthn.sol | 2 +- 5 files changed, 102 insertions(+), 16 deletions(-) diff --git a/certora/confs/WebAuthn.conf b/certora/confs/WebAuthn.conf index d69fc6128..ab59de055 100644 --- a/certora/confs/WebAuthn.conf +++ b/certora/confs/WebAuthn.conf @@ -10,9 +10,6 @@ ], "msg": "sanity_with_all_default_summaries", "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], "packages":[ "@safe-global=node_modules/@safe-global", "@account-abstraction=node_modules/@account-abstraction" @@ -23,5 +20,7 @@ "optimistic_hashing": true, "prover_version": "master", "server": "production", + "rule_sanity": "basic", + "loop_iter": "6", "verify": "WebAuthnHarness:certora/specs/WebAuthn.spec" } \ No newline at end of file diff --git a/certora/harnesses/WebAuthnHarness.sol b/certora/harnesses/WebAuthnHarness.sol index e214cdfb4..3635cec05 100644 --- a/certora/harnesses/WebAuthnHarness.sol +++ b/certora/harnesses/WebAuthnHarness.sol @@ -5,7 +5,7 @@ import {P256, WebAuthn} from "../../modules/passkey/contracts/libraries/WebAuthn contract WebAuthnHarness { - function castSignature(bytes calldata signature) internal pure returns (bool isValid, WebAuthn.Signature calldata data){ + function castSignature(bytes calldata signature) external pure returns (bool isValid, WebAuthn.Signature calldata data){ return WebAuthn.castSignature(signature); } @@ -52,5 +52,10 @@ contract WebAuthnHarness { ) public view returns (bool success) { return WebAuthn.verifySignature(challenge, signature, authenticatorFlags, x, y, verifiers); } + + function getSha256(bytes32 input) public view returns (bytes32 digest) { + bytes memory m = abi.encodePacked(input); + return WebAuthn._sha256(m); + } } diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index 354c89ee7..87cd96cac 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -22,6 +22,8 @@ methods{ // ] default NONDET; } +definition MAGIC_VALUE() returns bytes4 = to_bytes4(0x1626ba7e); + // use builtin rule sanity filtered { f -> f.contract == currentContract } // use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } @@ -122,9 +124,11 @@ rule deterministicSigner() ghost mathint numOfCreation; ghost mapping(address => uint) address_map; +ghost bool validValue; hook EXTCODESIZE(address addr) uint v{ - require address_map[addr] == v && addr <= max_uint160; + require address_map[addr] == v; + validValue = addr <= max_uint160; } hook CREATE2(uint value, uint offset, uint length, bytes32 salt) address v{ @@ -149,6 +153,20 @@ rule SignerCreationCantOverride() assert numOfCreation < 2; } +rule ValidValue() +{ + require !validValue; + + uint x; + uint y; + P256.Verifiers verifier; + + createSigner(x, y, verifier); + createSigner@withrevert(x, y, verifier); + + satisfy validValue; +} + /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Has no code integrity (Proved) │ @@ -182,4 +200,31 @@ rule createAndVerifyEQtoIsValidSignatureForSigner() bytes4 magic2 = createAndVerify(e, message, signature, x, y, verifier) at s; assert magic1 == magic2; +} + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ isValidSignatureForSigner Consistency │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ +rule isValidSignatureForSignerConsistency() +{ + env e; + method f; + calldataarg args; + + uint x; + uint y; + P256.Verifiers verifier; + + bytes signature; + bytes32 message; + + bytes4 magic1 = isValidSignatureForSigner(e, message, signature, x, y, verifier); + + f(e, args); + + bytes4 magic2 = isValidSignatureForSigner(e, message, signature, x, y, verifier); + + assert (magic1 == MAGIC_VALUE()) <=> (magic2 == MAGIC_VALUE()); } \ No newline at end of file diff --git a/certora/specs/WebAuthn.spec b/certora/specs/WebAuthn.spec index 938aba94a..f8915d8f0 100644 --- a/certora/specs/WebAuthn.spec +++ b/certora/specs/WebAuthn.spec @@ -11,13 +11,50 @@ import "spec_utils/optimizations.spec"; import "spec_utils/generic.spec"; // pick additional rules from here -use builtin rule sanity filtered { f -> f.contract == currentContract } - -use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } -use builtin rule msgValueInLoopRule; -use builtin rule viewReentrancy; -use rule privilegedOperation filtered { f -> f.contract == currentContract } -use rule timeoutChecker filtered { f -> f.contract == currentContract } -use rule simpleFrontRunning filtered { f -> f.contract == currentContract } -use rule noRevert filtered { f -> f.contract == currentContract } -use rule alwaysRevert filtered { f -> f.contract == currentContract } \ No newline at end of file +// use builtin rule sanity filtered { f -> f.contract == currentContract } + +// use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } +// use builtin rule msgValueInLoopRule; +// use builtin rule viewReentrancy; +// use rule privilegedOperation filtered { f -> f.contract == currentContract } +// use rule timeoutChecker filtered { f -> f.contract == currentContract } +// use rule simpleFrontRunning filtered { f -> f.contract == currentContract } +// use rule noRevert filtered { f -> f.contract == currentContract } +// use rule alwaysRevert filtered { f -> f.contract == currentContract } + +methods{ + function encodeClientDataJson(bytes32 message,string calldata signature) internal returns string => encodeClientDataJsonSummary(message, signature); +} + +ghost encodeClientDataJsonSummary(bytes32, string) returns string { + axiom forall bytes32 x1. forall string y1. forall bytes32 x2. forall string y2. x1 != x2 => ( + encodeClientDataJsonSummary(x1, y1) != encodeClientDataJsonSummary(x2, y2)); +} + +rule encodeClientDataJsonIntegrity(){ + env e; + + bytes32 challenge1; + string clientDataFields; + bytes32 challenge2; + + string a1 = encodeClientDataJson(challenge1, clientDataFields); + string b1 = encodeClientDataJson(challenge2, clientDataFields); + + require(challenge1 != challenge2); + + satisfy true; +} + + +rule shaIntegrity(){ + env e; + + bytes32 input1; + bytes32 input2; + + bytes32 input1_sha = getSha256(e, input1); + bytes32 input2_sha = getSha256(e, input2); + + assert (input1 != input2) <=> input1_sha != input2_sha; +} \ No newline at end of file diff --git a/modules/passkey/contracts/libraries/WebAuthn.sol b/modules/passkey/contracts/libraries/WebAuthn.sol index a4b2d0044..6234dcdb1 100644 --- a/modules/passkey/contracts/libraries/WebAuthn.sol +++ b/modules/passkey/contracts/libraries/WebAuthn.sol @@ -336,7 +336,7 @@ library WebAuthn { * @param input The input bytes to hash. * @return digest The SHA-256 digest of the input bytes. */ - function _sha256(bytes memory input) private view returns (bytes32 digest) { + function _sha256(bytes memory input) public view returns (bytes32 digest) { // solhint-disable-next-line no-inline-assembly assembly ("memory-safe") { // The SHA-256 precompile is at address 0x0002. Note that we don't check the whether or From d15faf81e752bd392b1d02a03242dda8e2394ccc Mon Sep 17 00:00:00 2001 From: liav-certora Date: Sun, 26 May 2024 15:50:53 +0300 Subject: [PATCH 033/103] more rules --- certora/confs/ProxySimulator.conf | 35 +++++++++++++ certora/confs/SafeWebAuthnSignerProxy.conf | 5 +- .../confs/SafeWebAuthnSignerProxyHarness.conf | 32 ++++++++++++ certora/specs/ProxySimulator.spec | 19 +++++++ certora/specs/SafeWebAuthnSignerProxy.spec | 20 +++++++ .../specs/SafeWebAuthnSignerProxyHarness.spec | 42 +++++++++++++++ modules/passkey/contracts/ProxySimulator.sol | 24 +++++++++ .../SafeWebAuthnSignerProxyHarness.sol | 52 +++++++++++++++++++ 8 files changed, 227 insertions(+), 2 deletions(-) create mode 100644 certora/confs/ProxySimulator.conf create mode 100644 certora/confs/SafeWebAuthnSignerProxyHarness.conf create mode 100644 certora/specs/ProxySimulator.spec create mode 100644 certora/specs/SafeWebAuthnSignerProxyHarness.spec create mode 100644 modules/passkey/contracts/ProxySimulator.sol create mode 100644 modules/passkey/contracts/SafeWebAuthnSignerProxyHarness.sol diff --git a/certora/confs/ProxySimulator.conf b/certora/confs/ProxySimulator.conf new file mode 100644 index 000000000..d655b9772 --- /dev/null +++ b/certora/confs/ProxySimulator.conf @@ -0,0 +1,35 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/passkey/contracts/ProxySimulator.sol", + "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol", + "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", + "modules/passkey/contracts/libraries/P256.sol", + ], + "link": [ + "ProxySimulator:_proxy=SafeWebAuthnSignerProxy", + "SafeWebAuthnSignerProxy:_VERIFIERS=P256", + "SafeWebAuthnSignerProxy:_SINGLETON=SafeWebAuthnSignerSingleton" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity_with_all_default_summaries", + "process": "emv", + "prover_args": [ + " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "optimistic_loop": true, + "loop_iter": "6", + "optimistic_hashing": true, + "prover_version": "master", + "server": "production", + "rule_sanity": "basic", + "verify": "ProxySimulator:certora/specs/ProxySimulator.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeWebAuthnSignerProxy.conf b/certora/confs/SafeWebAuthnSignerProxy.conf index 80b4979ff..2026917b4 100644 --- a/certora/confs/SafeWebAuthnSignerProxy.conf +++ b/certora/confs/SafeWebAuthnSignerProxy.conf @@ -3,8 +3,8 @@ "files": [ "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol", "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", - "certora/harnesses/ERC20Like/DummyWeth.sol", - "certora/harnesses/Utilities.sol", + //"certora/harnesses/ERC20Like/DummyWeth.sol", + //"certora/harnesses/Utilities.sol", "modules/passkey/contracts/libraries/P256.sol" ], "link": [ @@ -30,5 +30,6 @@ "optimistic_hashing": true, "prover_version": "master", "server": "production", + "rule_sanity": "basic", "verify": "SafeWebAuthnSignerProxy:certora/specs/SafeWebAuthnSignerProxy.spec" } \ No newline at end of file diff --git a/certora/confs/SafeWebAuthnSignerProxyHarness.conf b/certora/confs/SafeWebAuthnSignerProxyHarness.conf new file mode 100644 index 000000000..36652f76a --- /dev/null +++ b/certora/confs/SafeWebAuthnSignerProxyHarness.conf @@ -0,0 +1,32 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/passkey/contracts/SafeWebAuthnSignerProxyHarness.sol", + "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", + //"certora/harnesses/ERC20Like/DummyWeth.sol", + //"certora/harnesses/Utilities.sol", + "modules/passkey/contracts/libraries/P256.sol" + ], + "link": [ + "SafeWebAuthnSignerProxyHarness:_VERIFIERS=P256", + "SafeWebAuthnSignerProxyHarness:_SINGLETON=SafeWebAuthnSignerSingleton" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity_with_all_default_summaries", + "process": "emv", + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "solc_via_ir": false, + "optimistic_loop": true, + "loop_iter": "6", + "optimistic_hashing": true, + "prover_version": "master", + "server": "production", + "rule_sanity": "basic", + "verify": "SafeWebAuthnSignerProxyHarness:certora/specs/SafeWebAuthnSignerProxyHarness.spec" +} \ No newline at end of file diff --git a/certora/specs/ProxySimulator.spec b/certora/specs/ProxySimulator.spec new file mode 100644 index 000000000..a067d1796 --- /dev/null +++ b/certora/specs/ProxySimulator.spec @@ -0,0 +1,19 @@ +using SafeWebAuthnSignerProxy as SafeWebAuthnSignerProxy; + +definition MAGIC_VALUE() returns bytes4 = to_bytes4(0x1626ba7e); + +methods { + function authenticate(bytes32, bytes) external returns (bytes4) envfree; + function _._ external => DISPATCH [ + SafeWebAuthnSignerProxy._ + ] default NONDET; +} + +rule proxyReturnValue { + bytes32 message; + bytes signature; + + bytes4 ret = authenticate(message, signature); + + assert ret == MAGIC_VALUE(); +} diff --git a/certora/specs/SafeWebAuthnSignerProxy.spec b/certora/specs/SafeWebAuthnSignerProxy.spec index 3e33ea015..c0a9492c3 100644 --- a/certora/specs/SafeWebAuthnSignerProxy.spec +++ b/certora/specs/SafeWebAuthnSignerProxy.spec @@ -1,3 +1,4 @@ +/* Setup Artifacts import "ERC20/erc20cvl.spec"; import "ERC20/WETHcvl.spec"; import "ERC721/erc721.spec"; @@ -21,7 +22,26 @@ use rule timeoutChecker filtered { f -> f.contract == currentContract } use rule simpleFrontRunning filtered { f -> f.contract == currentContract } use rule noRevert filtered { f -> f.contract == currentContract } use rule alwaysRevert filtered { f -> f.contract == currentContract } +*/ + +using SafeWebAuthnSignerSingleton as SafeWebAuthnSignerSingleton; + +hook DELEGATECALL(uint g, address addr, uint argsOffset, uint argsLength, uint retOffset, uint retLength) uint rc { + // DELEGATECALL is used in this contract, but it only ever calls into the singleton. + assert (executingContract != currentContract || addr == SafeWebAuthnSignerSingleton, + "we should only `delegatecall` into the singleton." + ); +} +rule delegateCallsOnlyToSingleton { + env e; + method f; + calldataarg args; + + f(e, args); + + assert true; +} /* Rule: Proxy Configuration Paramateres Never Change -- Passed diff --git a/certora/specs/SafeWebAuthnSignerProxyHarness.spec b/certora/specs/SafeWebAuthnSignerProxyHarness.spec new file mode 100644 index 000000000..2048d0e01 --- /dev/null +++ b/certora/specs/SafeWebAuthnSignerProxyHarness.spec @@ -0,0 +1,42 @@ +/* Setup Artifacts +import "ERC20/erc20cvl.spec"; +import "ERC20/WETHcvl.spec"; +import "ERC721/erc721.spec"; +import "ERC1967/erc1967.spec"; +import "PriceAggregators/chainlink.spec"; +import "PriceAggregators/tellor.spec"; + +import "spec_utils/problems.spec"; +import "spec_utils/unresolved.spec"; +import "spec_utils/optimizations.spec"; + +import "spec_utils/generic.spec"; // pick additional rules from here + +use builtin rule sanity filtered { f -> f.contract == currentContract } + +use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } +use builtin rule msgValueInLoopRule; +use builtin rule viewReentrancy; +use rule privilegedOperation filtered { f -> f.contract == currentContract } +use rule timeoutChecker filtered { f -> f.contract == currentContract } +use rule simpleFrontRunning filtered { f -> f.contract == currentContract } +use rule noRevert filtered { f -> f.contract == currentContract } +use rule alwaysRevert filtered { f -> f.contract == currentContract } +*/ + +methods { + function fallbackButNotDelegating(uint256,uint256,P256.Verifiers) external returns (bytes4); +} + +rule fallbackDoesNotRevert { + env e; + uint256 x; + uint256 y; + P256.Verifiers verifiers; + + require e.msg.value <= nativeBalances[currentContract]; + + fallbackButNotDelegating@withrevert(e, x, y, verifiers); + + assert !lastReverted; +} \ No newline at end of file diff --git a/modules/passkey/contracts/ProxySimulator.sol b/modules/passkey/contracts/ProxySimulator.sol new file mode 100644 index 000000000..9d5053285 --- /dev/null +++ b/modules/passkey/contracts/ProxySimulator.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: LGPL-3.0-only +/* solhint-disable no-complex-fallback */ +pragma solidity >=0.8.0; + +import {SafeWebAuthnSignerProxy} from "./SafeWebAuthnSignerProxy.sol"; + +contract ProxySimulator { + + address internal _proxy; + + constructor(address proxy) { + _proxy = proxy; + } + + function authenticate(bytes32 message, bytes calldata signature) external returns (bytes4) { + bytes memory data = abi.encodeWithSignature("isValidSignature(bytes32,bytes)", message, signature); + + (bool success, bytes memory result) = _proxy.call(data); + + require(success); + + return abi.decode(result, (bytes4)); + } +} \ No newline at end of file diff --git a/modules/passkey/contracts/SafeWebAuthnSignerProxyHarness.sol b/modules/passkey/contracts/SafeWebAuthnSignerProxyHarness.sol new file mode 100644 index 000000000..5aae650c1 --- /dev/null +++ b/modules/passkey/contracts/SafeWebAuthnSignerProxyHarness.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: LGPL-3.0-only +/* solhint-disable no-complex-fallback */ +pragma solidity >=0.8.0; + +import {P256} from "./libraries/WebAuthn.sol"; +import {SafeWebAuthnSignerProxy} from "./SafeWebAuthnSignerProxy.sol"; + +/** + * @title Safe WebAuthn Signer Proxy + * @dev A specialized proxy to a {SafeWebAuthnSignerSingleton} signature validator implementation + * for Safe accounts. Using a proxy pattern for the signature validator greatly reduces deployment + * gas costs. + * @custom:security-contact bounty@safe.global + */ +contract SafeWebAuthnSignerProxyHarness is SafeWebAuthnSignerProxy { + /** + * @notice Creates a new WebAuthn Safe Signer Proxy. + * @param singleton The {SafeWebAuthnSignerSingleton} implementation to proxy to. + * @param x The x coordinate of the P-256 public key of the WebAuthn credential. + * @param y The y coordinate of the P-256 public key of the WebAuthn credential. + * @param verifiers The P-256 verifiers used for ECDSA signature verification. + */ + constructor(address singleton, uint256 x, uint256 y, P256.Verifiers verifiers) + SafeWebAuthnSignerProxy(singleton, x, y, verifiers) { } + + /** + * @dev Fallback function forwards all transactions and returns all received return data. + */ + function fallbackButNotDelegating(uint256 x, uint256 y, P256.Verifiers verifiers) external payable returns (bytes4) { + // solhint-disable-next-line no-inline-assembly + assembly { + // Forward the call to the singleton implementation. We append the configuration to the + // calldata instead of having the singleton implementation read it from storage. This is + // both more gas efficient and required for ERC-4337 compatibility. Note that we append + // the configuration fields in reverse order since the fields are packed, and this makes + // it so we don't need to mask any bits from the `verifiers` value. This computes `data` + // to be `abi.encodePacked(msg.data, x, y, verifiers)`. + let data := mload(0x40) + mstore(add(data, add(calldatasize(), 0x36)), verifiers) + mstore(add(data, add(calldatasize(), 0x20)), y) + mstore(add(data, calldatasize()), x) + calldatacopy(data, 0x00, calldatasize()) + + let success := true + returndatacopy(0, 0, returndatasize()) + if iszero(success) { + revert(0, returndatasize()) + } + return(0, returndatasize()) + } + } +} From ba92c5b7ed85fb10410e20704b92493088faf43f Mon Sep 17 00:00:00 2001 From: liav-certora Date: Sun, 26 May 2024 18:14:16 +0300 Subject: [PATCH 034/103] comments --- certora/specs/ProxySimulator.spec | 6 +++ certora/specs/SafeWebAuthnSignerProxy.spec | 10 ++++- .../specs/SafeWebAuthnSignerProxyHarness.spec | 37 ++++--------------- 3 files changed, 22 insertions(+), 31 deletions(-) diff --git a/certora/specs/ProxySimulator.spec b/certora/specs/ProxySimulator.spec index a067d1796..f4f4120a2 100644 --- a/certora/specs/ProxySimulator.spec +++ b/certora/specs/ProxySimulator.spec @@ -9,6 +9,12 @@ methods { ] default NONDET; } +/* +Property 14. Proxy - verify return data from the fallback is only one of the magicNumbers +Uses another contract that simulates interaction with the proxy. The reason is that the prover doesn't check all +possible calldata values so this simualtion will make the prover choose different values that will be passed on the calldata. +Rule stuck. +*/ rule proxyReturnValue { bytes32 message; bytes signature; diff --git a/certora/specs/SafeWebAuthnSignerProxy.spec b/certora/specs/SafeWebAuthnSignerProxy.spec index c0a9492c3..0f665b620 100644 --- a/certora/specs/SafeWebAuthnSignerProxy.spec +++ b/certora/specs/SafeWebAuthnSignerProxy.spec @@ -23,7 +23,6 @@ use rule simpleFrontRunning filtered { f -> f.contract == currentContract } use rule noRevert filtered { f -> f.contract == currentContract } use rule alwaysRevert filtered { f -> f.contract == currentContract } */ - using SafeWebAuthnSignerSingleton as SafeWebAuthnSignerSingleton; hook DELEGATECALL(uint g, address addr, uint argsOffset, uint argsLength, uint retOffset, uint retLength) uint rc { @@ -33,6 +32,11 @@ hook DELEGATECALL(uint g, address addr, uint argsOffset, uint argsLength, uint r ); } +/* +Property 12. Proxy - Delegate Call Integrity (calls the Singleton) +Hooking on delegate calls will make sure we'll get a violation if the singleton isn't the contract called. +Rule verified. +*/ rule delegateCallsOnlyToSingleton { env e; method f; @@ -44,7 +48,9 @@ rule delegateCallsOnlyToSingleton { } /* -Rule: Proxy Configuration Paramateres Never Change -- Passed +Property 11. Proxy - Immutability of Configuration Parameters (x, y, Singleton, verifier) +x, y, singleton and verifiers never changes after any function call. +Rule verified. */ rule configParametersImmutability { env e; diff --git a/certora/specs/SafeWebAuthnSignerProxyHarness.spec b/certora/specs/SafeWebAuthnSignerProxyHarness.spec index 2048d0e01..05f8b9302 100644 --- a/certora/specs/SafeWebAuthnSignerProxyHarness.spec +++ b/certora/specs/SafeWebAuthnSignerProxyHarness.spec @@ -1,42 +1,21 @@ -/* Setup Artifacts -import "ERC20/erc20cvl.spec"; -import "ERC20/WETHcvl.spec"; -import "ERC721/erc721.spec"; -import "ERC1967/erc1967.spec"; -import "PriceAggregators/chainlink.spec"; -import "PriceAggregators/tellor.spec"; - -import "spec_utils/problems.spec"; -import "spec_utils/unresolved.spec"; -import "spec_utils/optimizations.spec"; - -import "spec_utils/generic.spec"; // pick additional rules from here - -use builtin rule sanity filtered { f -> f.contract == currentContract } - -use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } -use builtin rule msgValueInLoopRule; -use builtin rule viewReentrancy; -use rule privilegedOperation filtered { f -> f.contract == currentContract } -use rule timeoutChecker filtered { f -> f.contract == currentContract } -use rule simpleFrontRunning filtered { f -> f.contract == currentContract } -use rule noRevert filtered { f -> f.contract == currentContract } -use rule alwaysRevert filtered { f -> f.contract == currentContract } -*/ - methods { function fallbackButNotDelegating(uint256,uint256,P256.Verifiers) external returns (bytes4); } +/* +Property 13. Proxy - Fallback data corruption does not revert the fallback (uses data appending that needed to be verified). +The fallback will only revert due to payable modifier. +Rule Verified. +*/ rule fallbackDoesNotRevert { env e; uint256 x; uint256 y; P256.Verifiers verifiers; - require e.msg.value <= nativeBalances[currentContract]; - + bool notEnoughEthSender = e.msg.value > nativeBalances[e.msg.sender]; + fallbackButNotDelegating@withrevert(e, x, y, verifiers); - assert !lastReverted; + assert lastReverted <=> notEnoughEthSender; } \ No newline at end of file From fc88173d416e55bb8997131ed3e2d3ecd2b4f36d Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Sun, 26 May 2024 18:17:10 +0300 Subject: [PATCH 035/103] Start webauth --- certora/harnesses/WebAuthnHarness.sol | 37 +++++++- certora/specs/WebAuthn.spec | 130 ++++++++++++++++++-------- 2 files changed, 125 insertions(+), 42 deletions(-) diff --git a/certora/harnesses/WebAuthnHarness.sol b/certora/harnesses/WebAuthnHarness.sol index 3635cec05..116ec7b7b 100644 --- a/certora/harnesses/WebAuthnHarness.sol +++ b/certora/harnesses/WebAuthnHarness.sol @@ -4,11 +4,33 @@ pragma solidity ^0.8.0; import {P256, WebAuthn} from "../../modules/passkey/contracts/libraries/WebAuthn.sol"; contract WebAuthnHarness { - + + mapping(bytes32 => mapping(bytes32 => string)) summaryMap; + + function getEncodeClientDataJsonSummary(bytes32 message, string calldata signature) public view returns (string memory){ + bytes32 hashed_signature = keccak256(abi.encodePacked(signature)); + string memory string_mapping = summaryMap[message][hashed_signature]; + bytes32 hashed_mapping = keccak256(abi.encodePacked(string_mapping)); + + require (encodeAxiom(message, hashed_signature, hashed_mapping)); + + return string_mapping; + } + + function encodeAxiom(bytes32 message, bytes32 signature, bytes32 result) internal pure returns (bool){ + return true; + } + function castSignature(bytes calldata signature) external pure returns (bool isValid, WebAuthn.Signature calldata data){ return WebAuthn.castSignature(signature); } + function compareStrings(string memory str1, string memory str2) public view returns (bool) { + bytes memory str1Bytes = bytes(str1); + bytes memory str2Bytes = bytes(str2); + return getSha256(str1Bytes) == getSha256(str2Bytes); + } + function encodeClientDataJson( bytes32 challenge, string calldata clientDataFields @@ -31,6 +53,14 @@ contract WebAuthnHarness { return WebAuthn.checkAuthenticatorFlags(authenticatorData, authenticatorFlags); } + function prepareSignature(bytes calldata authenticatorData, + string calldata clientDataFields, + uint256 r, + uint256 s) public pure returns(bytes memory signature, WebAuthn.Signature memory structSignature){ + signature = abi.encode(authenticatorData, clientDataFields, r, s); + structSignature = WebAuthn.Signature(authenticatorData, clientDataFields, r, s); + } + function verifySignature( bytes32 challenge, bytes calldata signature, @@ -53,9 +83,8 @@ contract WebAuthnHarness { return WebAuthn.verifySignature(challenge, signature, authenticatorFlags, x, y, verifiers); } - function getSha256(bytes32 input) public view returns (bytes32 digest) { - bytes memory m = abi.encodePacked(input); - return WebAuthn._sha256(m); + function getSha256(bytes memory input) public view returns (bytes32 digest) { + return WebAuthn._sha256(input); } } diff --git a/certora/specs/WebAuthn.spec b/certora/specs/WebAuthn.spec index f8915d8f0..b6c68c241 100644 --- a/certora/specs/WebAuthn.spec +++ b/certora/specs/WebAuthn.spec @@ -1,60 +1,114 @@ -import "ERC20/erc20cvl.spec"; -import "ERC20/WETHcvl.spec"; -import "ERC721/erc721.spec"; -import "ERC1967/erc1967.spec"; -import "PriceAggregators/chainlink.spec"; -import "PriceAggregators/tellor.spec"; - -import "spec_utils/problems.spec"; -import "spec_utils/unresolved.spec"; -import "spec_utils/optimizations.spec"; - -import "spec_utils/generic.spec"; // pick additional rules from here - -// use builtin rule sanity filtered { f -> f.contract == currentContract } - -// use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } -// use builtin rule msgValueInLoopRule; -// use builtin rule viewReentrancy; -// use rule privilegedOperation filtered { f -> f.contract == currentContract } -// use rule timeoutChecker filtered { f -> f.contract == currentContract } -// use rule simpleFrontRunning filtered { f -> f.contract == currentContract } -// use rule noRevert filtered { f -> f.contract == currentContract } -// use rule alwaysRevert filtered { f -> f.contract == currentContract } - methods{ - function encodeClientDataJson(bytes32 message,string calldata signature) internal returns string => encodeClientDataJsonSummary(message, signature); + function getEncodeClientDataJsonSummary(bytes32 message, string calldata signature) external returns (string memory); + + function encodeClientDataJson(bytes32 message, string calldata signature) internal returns (string memory) => + getEncodeClientDataJsonSummaryCVL(message, signature); + + function encodeAxiom(bytes32 message, bytes32 signature, bytes32 result) internal returns (bool) => + encodeAxiomSummary(message, signature, result); + + //function encodeSigningMessage(bytes32,bytes calldata,string calldata) internal returns(bytes memory) => NONDET; } -ghost encodeClientDataJsonSummary(bytes32, string) returns string { - axiom forall bytes32 x1. forall string y1. forall bytes32 x2. forall string y2. x1 != x2 => ( - encodeClientDataJsonSummary(x1, y1) != encodeClientDataJsonSummary(x2, y2)); +function getEncodeClientDataJsonSummaryCVL(bytes32 message, string signature) returns string +{ + env e; + return getEncodeClientDataJsonSummary(e, message, signature); } +ghost encodeAxiomSummary(bytes32, bytes32, bytes32) returns bool { + axiom forall bytes32 x1. forall bytes32 y1. forall bytes32 x2. forall bytes32 y2. forall bytes32 z. + (x1 != x2 || y1 != y2) => !(encodeAxiomSummary(x1, y1, z) && encodeAxiomSummary(x2, y2, z)); +} + +// Inner Check for Summarization rule encodeClientDataJsonIntegrity(){ + env e; bytes32 challenge1; string clientDataFields; bytes32 challenge2; - string a1 = encodeClientDataJson(challenge1, clientDataFields); - string b1 = encodeClientDataJson(challenge2, clientDataFields); - - require(challenge1 != challenge2); + string a1 = encodeClientDataJson(e, challenge1, clientDataFields); + string b1 = encodeClientDataJson(e, challenge2, clientDataFields); - satisfy true; + assert (challenge1 != challenge2) <=> !compareStrings(e, a1, b1); } - +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ shaIntegrity 2 different inputs results in 2 different hashes (Proved) │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ rule shaIntegrity(){ env e; - bytes32 input1; - bytes32 input2; + bytes input1; + bytes input2; bytes32 input1_sha = getSha256(e, input1); bytes32 input2_sha = getSha256(e, input2); - assert (input1 != input2) <=> input1_sha != input2_sha; -} \ No newline at end of file + assert (keccak256(input1) != keccak256(input2)) <=> input1_sha != input2_sha; +} + + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ every 2 challenges results in unique message when using encodeSigningMessage (Timeout cert-6290) │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ +rule uniqueMessagePerChallenge(){ + env e; + + bytes32 challenge1; + bytes32 challenge2; + bytes authenticatorData; + string clientDataField; + + bytes message1 = encodeSigningMessage(e, challenge1, authenticatorData, clientDataField); + bytes message2 = encodeSigningMessage(e, challenge2, authenticatorData, clientDataField); + + assert (challenge1 != challenge2) <=> (getSha256(e, message1) != getSha256(e, message2)); +} + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ verifySignature functions are equivalent │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ +rule verifySignatureEq(){ + env e; + + // verify signature related args + bytes32 challenge; + WebAuthn.AuthenticatorFlags authenticatorFlags; + uint256 x; + uint256 y; + P256.Verifiers verifiers; + + // signature related args + bytes authenticatorData; + string clientDataFields; + uint256 r; + uint256 s; + bytes bytesSignature; + WebAuthn.Signature structSignature; + + bytesSignature, structSignature = prepareSignature(e, authenticatorData, clientDataFields, r, s); + + storage firstStorage = lastStorage; + + bool result1 = verifySignature@withrevert(e, challenge, bytesSignature, authenticatorFlags, x, y, verifiers); + bool firstCallRevert = lastReverted; + + bool result2 = verifySignature@withrevert(e, challenge, bytesSignature, authenticatorFlags, x, y, verifiers) at firstStorage; + bool secondCallRevert = lastReverted; + + assert firstCallRevert == secondCallRevert; + assert result1 == result2; +} + + + From dca8c0280b4c3664c489a565457b9582b780b170 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Sun, 26 May 2024 18:22:27 +0300 Subject: [PATCH 036/103] Fix rule condition --- certora/specs/WebAuthn.spec | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/certora/specs/WebAuthn.spec b/certora/specs/WebAuthn.spec index b6c68c241..037c3a0bb 100644 --- a/certora/specs/WebAuthn.spec +++ b/certora/specs/WebAuthn.spec @@ -106,8 +106,7 @@ rule verifySignatureEq(){ bool result2 = verifySignature@withrevert(e, challenge, bytesSignature, authenticatorFlags, x, y, verifiers) at firstStorage; bool secondCallRevert = lastReverted; - assert firstCallRevert == secondCallRevert; - assert result1 == result2; + assert (firstCallRevert == secondCallRevert) || (result1 == result2); } From 76d0729907b463fb33bc4aebf3234ecaae4bbb87 Mon Sep 17 00:00:00 2001 From: liav-certora Date: Mon, 27 May 2024 12:58:11 +0300 Subject: [PATCH 037/103] Nurit CR --- certora/confs/ProxySimulator.conf | 12 +----------- certora/confs/SafeWebAuthnSignerProxy.conf | 10 ---------- certora/confs/SafeWebAuthnSignerProxyHarness.conf | 11 +---------- .../harnesses}/SafeWebAuthnSignerProxyHarness.sol | 15 ++++++++------- .../helpers}/ProxySimulator.sol | 2 +- certora/specs/ProxySimulator.spec | 1 + 6 files changed, 12 insertions(+), 39 deletions(-) rename {modules/passkey/contracts => certora/harnesses}/SafeWebAuthnSignerProxyHarness.sol (74%) rename {modules/passkey/contracts => certora/helpers}/ProxySimulator.sol (85%) diff --git a/certora/confs/ProxySimulator.conf b/certora/confs/ProxySimulator.conf index d655b9772..bc56bcf06 100644 --- a/certora/confs/ProxySimulator.conf +++ b/certora/confs/ProxySimulator.conf @@ -1,7 +1,7 @@ { "assert_autofinder_success": true, "files": [ - "modules/passkey/contracts/ProxySimulator.sol", + "certora/helpers/ProxySimulator.sol", "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol", "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", "modules/passkey/contracts/libraries/P256.sol", @@ -11,14 +11,6 @@ "SafeWebAuthnSignerProxy:_VERIFIERS=P256", "SafeWebAuthnSignerProxy:_SINGLETON=SafeWebAuthnSignerSingleton" ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity_with_all_default_summaries", - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], "packages":[ "@safe-global=node_modules/@safe-global", "@account-abstraction=node_modules/@account-abstraction" @@ -28,8 +20,6 @@ "optimistic_loop": true, "loop_iter": "6", "optimistic_hashing": true, - "prover_version": "master", - "server": "production", "rule_sanity": "basic", "verify": "ProxySimulator:certora/specs/ProxySimulator.spec" } \ No newline at end of file diff --git a/certora/confs/SafeWebAuthnSignerProxy.conf b/certora/confs/SafeWebAuthnSignerProxy.conf index 2026917b4..4ac4342b6 100644 --- a/certora/confs/SafeWebAuthnSignerProxy.conf +++ b/certora/confs/SafeWebAuthnSignerProxy.conf @@ -11,14 +11,6 @@ "SafeWebAuthnSignerProxy:_VERIFIERS=P256", "SafeWebAuthnSignerProxy:_SINGLETON=SafeWebAuthnSignerSingleton" ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity_with_all_default_summaries", - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], "packages":[ "@safe-global=node_modules/@safe-global", "@account-abstraction=node_modules/@account-abstraction" @@ -28,8 +20,6 @@ "optimistic_loop": true, "loop_iter": "6", "optimistic_hashing": true, - "prover_version": "master", - "server": "production", "rule_sanity": "basic", "verify": "SafeWebAuthnSignerProxy:certora/specs/SafeWebAuthnSignerProxy.spec" } \ No newline at end of file diff --git a/certora/confs/SafeWebAuthnSignerProxyHarness.conf b/certora/confs/SafeWebAuthnSignerProxyHarness.conf index 36652f76a..88193a0dd 100644 --- a/certora/confs/SafeWebAuthnSignerProxyHarness.conf +++ b/certora/confs/SafeWebAuthnSignerProxyHarness.conf @@ -1,21 +1,14 @@ { "assert_autofinder_success": true, "files": [ - "modules/passkey/contracts/SafeWebAuthnSignerProxyHarness.sol", + "certora/harnesses/SafeWebAuthnSignerProxyHarness.sol", "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", - //"certora/harnesses/ERC20Like/DummyWeth.sol", - //"certora/harnesses/Utilities.sol", "modules/passkey/contracts/libraries/P256.sol" ], "link": [ "SafeWebAuthnSignerProxyHarness:_VERIFIERS=P256", "SafeWebAuthnSignerProxyHarness:_SINGLETON=SafeWebAuthnSignerSingleton" ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity_with_all_default_summaries", - "process": "emv", "packages":[ "@safe-global=node_modules/@safe-global", "@account-abstraction=node_modules/@account-abstraction" @@ -25,8 +18,6 @@ "optimistic_loop": true, "loop_iter": "6", "optimistic_hashing": true, - "prover_version": "master", - "server": "production", "rule_sanity": "basic", "verify": "SafeWebAuthnSignerProxyHarness:certora/specs/SafeWebAuthnSignerProxyHarness.spec" } \ No newline at end of file diff --git a/modules/passkey/contracts/SafeWebAuthnSignerProxyHarness.sol b/certora/harnesses/SafeWebAuthnSignerProxyHarness.sol similarity index 74% rename from modules/passkey/contracts/SafeWebAuthnSignerProxyHarness.sol rename to certora/harnesses/SafeWebAuthnSignerProxyHarness.sol index 5aae650c1..29f9500f9 100644 --- a/modules/passkey/contracts/SafeWebAuthnSignerProxyHarness.sol +++ b/certora/harnesses/SafeWebAuthnSignerProxyHarness.sol @@ -2,15 +2,16 @@ /* solhint-disable no-complex-fallback */ pragma solidity >=0.8.0; -import {P256} from "./libraries/WebAuthn.sol"; -import {SafeWebAuthnSignerProxy} from "./SafeWebAuthnSignerProxy.sol"; +import {P256} from "../../modules/passkey/contracts/libraries/WebAuthn.sol"; +import {SafeWebAuthnSignerProxy} from "../../modules/passkey/contracts/SafeWebAuthnSignerProxy.sol"; /** - * @title Safe WebAuthn Signer Proxy - * @dev A specialized proxy to a {SafeWebAuthnSignerSingleton} signature validator implementation - * for Safe accounts. Using a proxy pattern for the signature validator greatly reduces deployment - * gas costs. - * @custom:security-contact bounty@safe.global + * @title Safe WebAuthn Signer Proxy Harness + * @dev This harness is written to be able to prove a certain property on the fallback function. + * The property we are proving using this harness is that no combination of x, y and verifiers can make the fallback revert + * due the problems with the untrivial data appanding. + * It adds another function `fallbackButNotDelegating which has the exact same functionality as the original fallback + * but it gets the x, y, and verifiers parameters instead of reading the immutable ones and does not make a delegatecall. */ contract SafeWebAuthnSignerProxyHarness is SafeWebAuthnSignerProxy { /** diff --git a/modules/passkey/contracts/ProxySimulator.sol b/certora/helpers/ProxySimulator.sol similarity index 85% rename from modules/passkey/contracts/ProxySimulator.sol rename to certora/helpers/ProxySimulator.sol index 9d5053285..d9decc29f 100644 --- a/modules/passkey/contracts/ProxySimulator.sol +++ b/certora/helpers/ProxySimulator.sol @@ -2,7 +2,7 @@ /* solhint-disable no-complex-fallback */ pragma solidity >=0.8.0; -import {SafeWebAuthnSignerProxy} from "./SafeWebAuthnSignerProxy.sol"; +import {SafeWebAuthnSignerProxy} from "../../modules/passkey/contracts/SafeWebAuthnSignerProxy.sol"; contract ProxySimulator { diff --git a/certora/specs/ProxySimulator.spec b/certora/specs/ProxySimulator.spec index f4f4120a2..a8405eeff 100644 --- a/certora/specs/ProxySimulator.spec +++ b/certora/specs/ProxySimulator.spec @@ -1,5 +1,6 @@ using SafeWebAuthnSignerProxy as SafeWebAuthnSignerProxy; +// This is the same MAGIC_VALUE constant used in ERC1271. definition MAGIC_VALUE() returns bytes4 = to_bytes4(0x1626ba7e); methods { From bf0ff62b84245307a9ebb05416eb68103fc2814c Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Mon, 27 May 2024 17:04:20 +0300 Subject: [PATCH 038/103] Add webAuth rules --- certora/confs/WebAuthn.conf | 4 +- certora/harnesses/WebAuthnHarness.sol | 16 +++++++ certora/specs/WebAuthn.spec | 63 ++++++++++++++++++++++++++- 3 files changed, 79 insertions(+), 4 deletions(-) diff --git a/certora/confs/WebAuthn.conf b/certora/confs/WebAuthn.conf index ab59de055..06ce73680 100644 --- a/certora/confs/WebAuthn.conf +++ b/certora/confs/WebAuthn.conf @@ -1,9 +1,7 @@ { "assert_autofinder_success": true, "files": [ - "certora/harnesses/WebAuthnHarness.sol", - "certora/harnesses/ERC20Like/DummyWeth.sol", - "certora/harnesses/Utilities.sol", + "certora/harnesses/WebAuthnHarness.sol" ], "java_args": [ " -ea -Dlevel.setup.helpers=info" diff --git a/certora/harnesses/WebAuthnHarness.sol b/certora/harnesses/WebAuthnHarness.sol index 116ec7b7b..642361d96 100644 --- a/certora/harnesses/WebAuthnHarness.sol +++ b/certora/harnesses/WebAuthnHarness.sol @@ -21,6 +21,22 @@ contract WebAuthnHarness { return true; } + function compareSignatures(WebAuthn.Signature memory sig1, WebAuthn.Signature memory sig2) public pure returns (bool) { + if (sig1.r != sig2.r || sig1.s != sig2.s) { + return false; + } + + if (keccak256(abi.encodePacked(sig1.clientDataFields)) != keccak256(abi.encodePacked(sig2.clientDataFields))) { + return false; + } + + if (keccak256(sig1.authenticatorData) != keccak256(sig2.authenticatorData)) { + return false; + } + + return true; + } + function castSignature(bytes calldata signature) external pure returns (bool isValid, WebAuthn.Signature calldata data){ return WebAuthn.castSignature(signature); } diff --git a/certora/specs/WebAuthn.spec b/certora/specs/WebAuthn.spec index 037c3a0bb..e055bee91 100644 --- a/certora/specs/WebAuthn.spec +++ b/certora/specs/WebAuthn.spec @@ -8,6 +8,7 @@ methods{ encodeAxiomSummary(message, signature, result); //function encodeSigningMessage(bytes32,bytes calldata,string calldata) internal returns(bytes memory) => NONDET; + function castSignature(bytes calldata signature) external returns (bool, WebAuthn.Signature calldata); } function getEncodeClientDataJsonSummaryCVL(bytes32 message, string signature) returns string @@ -75,7 +76,7 @@ rule uniqueMessagePerChallenge(){ /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ verifySignature functions are equivalent │ +│ verifySignature functions are equivalent (Vacuity check timeout cert-6290) │ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ rule verifySignatureEq(){ @@ -109,5 +110,65 @@ rule verifySignatureEq(){ assert (firstCallRevert == secondCallRevert) || (result1 == result2); } +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ CastSignature Consistent (Once valid always valid, Once failed always failed, includes revert cases and middle call)| +│ (Proved) | +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ + +rule castSignatureConsistent(){ + env e; + method f; + calldataarg args; + + bytes signature; + + bool firstIsValid; + WebAuthn.Signature firstData; + + bool secondIsValid; + WebAuthn.Signature secondData; + + firstIsValid, firstData = castSignature@withrevert(e, signature); + bool firstRevert = lastReverted; + + f(e, args); + + secondIsValid, secondData = castSignature@withrevert(e, signature); + bool secondRevert = lastReverted; + + if (!firstRevert && !secondRevert) { + assert compareSignatures(e, firstData, secondData) && firstIsValid == secondIsValid; + } + + assert firstRevert == secondRevert; +} + + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ CastSignature uniqueness (violated) | +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ + +rule castSignatureUniqueness(){ + + env e; + + bytes signature1; + bytes signature2; + + bool firstIsValid; + WebAuthn.Signature firstData; + + bool secondIsValid; + WebAuthn.Signature secondData; + + firstIsValid, firstData = castSignature(e, signature1); + secondIsValid, secondData = castSignature(e, signature2); + assert (getSha256(e, signature1) != getSha256(e, signature2)) && + ((firstIsValid && secondIsValid)) => !compareSignatures(e, firstData, secondData); +} \ No newline at end of file From 3e1758f43440db9f572b6e6d5200b7c3274d4902 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Mon, 27 May 2024 18:27:43 +0300 Subject: [PATCH 039/103] Fix assert --- certora/specs/WebAuthn.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/certora/specs/WebAuthn.spec b/certora/specs/WebAuthn.spec index e055bee91..11cd7120a 100644 --- a/certora/specs/WebAuthn.spec +++ b/certora/specs/WebAuthn.spec @@ -169,6 +169,6 @@ rule castSignatureUniqueness(){ secondIsValid, secondData = castSignature(e, signature2); - assert (getSha256(e, signature1) != getSha256(e, signature2)) && - ((firstIsValid && secondIsValid)) => !compareSignatures(e, firstData, secondData); + assert ((getSha256(e, signature1) != getSha256(e, signature2)) && + ((firstIsValid && secondIsValid))) => !compareSignatures(e, firstData, secondData); } \ No newline at end of file From 82aef3adb2803606062dddbe3e11b55ac082a482 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Mon, 27 May 2024 19:11:45 +0300 Subject: [PATCH 040/103] Cleaning --- certora/specs/SafeWebAuthnSignerFactory.spec | 27 -------------------- 1 file changed, 27 deletions(-) diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index 87cd96cac..fb46ceccf 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -1,14 +1,3 @@ -import "ERC20/erc20cvl.spec"; -import "ERC20/WETHcvl.spec"; -import "ERC721/erc721.spec"; -import "ERC1967/erc1967.spec"; -import "PriceAggregators/chainlink.spec"; -import "PriceAggregators/tellor.spec"; -import "spec_utils/problems.spec"; -import "spec_utils/unresolved.spec"; -import "spec_utils/optimizations.spec"; -import "spec_utils/generic.spec"; // pick additional rules from here - using SafeWebAuthnSignerProxy as proxy; using SafeWebAuthnSignerSingleton as singleton; @@ -16,26 +5,10 @@ methods{ function getSigner(uint256, uint256, P256.Verifiers) external returns (address) envfree; function createSigner(uint256, uint256, P256.Verifiers) external returns (address) envfree; function hasNoCode(address) external returns (bool) envfree; -// function _._ external => DISPATCH [ -// proxy._, -// singleton.isValidSignature(bytes32, bytes) -// ] default NONDET; } definition MAGIC_VALUE() returns bytes4 = to_bytes4(0x1626ba7e); - -// use builtin rule sanity filtered { f -> f.contract == currentContract } -// use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } -// use builtin rule msgValueInLoopRule; -// use builtin rule viewReentrancy; -// use rule privilegedOperation filtered { f -> f.contract == currentContract } -// use rule timeoutChecker filtered { f -> f.contract == currentContract } -// use rule simpleFrontRunning filtered { f -> f.contract == currentContract } -// use rule noRevert filtered { f -> f.contract == currentContract } -// use rule alwaysRevert filtered { f -> f.contract == currentContract } - - /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Singleton implementation never change (Proved) │ From 3a7ecc288ec8fda64093c47bce0e64faf7051a09 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Tue, 28 May 2024 10:25:49 +0300 Subject: [PATCH 041/103] Improve deterministic signer --- certora/specs/SafeWebAuthnSignerFactory.spec | 35 ++++++++++++-------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index fb46ceccf..731f268c4 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -2,8 +2,8 @@ using SafeWebAuthnSignerProxy as proxy; using SafeWebAuthnSignerSingleton as singleton; methods{ - function getSigner(uint256, uint256, P256.Verifiers) external returns (address) envfree; - function createSigner(uint256, uint256, P256.Verifiers) external returns (address) envfree; + function getSigner(uint256, uint256, P256.Verifiers) external returns (address); + function createSigner(uint256, uint256, P256.Verifiers) external returns (address); function hasNoCode(address) external returns (bool) envfree; } @@ -36,17 +36,19 @@ rule singletonNeverChanges() // consider adding the following munging after the creationcode to get a more clear dump 01234567891011121314152546 rule uniqueSigner(){ + env e; + uint256 firstX; uint256 firstY; P256.Verifiers firstVerifier; - address firstSigner = getSigner(firstX, firstY, firstVerifier); + address firstSigner = getSigner(e, firstX, firstY, firstVerifier); uint256 secondX; uint256 secondY; P256.Verifiers secondVerifier; - address secondSigner = getSigner(secondX, secondY, secondVerifier); + address secondSigner = getSigner(e, secondX, secondY, secondVerifier); assert firstSigner == secondSigner <=> (firstX == secondX && firstY == secondY && firstVerifier == secondVerifier); } @@ -58,17 +60,19 @@ rule uniqueSigner(){ */ rule createAndGetSignerEquivalence(){ + env e; + uint256 createX; uint256 createY; P256.Verifiers createVerifier; - address signer1 = createSigner(createX, createY, createVerifier); + address signer1 = createSigner(e, createX, createY, createVerifier); uint256 getX; uint256 getY; P256.Verifiers getVerifier; - address signer2 = getSigner(getX, getY, getVerifier); + address signer2 = getSigner(e, getX, getY, getVerifier); assert signer1 == signer2 <=> (createX == getX && createY == getY && createVerifier == getVerifier); } @@ -80,13 +84,16 @@ rule createAndGetSignerEquivalence(){ */ rule deterministicSigner() { + env e1; + env e2; + uint x; uint y; P256.Verifiers verifier; - address signer = getSigner(x, y, verifier); + address signer = getSigner(e1, x, y, verifier); - assert signer == getSigner(x, y, verifier); + assert signer == getSigner(e2, x, y, verifier); } /* @@ -111,31 +118,33 @@ hook CREATE2(uint value, uint offset, uint length, bytes32 salt) address v{ rule SignerCreationCantOverride() { + env e; require numOfCreation == 0; uint x; uint y; P256.Verifiers verifier; - address a = getSigner(x, y, verifier); + address a = getSigner(e, x, y, verifier); require address_map[a] == 0; - createSigner(x, y, verifier); - createSigner@withrevert(x, y, verifier); + createSigner(e, x, y, verifier); + createSigner@withrevert(e, x, y, verifier); assert numOfCreation < 2; } rule ValidValue() { + env e; require !validValue; uint x; uint y; P256.Verifiers verifier; - createSigner(x, y, verifier); - createSigner@withrevert(x, y, verifier); + createSigner(e, x, y, verifier); + createSigner@withrevert(e, x, y, verifier); satisfy validValue; } From 34d557b61d22db3c6f55b84b90ea47b1cfc90e58 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Tue, 28 May 2024 13:55:47 +0300 Subject: [PATCH 042/103] Fix summary rule is still violated --- certora/specs/WebAuthn.spec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/certora/specs/WebAuthn.spec b/certora/specs/WebAuthn.spec index 11cd7120a..932820496 100644 --- a/certora/specs/WebAuthn.spec +++ b/certora/specs/WebAuthn.spec @@ -1,7 +1,7 @@ methods{ function getEncodeClientDataJsonSummary(bytes32 message, string calldata signature) external returns (string memory); - function encodeClientDataJson(bytes32 message, string calldata signature) internal returns (string memory) => + function WebAuthn.encodeClientDataJson(bytes32 message, string calldata signature) internal returns (string memory) => getEncodeClientDataJsonSummaryCVL(message, signature); function encodeAxiom(bytes32 message, bytes32 signature, bytes32 result) internal returns (bool) => @@ -35,6 +35,7 @@ rule encodeClientDataJsonIntegrity(){ string b1 = encodeClientDataJson(e, challenge2, clientDataFields); assert (challenge1 != challenge2) <=> !compareStrings(e, a1, b1); + satisfy true; } /* From caf2a109cd3712ed08ae5522b1618480376b68da Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Tue, 28 May 2024 13:57:30 +0300 Subject: [PATCH 043/103] Apply smt suggestion to avoid timeout on Encoding message --- certora/confs/WebAuthn.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/certora/confs/WebAuthn.conf b/certora/confs/WebAuthn.conf index 06ce73680..285439bef 100644 --- a/certora/confs/WebAuthn.conf +++ b/certora/confs/WebAuthn.conf @@ -12,6 +12,7 @@ "@safe-global=node_modules/@safe-global", "@account-abstraction=node_modules/@account-abstraction" ], + "prover_args": ["-useBitVectorTheory"], "solc": "solc8.23", "solc_via_ir": false, "optimistic_loop": true, From 1a90bed25e8fc9ac3953e34663d68f3886c0d7ba Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Wed, 29 May 2024 12:14:32 +0300 Subject: [PATCH 044/103] Remove summary apply bitvector --- certora/harnesses/WebAuthnHarness.sol | 17 ---------------- certora/specs/WebAuthn.spec | 29 +++++---------------------- 2 files changed, 5 insertions(+), 41 deletions(-) diff --git a/certora/harnesses/WebAuthnHarness.sol b/certora/harnesses/WebAuthnHarness.sol index 642361d96..26bad09ed 100644 --- a/certora/harnesses/WebAuthnHarness.sol +++ b/certora/harnesses/WebAuthnHarness.sol @@ -4,23 +4,6 @@ pragma solidity ^0.8.0; import {P256, WebAuthn} from "../../modules/passkey/contracts/libraries/WebAuthn.sol"; contract WebAuthnHarness { - - mapping(bytes32 => mapping(bytes32 => string)) summaryMap; - - function getEncodeClientDataJsonSummary(bytes32 message, string calldata signature) public view returns (string memory){ - bytes32 hashed_signature = keccak256(abi.encodePacked(signature)); - string memory string_mapping = summaryMap[message][hashed_signature]; - bytes32 hashed_mapping = keccak256(abi.encodePacked(string_mapping)); - - require (encodeAxiom(message, hashed_signature, hashed_mapping)); - - return string_mapping; - } - - function encodeAxiom(bytes32 message, bytes32 signature, bytes32 result) internal pure returns (bool){ - return true; - } - function compareSignatures(WebAuthn.Signature memory sig1, WebAuthn.Signature memory sig2) public pure returns (bool) { if (sig1.r != sig2.r || sig1.s != sig2.s) { return false; diff --git a/certora/specs/WebAuthn.spec b/certora/specs/WebAuthn.spec index 932820496..eeae6ee2d 100644 --- a/certora/specs/WebAuthn.spec +++ b/certora/specs/WebAuthn.spec @@ -1,28 +1,9 @@ -methods{ - function getEncodeClientDataJsonSummary(bytes32 message, string calldata signature) external returns (string memory); - - function WebAuthn.encodeClientDataJson(bytes32 message, string calldata signature) internal returns (string memory) => - getEncodeClientDataJsonSummaryCVL(message, signature); - - function encodeAxiom(bytes32 message, bytes32 signature, bytes32 result) internal returns (bool) => - encodeAxiomSummary(message, signature, result); - - //function encodeSigningMessage(bytes32,bytes calldata,string calldata) internal returns(bytes memory) => NONDET; - function castSignature(bytes calldata signature) external returns (bool, WebAuthn.Signature calldata); -} - -function getEncodeClientDataJsonSummaryCVL(bytes32 message, string signature) returns string -{ - env e; - return getEncodeClientDataJsonSummary(e, message, signature); -} -ghost encodeAxiomSummary(bytes32, bytes32, bytes32) returns bool { - axiom forall bytes32 x1. forall bytes32 y1. forall bytes32 x2. forall bytes32 y2. forall bytes32 z. - (x1 != x2 || y1 != y2) => !(encodeAxiomSummary(x1, y1, z) && encodeAxiomSummary(x2, y2, z)); -} - -// Inner Check for Summarization +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ encodeClientDataJsonIntegrity 2 different challenges results in 2 different clientDataJson (Violated) │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ rule encodeClientDataJsonIntegrity(){ env e; From 8643c6f7c8b4f60ffac9e550e89f2271ab31c5ed Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Wed, 29 May 2024 12:28:10 +0300 Subject: [PATCH 045/103] Improve consistent rule for cast signature --- certora/specs/WebAuthn.spec | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/certora/specs/WebAuthn.spec b/certora/specs/WebAuthn.spec index eeae6ee2d..166434d12 100644 --- a/certora/specs/WebAuthn.spec +++ b/certora/specs/WebAuthn.spec @@ -101,6 +101,11 @@ rule verifySignatureEq(){ rule castSignatureConsistent(){ env e; + env e1; + env e2; + + require (e1.msg.value == e2.msg.value) && (e1.msg.value == e.msg.value) && (e.msg.value == 0); + method f; calldataarg args; @@ -112,19 +117,19 @@ rule castSignatureConsistent(){ bool secondIsValid; WebAuthn.Signature secondData; - firstIsValid, firstData = castSignature@withrevert(e, signature); + firstIsValid, firstData = castSignature@withrevert(e1, signature); bool firstRevert = lastReverted; f(e, args); - secondIsValid, secondData = castSignature@withrevert(e, signature); + secondIsValid, secondData = castSignature@withrevert(e2, signature); bool secondRevert = lastReverted; if (!firstRevert && !secondRevert) { assert compareSignatures(e, firstData, secondData) && firstIsValid == secondIsValid; } - assert firstRevert == secondRevert; + assert (firstRevert == secondRevert); } From 9fa9ac8c734ab6850f1e264feddbe6022f233e0a Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Wed, 29 May 2024 12:39:40 +0300 Subject: [PATCH 046/103] Split between BitVector theory conf and regular --- certora/confs/WebAuthn.conf | 1 - certora/confs/WebAuthnBitVectorTheory.conf | 25 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 certora/confs/WebAuthnBitVectorTheory.conf diff --git a/certora/confs/WebAuthn.conf b/certora/confs/WebAuthn.conf index 285439bef..06ce73680 100644 --- a/certora/confs/WebAuthn.conf +++ b/certora/confs/WebAuthn.conf @@ -12,7 +12,6 @@ "@safe-global=node_modules/@safe-global", "@account-abstraction=node_modules/@account-abstraction" ], - "prover_args": ["-useBitVectorTheory"], "solc": "solc8.23", "solc_via_ir": false, "optimistic_loop": true, diff --git a/certora/confs/WebAuthnBitVectorTheory.conf b/certora/confs/WebAuthnBitVectorTheory.conf new file mode 100644 index 000000000..285439bef --- /dev/null +++ b/certora/confs/WebAuthnBitVectorTheory.conf @@ -0,0 +1,25 @@ +{ + "assert_autofinder_success": true, + "files": [ + "certora/harnesses/WebAuthnHarness.sol" + ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], + "msg": "sanity_with_all_default_summaries", + "process": "emv", + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "prover_args": ["-useBitVectorTheory"], + "solc": "solc8.23", + "solc_via_ir": false, + "optimistic_loop": true, + "optimistic_hashing": true, + "prover_version": "master", + "server": "production", + "rule_sanity": "basic", + "loop_iter": "6", + "verify": "WebAuthnHarness:certora/specs/WebAuthn.spec" +} \ No newline at end of file From a2ef981cc0c10bfc2c24006001b178106ca1a2c8 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Wed, 29 May 2024 13:47:06 +0300 Subject: [PATCH 047/103] Replace cast signature rules --- certora/harnesses/WebAuthnHarness.sol | 4 +++ certora/specs/WebAuthn.spec | 45 ++++++++++++++++++--------- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/certora/harnesses/WebAuthnHarness.sol b/certora/harnesses/WebAuthnHarness.sol index 26bad09ed..e56c8430c 100644 --- a/certora/harnesses/WebAuthnHarness.sol +++ b/certora/harnesses/WebAuthnHarness.sol @@ -20,6 +20,10 @@ contract WebAuthnHarness { return true; } + function encodeSignature(WebAuthn.Signature calldata sig) external pure returns (bytes memory signature){ + signature = abi.encode(sig); + } + function castSignature(bytes calldata signature) external pure returns (bool isValid, WebAuthn.Signature calldata data){ return WebAuthn.castSignature(signature); } diff --git a/certora/specs/WebAuthn.spec b/certora/specs/WebAuthn.spec index 166434d12..f3abcbc47 100644 --- a/certora/specs/WebAuthn.spec +++ b/certora/specs/WebAuthn.spec @@ -132,30 +132,45 @@ rule castSignatureConsistent(){ assert (firstRevert == secondRevert); } +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ CastSignature Canonical Deterministic Decoding (Violated) | +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ + +rule castSignatureDeterministicDecoding(){ + env e; + + WebAuthn.Signature structSignature; + bytes encodeSig = encodeSignature(e, structSignature); + + WebAuthn.Signature decodedSignature; + bool isValid; + + isValid, decodedSignature = castSignature(e, encodeSig); + + assert isValid <=> compareSignatures(e, structSignature, decodedSignature); +} + /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ CastSignature uniqueness (violated) | +│ CastSignature Length Check Validity | └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ -rule castSignatureUniqueness(){ - +rule castSignatureLengthCheckValidity(){ env e; - bytes signature1; - bytes signature2; - - bool firstIsValid; - WebAuthn.Signature firstData; - - bool secondIsValid; - WebAuthn.Signature secondData; + WebAuthn.Signature structSignature; + bytes encodeSig; - firstIsValid, firstData = castSignature(e, signature1); + WebAuthn.Signature decodedSignature; + bool isValid; - secondIsValid, secondData = castSignature(e, signature2); + isValid, decodedSignature = castSignature(e, encodeSig); - assert ((getSha256(e, signature1) != getSha256(e, signature2)) && - ((firstIsValid && secondIsValid))) => !compareSignatures(e, firstData, secondData); + assert compareSignatures(e, structSignature, decodedSignature) => ( + isValid <=> encodeSig.length <= encodeSignature(e, structSignature).length + ); } \ No newline at end of file From a932fab34bb9287e33af773354a5e91abec01f52 Mon Sep 17 00:00:00 2001 From: liav-certora Date: Thu, 30 May 2024 00:49:49 +0300 Subject: [PATCH 048/103] . --- certora/confs/SafeWebAuthnSignerProxy.conf | 2 - certora/specs/SafeWebAuthnSignerProxy.spec | 71 ++++++++++------------ 2 files changed, 31 insertions(+), 42 deletions(-) diff --git a/certora/confs/SafeWebAuthnSignerProxy.conf b/certora/confs/SafeWebAuthnSignerProxy.conf index 4ac4342b6..e6880f9f0 100644 --- a/certora/confs/SafeWebAuthnSignerProxy.conf +++ b/certora/confs/SafeWebAuthnSignerProxy.conf @@ -3,8 +3,6 @@ "files": [ "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol", "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", - //"certora/harnesses/ERC20Like/DummyWeth.sol", - //"certora/harnesses/Utilities.sol", "modules/passkey/contracts/libraries/P256.sol" ], "link": [ diff --git a/certora/specs/SafeWebAuthnSignerProxy.spec b/certora/specs/SafeWebAuthnSignerProxy.spec index 0f665b620..65532fa5d 100644 --- a/certora/specs/SafeWebAuthnSignerProxy.spec +++ b/certora/specs/SafeWebAuthnSignerProxy.spec @@ -1,50 +1,13 @@ -/* Setup Artifacts -import "ERC20/erc20cvl.spec"; -import "ERC20/WETHcvl.spec"; -import "ERC721/erc721.spec"; -import "ERC1967/erc1967.spec"; -import "PriceAggregators/chainlink.spec"; -import "PriceAggregators/tellor.spec"; - -import "spec_utils/problems.spec"; -import "spec_utils/unresolved.spec"; -import "spec_utils/optimizations.spec"; - -import "spec_utils/generic.spec"; // pick additional rules from here - -use builtin rule sanity filtered { f -> f.contract == currentContract } - -use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } -use builtin rule msgValueInLoopRule; -use builtin rule viewReentrancy; -use rule privilegedOperation filtered { f -> f.contract == currentContract } -use rule timeoutChecker filtered { f -> f.contract == currentContract } -use rule simpleFrontRunning filtered { f -> f.contract == currentContract } -use rule noRevert filtered { f -> f.contract == currentContract } -use rule alwaysRevert filtered { f -> f.contract == currentContract } -*/ using SafeWebAuthnSignerSingleton as SafeWebAuthnSignerSingleton; +persistent ghost uint delegateSuccess; + hook DELEGATECALL(uint g, address addr, uint argsOffset, uint argsLength, uint retOffset, uint retLength) uint rc { // DELEGATECALL is used in this contract, but it only ever calls into the singleton. assert (executingContract != currentContract || addr == SafeWebAuthnSignerSingleton, "we should only `delegatecall` into the singleton." ); -} - -/* -Property 12. Proxy - Delegate Call Integrity (calls the Singleton) -Hooking on delegate calls will make sure we'll get a violation if the singleton isn't the contract called. -Rule verified. -*/ -rule delegateCallsOnlyToSingleton { - env e; - method f; - calldataarg args; - - f(e, args); - - assert true; + delegateSuccess = rc; } /* @@ -74,3 +37,31 @@ rule configParametersImmutability { yBefore == yAfter && verifiersBefore == verifiersAfter; } + +/* +Property 12. Proxy - Delegate Call Integrity (calls the Singleton) +Hooking on delegate calls will make sure we'll get a violation if the singleton isn't the contract called. +Rule verified. +*/ +rule delegateCallsOnlyToSingleton { + env e; + method f; + calldataarg args; + + f(e, args); + + assert true; +} + +/* +Property 13. Proxy - Fallback reverting conditions. +Fallback reverts iff the delegatecall didn't succeed. Data manipulation does not revert. +Rule verified. +*/ +rule fallbackRevertingConditions(method f, calldataarg args) filtered { f -> f.isFallback } { + env e; + + f@withrevert(e, args); + + assert lastReverted <=> delegateSuccess == 0; +} From 6c7348ef0bb7c542f652e31671442a538b76ebe3 Mon Sep 17 00:00:00 2001 From: yoavelmalem Date: Thu, 30 May 2024 17:40:57 +0300 Subject: [PATCH 049/103] Added summaries and additional fixes --- .../confs/SafeWebAuthnSignerSingleton.conf | 8 ++ .../SafeWebAuthnSignerSingletonHarness.sol | 4 + certora/harnesses/WebAuthnHarness.sol | 17 ++++ .../specs/SafeWebAuthnSignerSingleton.spec | 84 ++++++++++++++----- 4 files changed, 92 insertions(+), 21 deletions(-) diff --git a/certora/confs/SafeWebAuthnSignerSingleton.conf b/certora/confs/SafeWebAuthnSignerSingleton.conf index ccea556a2..e9ce50e04 100644 --- a/certora/confs/SafeWebAuthnSignerSingleton.conf +++ b/certora/confs/SafeWebAuthnSignerSingleton.conf @@ -2,8 +2,13 @@ "assert_autofinder_success": true, "files": [ "modules/passkey/contracts/SafeWebAuthnSignerFactory.sol", + "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol", "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", + "modules/passkey/contracts/libraries/WebAuthn.sol", + "modules/passkey/contracts/libraries/P256.sol", + "modules/passkey/contracts/libraries/ERC1271.sol", "certora/harnesses/SafeWebAuthnSignerSingletonHarness.sol", + "certora/harnesses/WebAuthnHarness.sol", ], "java_args": [ " -ea -Dlevel.setup.helpers=info" @@ -12,14 +17,17 @@ "process": "emv", "prover_args": [ " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" + //" -useBitVectorTheory" ], "packages":[ "@safe-global=node_modules/@safe-global", "@account-abstraction=node_modules/@account-abstraction" ], "solc": "solc8.23", + "rule_sanity": "basic", "solc_via_ir": false, "optimistic_loop": true, + "loop_iter": "6", "optimistic_hashing": true, "prover_version": "master", "server": "production", diff --git a/certora/harnesses/SafeWebAuthnSignerSingletonHarness.sol b/certora/harnesses/SafeWebAuthnSignerSingletonHarness.sol index b47960248..cddb033e0 100644 --- a/certora/harnesses/SafeWebAuthnSignerSingletonHarness.sol +++ b/certora/harnesses/SafeWebAuthnSignerSingletonHarness.sol @@ -19,4 +19,8 @@ contract SafeWebAuthnSignerSingletonHarness is SafeWebAuthnSignerSingleton { (uint256 x, uint256 y, P256.Verifiers verifiers) = getConfiguration(); success = WebAuthn.verifySignature(message, signature, WebAuthn.USER_VERIFICATION, x, y, verifiers); } + + function getConfigurationHarnessed(bytes32 message, bytes calldata signature) public pure returns (uint256 new_x, uint256 new_y, P256.Verifiers new_verifiers) { + (uint256 new_x, uint256 new_y, P256.Verifiers new_verifiers) = getConfiguration(); + } } diff --git a/certora/harnesses/WebAuthnHarness.sol b/certora/harnesses/WebAuthnHarness.sol index e56c8430c..a343eb3bf 100644 --- a/certora/harnesses/WebAuthnHarness.sol +++ b/certora/harnesses/WebAuthnHarness.sol @@ -89,5 +89,22 @@ contract WebAuthnHarness { function getSha256(bytes memory input) public view returns (bytes32 digest) { return WebAuthn._sha256(input); } + + mapping (bytes32 => mapping (bytes32 => mapping (bytes32 => bytes))) symbolicMessageSummary; + + function GETencodeSigningMessageSummary(bytes32 challenge, bytes calldata authenticatorData, string calldata clientDataFields) public returns (bytes memory){ + bytes32 hashed_authenticatorData = keccak256(authenticatorData); + bytes32 hashed_clientDataFields = keccak256(abi.encodePacked(clientDataFields)); + + bytes memory bytes_mapping = symbolicMessageSummary[challenge][hashed_authenticatorData][hashed_clientDataFields]; + + require (checkInjective(challenge, hashed_authenticatorData, hashed_clientDataFields, keccak256(bytes_mapping))); + + return bytes_mapping; + } + + function checkInjective(bytes32 challenge, bytes32 authenticatorData, bytes32 clientDataFields, bytes32 result) internal view returns (bool){ + return true; + } } diff --git a/certora/specs/SafeWebAuthnSignerSingleton.spec b/certora/specs/SafeWebAuthnSignerSingleton.spec index 54cb25549..f6192e82e 100644 --- a/certora/specs/SafeWebAuthnSignerSingleton.spec +++ b/certora/specs/SafeWebAuthnSignerSingleton.spec @@ -1,3 +1,33 @@ +using SafeWebAuthnSignerProxy as proxy; +using WebAuthnHarness as WebAuthnHarness; + +methods { + function P256.verifySignatureAllowMalleability(P256.Verifiers a, bytes32 b, uint256 c, uint256 d, uint256 e, uint256 f) internal returns (bool) => + verifySignatureAllowMalleabilityGhost(a, b, c, d, e, f); + + function WebAuthn.encodeSigningMessage(bytes32 challenge, bytes calldata authenticatorData, string calldata clientDataFields) internal returns (bytes memory) => + GETencodeSigningMessageCVL(challenge, authenticatorData, clientDataFields); + + function WebAuthnHarness.checkInjective(bytes32 challenge, bytes32 authenticatorData, bytes32 clientDataFields, bytes32 result) internal returns (bool) => + checkInjectiveSummary(challenge, authenticatorData, clientDataFields, result); +} + +function GETencodeSigningMessageCVL(bytes32 challenge, bytes authenticatorData, string clientDataFields) returns bytes +{ + env e; + return WebAuthnHarness.GETencodeSigningMessageSummary(e, challenge, authenticatorData, clientDataFields); +} + +ghost checkInjectiveSummary(bytes32, bytes32, bytes32, bytes32) returns bool { + axiom forall bytes32 x1. forall bytes32 y1. forall bytes32 z1. forall bytes32 x2. forall bytes32 y2. forall bytes32 z2. forall bytes32 result. + checkInjectiveSummary(x1, y1, z1, result) && checkInjectiveSummary(x2, y2, z2, result) => x1 == x2; +} + +ghost verifySignatureAllowMalleabilityGhost(P256.Verifiers, bytes32, uint256, uint256, uint256, uint256) returns bool { + axiom forall P256.Verifiers a. forall bytes32 message1. forall bytes32 message2. forall uint256 c. forall uint256 d. forall uint256 e. forall uint256 f. + verifySignatureAllowMalleabilityGhost(a, message1, c, d, e, f) && + verifySignatureAllowMalleabilityGhost(a, message2, c, d, e, f) => message1 == message2; +} /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ @@ -5,39 +35,52 @@ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ -rule verifySignatureIntegrity(env e){ +rule verifySignatureUniqueness(env e){ bytes32 first_message; + bytes32 second_message; bytes signature; + require (first_message != second_message); + bool first_message_verified = verifySignatureHarnessed(e, first_message, signature); + bool second_message_verified = verifySignatureHarnessed(e, second_message, signature); + + assert !(first_message_verified && second_message_verified); +} +rule verifySignatureIntegrity(env e){ + bytes32 first_message; bytes32 second_message; + bytes signature; + + bool first_message_verified = verifySignatureHarnessed(e, first_message, signature); + require (first_message_verified); + bool second_message_verified = verifySignatureHarnessed(e, second_message, signature); - assert (first_message_verified == true && second_message_verified == true) => first_message == second_message; - assert first_message == second_message => first_message_verified == second_message_verified; + assert second_message_verified <=> first_message == second_message; } - /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ getConfiguration Function (Integrity) │ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ + // TODO Not Completed Yet // rule verifyGetConfigurationIntegrity(env e){ +// bytes32 data; +// bytes first_signature; // uint256 x; // uint256 y; // P256.Verifiers verifiers; // uint256 new_x; uint256 new_y; P256.Verifiers new_verifiers; -// bytes32 message; -// // bytes data = assignValues(e, x, y, verifiers); -// (new_x, new_y, new_verifiers) = temp(e, x, y, verifiers); - -// // (x, y, verifiers) = getConfiguration(e); -// // (new_x, new_y, new_verifiers) = getConfigurationHarnessed(e, data); +// x = proxy.getX(); +// y = proxy.getY(); +// verifiers = proxy.getVerifiers(); +// (new_x, new_y, new_verifiers) = proxy.getConfiguration(e); // assert x == new_x; // assert y == new_y; @@ -45,7 +88,6 @@ rule verifySignatureIntegrity(env e){ // satisfy true; // } - /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Both is valid Signature behave the same way │ @@ -56,17 +98,16 @@ rule verifyIsValidSignatureAreEqual(env e){ bytes data; bytes first_signature; - bool hashed_data_verified = verifySignatureHarnessed(e, keccak256(data), first_signature); + bytes4 magicValue_hashed = isValidSignature(e, data, first_signature); - bytes second_signature; bytes32 message; - bool message_verified = verifySignatureHarnessed(e, message, second_signature); + bytes4 magicValue_message = isValidSignature(e, message, first_signature); - assert (hashed_data_verified == true && message_verified == true) => message == keccak256(data); - assert message == keccak256(data) => hashed_data_verified == message_verified; + assert (magicValue_hashed == to_bytes4(0x20c13b0b) && magicValue_message == to_bytes4(0x1626ba7e)) => message == keccak256(data); + assert message == keccak256(data) => (magicValue_hashed == to_bytes4(0x20c13b0b) && magicValue_message == to_bytes4(0x1626ba7e)) || + (magicValue_hashed != to_bytes4(0x20c13b0b) && magicValue_message != to_bytes4(0x1626ba7e)); } - /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Once signer passed isValidSignature it will never fail on it after any call │ @@ -74,24 +115,27 @@ rule verifyIsValidSignatureAreEqual(env e){ */ rule verifyIsValidSignatureWillContinueToSucceed(env e){ + method f; + calldataarg args; bytes32 message; bytes signature; bool first_verified = verifySignatureHarnessed(e, message, signature); require first_verified == true; + f(e, args); + bool second_verified = verifySignatureHarnessed(e, message, signature); assert second_verified; } - /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Once isValidSignature failed, it will never pass before createSigner called │ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ -rule IsValidSignatureMustSucceedAfterCreation(env e){ +rule IsValidSignatureWillSucceedOnlyAfterCreation(env e){ method f; calldataarg args; @@ -103,8 +147,6 @@ rule IsValidSignatureMustSucceedAfterCreation(env e){ f(e, args); - bool second_verified = verifySignatureHarnessed(e, message, signature); assert second_verified => f.selector == sig:SafeWebAuthnSignerFactory.createSigner(uint256, uint256, P256.Verifiers).selector; } - From 82cecef4edb27568bdd51281c3e48a48b67c72fd Mon Sep 17 00:00:00 2001 From: yoavelmalem Date: Thu, 30 May 2024 18:03:21 +0300 Subject: [PATCH 050/103] Added Jochen suggestion --- certora/harnesses/WebAuthnHarness.sol | 2 +- certora/specs/WebAuthn.spec | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/certora/harnesses/WebAuthnHarness.sol b/certora/harnesses/WebAuthnHarness.sol index a343eb3bf..3bce872a1 100644 --- a/certora/harnesses/WebAuthnHarness.sol +++ b/certora/harnesses/WebAuthnHarness.sol @@ -21,7 +21,7 @@ contract WebAuthnHarness { } function encodeSignature(WebAuthn.Signature calldata sig) external pure returns (bytes memory signature){ - signature = abi.encode(sig); + signature = abi.encode(sig.authenticatorData, sig.clientDataFields, sig.r, sig.s); } function castSignature(bytes calldata signature) external pure returns (bool isValid, WebAuthn.Signature calldata data){ diff --git a/certora/specs/WebAuthn.spec b/certora/specs/WebAuthn.spec index f3abcbc47..ae670ea88 100644 --- a/certora/specs/WebAuthn.spec +++ b/certora/specs/WebAuthn.spec @@ -134,7 +134,7 @@ rule castSignatureConsistent(){ /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ CastSignature Canonical Deterministic Decoding (Violated) | +│ CastSignature Canonical Deterministic Decoding (Violated (Timeout CERT-6341)) | └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ @@ -173,4 +173,4 @@ rule castSignatureLengthCheckValidity(){ assert compareSignatures(e, structSignature, decodedSignature) => ( isValid <=> encodeSig.length <= encodeSignature(e, structSignature).length ); -} \ No newline at end of file +} From f9251a91df5b6dd6c43679545ea372847c80fa45 Mon Sep 17 00:00:00 2001 From: liav-certora Date: Thu, 30 May 2024 19:50:51 +0300 Subject: [PATCH 051/103] . --- certora/harnesses/WebAuthnHarness.sol | 4 + certora/specs/WebAuthn.spec | 101 ++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/certora/harnesses/WebAuthnHarness.sol b/certora/harnesses/WebAuthnHarness.sol index e56c8430c..7465c8abd 100644 --- a/certora/harnesses/WebAuthnHarness.sol +++ b/certora/harnesses/WebAuthnHarness.sol @@ -28,6 +28,10 @@ contract WebAuthnHarness { return WebAuthn.castSignature(signature); } + function castSignature_notreturns(bytes calldata signature) external pure { + WebAuthn.castSignature(signature); + } + function compareStrings(string memory str1, string memory str2) public view returns (bool) { bytes memory str1Bytes = bytes(str1); bytes memory str2Bytes = bytes(str2); diff --git a/certora/specs/WebAuthn.spec b/certora/specs/WebAuthn.spec index f3abcbc47..b892cfe59 100644 --- a/certora/specs/WebAuthn.spec +++ b/certora/specs/WebAuthn.spec @@ -173,4 +173,105 @@ rule castSignatureLengthCheckValidity(){ assert compareSignatures(e, structSignature, decodedSignature) => ( isValid <=> encodeSig.length <= encodeSignature(e, structSignature).length ); +} + +/* +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ castSignature Reverting Conditions | +| Will only revert if function was paid. | +| Passes - https://prover.certora.com/output/15800/9d6be0f24e094ffe94b9faf1ed8bfc8f?anonymousKey=517b64e4e1693de5294f836400a5581fc7ec0bcf | +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ +rule castSignatureRevertingConditions { + env e; + bytes signature; + + bool triedTransferringEth = e.msg.value != 0; + + castSignature_notreturns@withrevert(e, signature); + + assert lastReverted <=> triedTransferringEth; +} + +/* +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ encodeClientDataJson Reverting Conditions | +| Will only revert if function was paid. | +| Passes - https://prover.certora.com/output/15800/ccc8d2fd45b04cf5ac8efdf263820324?anonymousKey=68d9ae2e6f4f22dd7aae9f8bddbc5faf7de12df1 | +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ +rule encodeClientDataJsonRevertingConditions { + env e; + bytes32 challenge; + string clientDataFields; + + bool triedTransferringEth = e.msg.value != 0; + + encodeClientDataJson@withrevert(e, challenge, clientDataFields); + + assert lastReverted <=> triedTransferringEth; +} + +/* +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ encodeSigningMessage Reverting Conditions | +| Will only revert if function was paid. | +| Passes - https://prover.certora.com/output/15800/93685eaf7e7146eabaa38125dc32f29b?anonymousKey=eaf6d4135849f0476d8e9e6a758cca8818a96b94 | +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ +rule encodeSigningMessageRevertingConditions { + env e; + bytes32 challenge; + bytes authenticatorData; + string clientDataFields; + + bool triedTransferringEth = e.msg.value != 0; + + encodeSigningMessage@withrevert(e, challenge, authenticatorData, clientDataFields); + + assert lastReverted <=> triedTransferringEth; +} + +/* +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ checkAuthenticatorFlags Reverting Conditions | +| Will only revert if function was paid or the bytes array `authenticatorData`'s length is too small (<= 32 bytes). | +| Passes - https://prover.certora.com/output/15800/d2d34792998c479db5f38430efc7dc8b?anonymousKey=7e68511768f3da35bdb9f75c9f86d40d2e07e2aa | +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ +rule checkAuthenticatorFlagsRevertingConditions { + env e; + bytes authenticatorData; + WebAuthn.AuthenticatorFlags authenticatorFlags; + + bool triedTransferringEth = e.msg.value != 0; + bool dataLengthInsufficient = authenticatorData.length <= 32; + + checkAuthenticatorFlags@withrevert(e, authenticatorData, authenticatorFlags); + + assert lastReverted <=> (triedTransferringEth || dataLengthInsufficient); +} + +/* +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ verifySignature Reverting Conditions | +| Will only revert if function was paid or the bytes array `authenticatorData`'s in `signature` length is too small (<= 32 bytes). | +| Passes - https://prover.certora.com/output/15800/d2d34792998c479db5f38430efc7dc8b?anonymousKey=7e68511768f3da35bdb9f75c9f86d40d2e07e2aa | +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ +rule verifySignatureRevertingConditions { + env e; + bytes32 challenge; + WebAuthn.Signature signature; + WebAuthn.AuthenticatorFlags authenticatorFlags; + uint256 x; + uint256 y; + P256.Verifiers verifiers; + + bool triedTransferringEth = e.msg.value != 0; + bool dataLengthInsufficient = signature.authenticatorData.length <= 32; + + verifySignature@withrevert(e, challenge, signature, authenticatorFlags, x, y, verifiers); + + assert lastReverted <=> (triedTransferringEth || dataLengthInsufficient); } \ No newline at end of file From 3983d89bef001a4e3d3f69ae46bf767c344f7886 Mon Sep 17 00:00:00 2001 From: yoavelmalem Date: Mon, 3 Jun 2024 11:39:10 +0300 Subject: [PATCH 052/103] Small updates and improved castSignatureLengthCheckValidity condition --- certora/confs/SafeWebAuthnSignerSingleton.conf | 2 -- certora/specs/SafeWebAuthnSignerSingleton.spec | 1 - certora/specs/WebAuthn.spec | 5 ++--- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/certora/confs/SafeWebAuthnSignerSingleton.conf b/certora/confs/SafeWebAuthnSignerSingleton.conf index e9ce50e04..5082a520f 100644 --- a/certora/confs/SafeWebAuthnSignerSingleton.conf +++ b/certora/confs/SafeWebAuthnSignerSingleton.conf @@ -2,11 +2,9 @@ "assert_autofinder_success": true, "files": [ "modules/passkey/contracts/SafeWebAuthnSignerFactory.sol", - "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol", "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", "modules/passkey/contracts/libraries/WebAuthn.sol", "modules/passkey/contracts/libraries/P256.sol", - "modules/passkey/contracts/libraries/ERC1271.sol", "certora/harnesses/SafeWebAuthnSignerSingletonHarness.sol", "certora/harnesses/WebAuthnHarness.sol", ], diff --git a/certora/specs/SafeWebAuthnSignerSingleton.spec b/certora/specs/SafeWebAuthnSignerSingleton.spec index f6192e82e..415023104 100644 --- a/certora/specs/SafeWebAuthnSignerSingleton.spec +++ b/certora/specs/SafeWebAuthnSignerSingleton.spec @@ -1,4 +1,3 @@ -using SafeWebAuthnSignerProxy as proxy; using WebAuthnHarness as WebAuthnHarness; methods { diff --git a/certora/specs/WebAuthn.spec b/certora/specs/WebAuthn.spec index ae670ea88..2f0748054 100644 --- a/certora/specs/WebAuthn.spec +++ b/certora/specs/WebAuthn.spec @@ -169,8 +169,7 @@ rule castSignatureLengthCheckValidity(){ bool isValid; isValid, decodedSignature = castSignature(e, encodeSig); + bool length_is_correct = encodeSig.length <= encodeSignature(e, decodedSignature).length; - assert compareSignatures(e, structSignature, decodedSignature) => ( - isValid <=> encodeSig.length <= encodeSignature(e, structSignature).length - ); + assert isValid <=> length_is_correct; } From 6a21c3fb59d97c20a6be842913a5df2b1bb4c68f Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Tue, 4 Jun 2024 11:40:31 +0300 Subject: [PATCH 053/103] Fix uniqueSigner and createAndGetSignerEquivalence rules --- certora/confs/WebAuthnBitVectorTheory.conf | 1 + .../SafeWebAuthnSignerFactoryHarness.sol | 17 +++++ certora/specs/SafeWebAuthnSignerFactory.spec | 68 +++++++++++++++++-- certora/specs/WebAuthn.spec | 5 +- .../contracts/SafeWebAuthnSignerFactory.sol | 1 + 5 files changed, 84 insertions(+), 8 deletions(-) diff --git a/certora/confs/WebAuthnBitVectorTheory.conf b/certora/confs/WebAuthnBitVectorTheory.conf index 285439bef..7b928b6fa 100644 --- a/certora/confs/WebAuthnBitVectorTheory.conf +++ b/certora/confs/WebAuthnBitVectorTheory.conf @@ -13,6 +13,7 @@ "@account-abstraction=node_modules/@account-abstraction" ], "prover_args": ["-useBitVectorTheory"], + "rule": ["encodeClientDataJsonIntegrity", "uniqueMessagePerChallenge"], "solc": "solc8.23", "solc_via_ir": false, "optimistic_loop": true, diff --git a/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol b/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol index 8c9fd02f2..0321274e4 100644 --- a/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol +++ b/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol @@ -29,4 +29,21 @@ contract SafeWebAuthnSignerFactoryHarness is SafeWebAuthnSignerFactory { require(success); magicValue = abi.decode(result, (bytes4)); } + + function getSignerHarnessed(uint256 x, uint256 y, P256.Verifiers verifiers) public view returns (uint256 value) { + bytes32 codeHash = keccak256( + abi.encodePacked( + type(SafeWebAuthnSignerProxy).creationCode, + "01234567891011121314152546", + uint256(uint160(address(SINGLETON))), + x, + y, + uint256(P256.Verifiers.unwrap(verifiers)) + ) + ); + value = uint256(keccak256(abi.encodePacked(hex"ff", address(this), bytes32(0), codeHash))); + } + function castToAddress(uint256 value) public pure returns (address addr){ + addr = address(uint160(value)); + } } diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index 731f268c4..c0e6c7e72 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -2,7 +2,8 @@ using SafeWebAuthnSignerProxy as proxy; using SafeWebAuthnSignerSingleton as singleton; methods{ - function getSigner(uint256, uint256, P256.Verifiers) external returns (address); + // function getSigner(uint256, uint256, P256.Verifiers) external returns (address); + // function getSigner(uint256 x, uint256 y, P256.Verifiers v) internal returns (address) => getSignerGhost(x, y, v); function createSigner(uint256, uint256, P256.Verifiers) external returns (address); function hasNoCode(address) external returns (bool) envfree; } @@ -30,10 +31,51 @@ rule singletonNeverChanges() /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ getSigner is unique for every x,y and verifier combination (Violated but low prob) │ + getSigner is unique for every x,y and verifier combination, proved with assumptions: + 1.) value before cast to address <= max_uint160. + 2.) munging required to complete signer data to be constructed from full 32bytes size arrays + function getSignerHarnessed(uint256 x, uint256 y, P256.Verifiers verifiers) public view returns (uint256 value) { + bytes32 codeHash = keccak256( + abi.encodePacked( + type(SafeWebAuthnSignerProxy).creationCode, + "01234567891011121314152546", <--------------- HERE! + uint256(uint160(address(SINGLETON))), + x, + y, + uint256(P256.Verifiers.unwrap(verifiers)) + ) + ); + value = uint256(keccak256(abi.encodePacked(hex"ff", address(this), bytes32(0), codeHash))); + } └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ -// consider adding the following munging after the creationcode to get a more clear dump 01234567891011121314152546 + +// helper rule to justify the use of the munged implementation (proved) +rule mungedEquivalence() +{ + env e1; + env e2; + + require e1.msg.value == 0 && e2.msg.value == 0; + uint256 x; + uint256 y; + P256.Verifiers verifier; + + storage s = lastStorage; + + uint256 harnessedSignerValue = getSignerHarnessed@withrevert(e1, x, y, verifier); + bool harnessedSignerRevert1 = lastReverted; + + address harnessedSigner = castToAddress@withrevert(e1, harnessedSignerValue); + bool harnessedSignerRevert2 = harnessedSignerRevert1 && lastReverted; + + address signer = getSigner@withrevert(e2, x, y, verifier) at s; + bool signerRevert = lastReverted; + + assert (harnessedSignerRevert2 == signerRevert); + assert (!harnessedSignerRevert2 && !signerRevert) => (harnessedSigner == signer); +} + rule uniqueSigner(){ env e; @@ -42,23 +84,37 @@ rule uniqueSigner(){ uint256 firstY; P256.Verifiers firstVerifier; - address firstSigner = getSigner(e, firstX, firstY, firstVerifier); + uint256 firstSignerValue = getSignerHarnessed(e, firstX, firstY, firstVerifier); + require firstSignerValue <= max_uint160; + + address firstSigner = castToAddress(e, firstSignerValue); uint256 secondX; uint256 secondY; P256.Verifiers secondVerifier; - address secondSigner = getSigner(e, secondX, secondY, secondVerifier); + uint256 secondSignerValue = getSignerHarnessed(e, secondX, secondY, secondVerifier); + require secondSignerValue <= max_uint160; + + address secondSigner = castToAddress(e, secondSignerValue); + assert firstSigner == secondSigner <=> (firstX == secondX && firstY == secondY && firstVerifier == secondVerifier); } /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ createSigner and getSigner always returns the same address (Violated but low prob) │ +│ createSigner and getSigner always returns the same address (Proved under assumption) │ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ +// Summary is correct only if the unique signer rule is proved !!! +ghost getSignerGhost(uint256, uint256, P256.Verifiers) returns address { + axiom forall uint256 x1. forall uint256 y1. forall P256.Verifiers v1. + forall uint256 x2. forall uint256 y2. forall P256.Verifiers v2. + (getSignerGhost(x1, y1, v1) == getSignerGhost(x2, y2, v2)) <=> (x1 == x2 && y1 == y2 && v1 == v2); +} + rule createAndGetSignerEquivalence(){ env e; diff --git a/certora/specs/WebAuthn.spec b/certora/specs/WebAuthn.spec index f3abcbc47..efc64ed45 100644 --- a/certora/specs/WebAuthn.spec +++ b/certora/specs/WebAuthn.spec @@ -86,10 +86,11 @@ rule verifySignatureEq(){ bool result1 = verifySignature@withrevert(e, challenge, bytesSignature, authenticatorFlags, x, y, verifiers); bool firstCallRevert = lastReverted; - bool result2 = verifySignature@withrevert(e, challenge, bytesSignature, authenticatorFlags, x, y, verifiers) at firstStorage; + bool result2 = verifySignature@withrevert(e, challenge, structSignature, authenticatorFlags, x, y, verifiers) at firstStorage; bool secondCallRevert = lastReverted; - assert (firstCallRevert == secondCallRevert) || (result1 == result2); + assert firstCallRevert == secondCallRevert; + assert (!firstCallRevert && !secondCallRevert) => result1 == result2; } /* diff --git a/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol b/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol index 26d9f54c0..629fc574a 100644 --- a/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol +++ b/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol @@ -36,6 +36,7 @@ contract SafeWebAuthnSignerFactory is ISafeSignerFactory { bytes32 codeHash = keccak256( abi.encodePacked( type(SafeWebAuthnSignerProxy).creationCode, + "01234567891011121314152546", // munged for word alignment workaround (32 bytes) uint256(uint160(address(SINGLETON))), x, y, From 6ba67428a0541d9c215d56a4d847e35de039d6f6 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Tue, 4 Jun 2024 11:42:18 +0300 Subject: [PATCH 054/103] Add summary for getsiner --- certora/specs/SafeWebAuthnSignerFactory.spec | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index c0e6c7e72..e8972bc07 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -2,8 +2,7 @@ using SafeWebAuthnSignerProxy as proxy; using SafeWebAuthnSignerSingleton as singleton; methods{ - // function getSigner(uint256, uint256, P256.Verifiers) external returns (address); - // function getSigner(uint256 x, uint256 y, P256.Verifiers v) internal returns (address) => getSignerGhost(x, y, v); + function getSigner(uint256 x, uint256 y, P256.Verifiers v) internal returns (address) => getSignerGhost(x, y, v); function createSigner(uint256, uint256, P256.Verifiers) external returns (address); function hasNoCode(address) external returns (bool) envfree; } From 80a64da3adf36ae4da5ac0e0bf1498fc94181edb Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Tue, 4 Jun 2024 11:42:56 +0300 Subject: [PATCH 055/103] Add summary for getsiner --- certora/specs/SafeWebAuthnSignerFactory.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index e8972bc07..5c1088890 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -49,7 +49,7 @@ rule singletonNeverChanges() └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ -// helper rule to justify the use of the munged implementation (proved) +// helper rule to justify the use of the munged implementation (proved) need to drop getSigner summary before execution. rule mungedEquivalence() { env e1; From bde14d64cb4a37b189ea4ff3cde9ac38352b04ab Mon Sep 17 00:00:00 2001 From: yoavelmalem Date: Tue, 4 Jun 2024 15:03:41 +0300 Subject: [PATCH 056/103] Addressed Hristo comments --- certora/specs/SafeWebAuthnSignerSingleton.spec | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/certora/specs/SafeWebAuthnSignerSingleton.spec b/certora/specs/SafeWebAuthnSignerSingleton.spec index 415023104..146f6413a 100644 --- a/certora/specs/SafeWebAuthnSignerSingleton.spec +++ b/certora/specs/SafeWebAuthnSignerSingleton.spec @@ -39,12 +39,10 @@ rule verifySignatureUniqueness(env e){ bytes32 second_message; bytes signature; - require (first_message != second_message); - bool first_message_verified = verifySignatureHarnessed(e, first_message, signature); bool second_message_verified = verifySignatureHarnessed(e, second_message, signature); - assert !(first_message_verified && second_message_verified); + assert (first_message != second_message) => !(first_message_verified && second_message_verified); } rule verifySignatureIntegrity(env e){ @@ -104,7 +102,7 @@ rule verifyIsValidSignatureAreEqual(env e){ assert (magicValue_hashed == to_bytes4(0x20c13b0b) && magicValue_message == to_bytes4(0x1626ba7e)) => message == keccak256(data); assert message == keccak256(data) => (magicValue_hashed == to_bytes4(0x20c13b0b) && magicValue_message == to_bytes4(0x1626ba7e)) || - (magicValue_hashed != to_bytes4(0x20c13b0b) && magicValue_message != to_bytes4(0x1626ba7e)); + (magicValue_hashed == to_bytes4(0) && magicValue_message == to_bytes4(0)); } /* @@ -119,18 +117,20 @@ rule verifyIsValidSignatureWillContinueToSucceed(env e){ bytes32 message; bytes signature; - bool first_verified = verifySignatureHarnessed(e, message, signature); - require first_verified == true; + bool first_verified = verifySignatureHarnessed@withrevert(e, message, signature); + bool first_revert = lastReverted; f(e, args); - bool second_verified = verifySignatureHarnessed(e, message, signature); - assert second_verified; + bool second_verified = verifySignatureHarnessed@withrevert(e, message, signature); + + assert !first_revert && !lastReverted; + assert first_verified => second_verified; } /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ Once isValidSignature failed, it will never pass before createSigner called │ +│ If isValidSignature failed, the only method that can make it pass is createSigner │ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ From d668c4142c163c2f704a133c94cd8ec2ccf91143 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Tue, 4 Jun 2024 15:35:41 +0300 Subject: [PATCH 057/103] Add munging for hasNoCode --- certora/specs/SafeWebAuthnSignerFactory.spec | 17 --------------- .../contracts/SafeWebAuthnSignerFactory.sol | 21 ++++++++++++------- 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index 5c1088890..61964356e 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -159,11 +159,9 @@ rule deterministicSigner() ghost mathint numOfCreation; ghost mapping(address => uint) address_map; -ghost bool validValue; hook EXTCODESIZE(address addr) uint v{ require address_map[addr] == v; - validValue = addr <= max_uint160; } hook CREATE2(uint value, uint offset, uint length, bytes32 salt) address v{ @@ -189,21 +187,6 @@ rule SignerCreationCantOverride() assert numOfCreation < 2; } -rule ValidValue() -{ - env e; - require !validValue; - - uint x; - uint y; - P256.Verifiers verifier; - - createSigner(e, x, y, verifier); - createSigner@withrevert(e, x, y, verifier); - - satisfy validValue; -} - /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Has no code integrity (Proved) │ diff --git a/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol b/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol index 629fc574a..490704cd4 100644 --- a/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol +++ b/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol @@ -87,15 +87,22 @@ contract SafeWebAuthnSignerFactory is ISafeSignerFactory { } } + // /** + // * @dev Checks if the provided account has no code. + // * @param account The address of the account to check. + // * @return result True if the account has no code, false otherwise. + // */ + // function _hasNoCode(address account) internal view returns (bool result) { + // // solhint-disable-next-line no-inline-assembly + // assembly ("memory-safe") { + // result := iszero(extcodesize(account)) + // } + // } + /** - * @dev Checks if the provided account has no code. - * @param account The address of the account to check. - * @return result True if the account has no code, false otherwise. + Munged. */ function _hasNoCode(address account) internal view returns (bool result) { - // solhint-disable-next-line no-inline-assembly - assembly ("memory-safe") { - result := iszero(extcodesize(account)) - } + return account.code.length > 0; } } From 1523c06547d5b667f23c63769973029c43304897 Mon Sep 17 00:00:00 2001 From: yoavelmalem Date: Tue, 4 Jun 2024 15:50:50 +0300 Subject: [PATCH 058/103] revert verifyIsValidSignatureWillContinueToSucceed changes --- certora/specs/SafeWebAuthnSignerSingleton.spec | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/certora/specs/SafeWebAuthnSignerSingleton.spec b/certora/specs/SafeWebAuthnSignerSingleton.spec index 146f6413a..8a9d928d4 100644 --- a/certora/specs/SafeWebAuthnSignerSingleton.spec +++ b/certora/specs/SafeWebAuthnSignerSingleton.spec @@ -117,15 +117,13 @@ rule verifyIsValidSignatureWillContinueToSucceed(env e){ bytes32 message; bytes signature; - bool first_verified = verifySignatureHarnessed@withrevert(e, message, signature); - bool first_revert = lastReverted; + bool first_verified = verifySignatureHarnessed(e, message, signature); + require first_verified; f(e, args); - bool second_verified = verifySignatureHarnessed@withrevert(e, message, signature); - - assert !first_revert && !lastReverted; - assert first_verified => second_verified; + bool second_verified = verifySignatureHarnessed(e, message, signature); + assert second_verified; } /* From a43568dc44df652404360cdc456aab780f5d8017 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Wed, 5 Jun 2024 12:02:28 +0300 Subject: [PATCH 059/103] Fix verifySignatureEq --- certora/harnesses/WebAuthnHarness.sol | 17 ++++++++++++ certora/specs/WebAuthn.spec | 39 ++++++++++++++++----------- 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/certora/harnesses/WebAuthnHarness.sol b/certora/harnesses/WebAuthnHarness.sol index e56c8430c..c7548c0a3 100644 --- a/certora/harnesses/WebAuthnHarness.sol +++ b/certora/harnesses/WebAuthnHarness.sol @@ -4,6 +4,23 @@ pragma solidity ^0.8.0; import {P256, WebAuthn} from "../../modules/passkey/contracts/libraries/WebAuthn.sol"; contract WebAuthnHarness { + + mapping (bytes32 => mapping (bytes32 => string)) symbolicClientDataJson; + + function SencodeDataJson(bytes32 challenge, string calldata clientDataFields) public returns (string memory){ + bytes32 hashClientDataFields = keccak256(abi.encodePacked(clientDataFields)); + string memory stringResult = symbolicClientDataJson[challenge][hashClientDataFields]; + bytes32 hashResult = keccak256(abi.encodePacked(stringResult)); + + require (checkInjective(challenge, hashClientDataFields, hashResult)); + + return stringResult; + } + + function checkInjective(bytes32 challenge, bytes32 clientDataFields, bytes32 result) internal view returns (bool){ + return true; + } + function compareSignatures(WebAuthn.Signature memory sig1, WebAuthn.Signature memory sig2) public pure returns (bool) { if (sig1.r != sig2.r || sig1.s != sig2.s) { return false; diff --git a/certora/specs/WebAuthn.spec b/certora/specs/WebAuthn.spec index efc64ed45..028a8b1c0 100644 --- a/certora/specs/WebAuthn.spec +++ b/certora/specs/WebAuthn.spec @@ -1,22 +1,30 @@ -/* -┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ encodeClientDataJsonIntegrity 2 different challenges results in 2 different clientDataJson (Violated) │ -└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ -*/ -rule encodeClientDataJsonIntegrity(){ +methods { + function WebAuthn.encodeClientDataJson(bytes32 challenge, string calldata clientDataFields) internal returns (string memory) => + SencodeDataJsonCVL(challenge, clientDataFields); - env e; + function checkInjective(bytes32 challenge, bytes32 clientDataFields, bytes32 result) internal returns (bool) => + checkInjectiveSummary(challenge, clientDataFields, result); - bytes32 challenge1; - string clientDataFields; - bytes32 challenge2; + function P256.verifySignatureAllowMalleability(P256.Verifiers a, bytes32 b, uint256 c, uint256 d, uint256 e, uint256 f) internal returns bool => + verifySignatureAllowMalleabilityGhost(a, b, c, d, e, f); +} + +function SencodeDataJsonCVL(bytes32 challenge, string clientDataFields) returns string +{ + env e; + return SencodeDataJson(e, challenge, clientDataFields); +} - string a1 = encodeClientDataJson(e, challenge1, clientDataFields); - string b1 = encodeClientDataJson(e, challenge2, clientDataFields); +ghost checkInjectiveSummary(bytes32, bytes32, bytes32) returns bool { + axiom forall bytes32 x1. forall bytes32 y1. forall bytes32 x2. forall bytes32 y2. forall bytes32 result. + (x1 != x2) => !(checkInjectiveSummary(x1, y1, result) && checkInjectiveSummary(x2, y2, result)); +} - assert (challenge1 != challenge2) <=> !compareStrings(e, a1, b1); - satisfy true; +ghost verifySignatureAllowMalleabilityGhost(P256.Verifiers, bytes32, uint256, uint256, uint256, uint256) returns bool { + axiom forall P256.Verifiers a. forall bytes32 message1. forall bytes32 message2. forall uint256 c. forall uint256 d. forall uint256 e. forall uint256 f. + verifySignatureAllowMalleabilityGhost(a, message1, c, d, e, f) && + verifySignatureAllowMalleabilityGhost(a, message2, c, d, e, f) => message1 == message2; } /* @@ -48,6 +56,7 @@ rule uniqueMessagePerChallenge(){ bytes32 challenge1; bytes32 challenge2; bytes authenticatorData; + require authenticatorData.length % 32 == 0; string clientDataField; bytes message1 = encodeSigningMessage(e, challenge1, authenticatorData, clientDataField); @@ -58,7 +67,7 @@ rule uniqueMessagePerChallenge(){ /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ verifySignature functions are equivalent (Vacuity check timeout cert-6290) │ +│ verifySignature functions are equivalent (Proved) │ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ rule verifySignatureEq(){ From 6e49f8657e538cc0cb9a74e15fa972a2646c47dd Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Wed, 5 Jun 2024 12:44:14 +0300 Subject: [PATCH 060/103] Fix castSignatureDeterministicDecoding and castSignatureLengthCheckValidity --- certora/harnesses/WebAuthnHarness.sol | 2 +- certora/specs/WebAuthn.spec | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/certora/harnesses/WebAuthnHarness.sol b/certora/harnesses/WebAuthnHarness.sol index c7548c0a3..20f0410ab 100644 --- a/certora/harnesses/WebAuthnHarness.sol +++ b/certora/harnesses/WebAuthnHarness.sol @@ -38,7 +38,7 @@ contract WebAuthnHarness { } function encodeSignature(WebAuthn.Signature calldata sig) external pure returns (bytes memory signature){ - signature = abi.encode(sig); + signature = abi.encode(sig.authenticatorData, sig.clientDataFields, sig.r, sig.s); } function castSignature(bytes calldata signature) external pure returns (bool isValid, WebAuthn.Signature calldata data){ diff --git a/certora/specs/WebAuthn.spec b/certora/specs/WebAuthn.spec index 028a8b1c0..424662be2 100644 --- a/certora/specs/WebAuthn.spec +++ b/certora/specs/WebAuthn.spec @@ -144,7 +144,7 @@ rule castSignatureConsistent(){ /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ CastSignature Canonical Deterministic Decoding (Violated) | +│ CastSignature Canonical Deterministic Decoding (Proved) | └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ @@ -165,7 +165,7 @@ rule castSignatureDeterministicDecoding(){ /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ CastSignature Length Check Validity | +│ CastSignature Length Check Validity (Proved) | └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ From a346a51ca97f6de5bced9947304f9836439a2c0f Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Wed, 5 Jun 2024 14:31:02 +0300 Subject: [PATCH 061/103] Add verifySignatureConsistent --- certora/specs/WebAuthn.spec | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/certora/specs/WebAuthn.spec b/certora/specs/WebAuthn.spec index 424662be2..99f011d95 100644 --- a/certora/specs/WebAuthn.spec +++ b/certora/specs/WebAuthn.spec @@ -102,6 +102,40 @@ rule verifySignatureEq(){ assert (!firstCallRevert && !secondCallRevert) => result1 == result2; } + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ verifySignature consistent │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ +rule verifySignatureConsistent(){ + env e; + env e1; + env e2; + require e1.msg.value == 0 && e2.msg.value == 0; + method f; + calldataarg args; + + bytes32 challenge; + WebAuthn.AuthenticatorFlags authenticatorFlags; + uint256 x; + uint256 y; + P256.Verifiers verifiers; + bytes bytesSignature; + + + bool result1 = verifySignature@withrevert(e1, challenge, bytesSignature, authenticatorFlags, x, y, verifiers); + bool firstCallRevert = lastReverted; + + f(e, args); + + bool result2 = verifySignature@withrevert(e2, challenge, bytesSignature, authenticatorFlags, x, y, verifiers); + bool secondCallRevert = lastReverted; + + assert firstCallRevert == secondCallRevert; + assert (!firstCallRevert && !secondCallRevert) => result1 == result2; +} + /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ CastSignature Consistent (Once valid always valid, Once failed always failed, includes revert cases and middle call)| From 10473cf2e2c5c08ddc6c60e37752c64493d30bf0 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Wed, 5 Jun 2024 14:31:16 +0300 Subject: [PATCH 062/103] . --- certora/specs/WebAuthn.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certora/specs/WebAuthn.spec b/certora/specs/WebAuthn.spec index 99f011d95..e52f45415 100644 --- a/certora/specs/WebAuthn.spec +++ b/certora/specs/WebAuthn.spec @@ -105,7 +105,7 @@ rule verifySignatureEq(){ /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ verifySignature consistent │ +│ verifySignature consistent (Proved) │ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ rule verifySignatureConsistent(){ From d3e5471743bf81266487281e61037da0b87e93e9 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Wed, 5 Jun 2024 14:33:42 +0300 Subject: [PATCH 063/103] . --- .../contracts/SafeWebAuthnSignerFactory.sol | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol b/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol index 490704cd4..cb39fe9ed 100644 --- a/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol +++ b/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol @@ -87,22 +87,22 @@ contract SafeWebAuthnSignerFactory is ISafeSignerFactory { } } - // /** - // * @dev Checks if the provided account has no code. - // * @param account The address of the account to check. - // * @return result True if the account has no code, false otherwise. - // */ - // function _hasNoCode(address account) internal view returns (bool result) { - // // solhint-disable-next-line no-inline-assembly - // assembly ("memory-safe") { - // result := iszero(extcodesize(account)) - // } - // } - /** - Munged. + * @dev Checks if the provided account has no code. + * @param account The address of the account to check. + * @return result True if the account has no code, false otherwise. */ function _hasNoCode(address account) internal view returns (bool result) { - return account.code.length > 0; + // solhint-disable-next-line no-inline-assembly + assembly ("memory-safe") { + result := iszero(extcodesize(account)) + } } + + // /** + // possible munge to pass the SignerCreationCantOverride rule, wait for concusion. + // */ + // function _hasNoCode(address account) internal view returns (bool result) { + // return account.code.length > 0; + // } } From 80ab0d5ebc8849dc1114c691fce56f625d83d144 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Wed, 5 Jun 2024 16:43:29 +0300 Subject: [PATCH 064/103] Organize files --- certora/confs/GetSigner.conf | 23 ++++ certora/confs/SafeWebAuthnSignerFactory.conf | 14 +-- certora/confs/WebAuthnBitVectorTheory.conf | 26 ----- certora/harnesses/GetSignerHarness.sol | 43 ++++++++ .../SafeWebAuthnSignerFactoryHarness.sol | 20 +--- certora/specs/GetSigner.spec | 70 ++++++++++++ certora/specs/SafeWebAuthnSignerFactory.spec | 104 +++--------------- .../contracts/SafeWebAuthnSignerFactory.sol | 14 +-- 8 files changed, 165 insertions(+), 149 deletions(-) create mode 100644 certora/confs/GetSigner.conf delete mode 100644 certora/confs/WebAuthnBitVectorTheory.conf create mode 100644 certora/harnesses/GetSignerHarness.sol create mode 100644 certora/specs/GetSigner.spec diff --git a/certora/confs/GetSigner.conf b/certora/confs/GetSigner.conf new file mode 100644 index 000000000..4484a3502 --- /dev/null +++ b/certora/confs/GetSigner.conf @@ -0,0 +1,23 @@ +{ + "assert_autofinder_success": true, + "files": [ + "certora/harnesses/GetSignerHarness.sol", + "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", + "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol" + ], + "link": [ + "GetSignerHarness:SINGLETON=SafeWebAuthnSignerSingleton" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "rule_sanity": "basic", + "solc": "solc8.23", + "solc_via_ir": false, + "optimistic_loop": true, + "optimistic_hashing": true, + "hashing_length_bound": "4694", + "loop_iter": "144", + "verify": "GetSignerHarness:certora/specs/GetSigner.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeWebAuthnSignerFactory.conf b/certora/confs/SafeWebAuthnSignerFactory.conf index 6dfe5b5f5..6e029af9d 100644 --- a/certora/confs/SafeWebAuthnSignerFactory.conf +++ b/certora/confs/SafeWebAuthnSignerFactory.conf @@ -3,18 +3,11 @@ "files": [ "certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol", "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", - "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol", - "certora/harnesses/ERC20Like/DummyWeth.sol", - "certora/harnesses/Utilities.sol", - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" + "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol" ], "link": [ "SafeWebAuthnSignerFactoryHarness:SINGLETON=SafeWebAuthnSignerSingleton" ], - "msg": "sanity_with_all_default_summaries", - "process": "emv", "packages":[ "@safe-global=node_modules/@safe-global", "@account-abstraction=node_modules/@account-abstraction" @@ -24,9 +17,6 @@ "solc_via_ir": false, "optimistic_loop": true, "optimistic_hashing": true, - "hashing_length_bound": "4694", - "loop_iter": "144", - "prover_version": "master", - "server": "production", + "loop_iter": "6", "verify": "SafeWebAuthnSignerFactoryHarness:certora/specs/SafeWebAuthnSignerFactory.spec" } \ No newline at end of file diff --git a/certora/confs/WebAuthnBitVectorTheory.conf b/certora/confs/WebAuthnBitVectorTheory.conf deleted file mode 100644 index 7b928b6fa..000000000 --- a/certora/confs/WebAuthnBitVectorTheory.conf +++ /dev/null @@ -1,26 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "certora/harnesses/WebAuthnHarness.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity_with_all_default_summaries", - "process": "emv", - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "prover_args": ["-useBitVectorTheory"], - "rule": ["encodeClientDataJsonIntegrity", "uniqueMessagePerChallenge"], - "solc": "solc8.23", - "solc_via_ir": false, - "optimistic_loop": true, - "optimistic_hashing": true, - "prover_version": "master", - "server": "production", - "rule_sanity": "basic", - "loop_iter": "6", - "verify": "WebAuthnHarness:certora/specs/WebAuthn.spec" -} \ No newline at end of file diff --git a/certora/harnesses/GetSignerHarness.sol b/certora/harnesses/GetSignerHarness.sol new file mode 100644 index 000000000..940563550 --- /dev/null +++ b/certora/harnesses/GetSignerHarness.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity >=0.8.0; + +import {SafeWebAuthnSignerFactory} from "../../modules/passkey/contracts/SafeWebAuthnSignerFactory.sol"; +import {P256} from "../../modules/passkey/contracts/libraries/P256.sol"; +import {SafeWebAuthnSignerProxy} from "../../modules/passkey/contracts/SafeWebAuthnSignerProxy.sol"; + +contract GetSignerHarness is SafeWebAuthnSignerFactory { + + function getSignerHarnessed(uint256 x, uint256 y, P256.Verifiers verifiers) public view returns (uint256 value) { + bytes32 codeHash = keccak256( + abi.encodePacked( + type(SafeWebAuthnSignerProxy).creationCode, + "01234567891011121314152546", + uint256(uint160(address(SINGLETON))), + x, + y, + uint256(P256.Verifiers.unwrap(verifiers)) + ) + ); + value = uint256(keccak256(abi.encodePacked(hex"ff", address(this), bytes32(0), codeHash))); + } + function castToAddress(uint256 value) public pure returns (address addr){ + addr = address(uint160(value)); + } + + /** + * munged getSigner + */ + function getSigner(uint256 x, uint256 y, P256.Verifiers verifiers) public view override returns (address signer) { + bytes32 codeHash = keccak256( + abi.encodePacked( + type(SafeWebAuthnSignerProxy).creationCode, + "01234567891011121314152546", // munged for word alignment workaround (32 bytes) + uint256(uint160(address(SINGLETON))), + x, + y, + uint256(P256.Verifiers.unwrap(verifiers)) + ) + ); + signer = address(uint160(uint256(keccak256(abi.encodePacked(hex"ff", address(this), bytes32(0), codeHash))))); + } +} diff --git a/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol b/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol index 0321274e4..a6dfb4101 100644 --- a/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol +++ b/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol @@ -30,20 +30,10 @@ contract SafeWebAuthnSignerFactoryHarness is SafeWebAuthnSignerFactory { magicValue = abi.decode(result, (bytes4)); } - function getSignerHarnessed(uint256 x, uint256 y, P256.Verifiers verifiers) public view returns (uint256 value) { - bytes32 codeHash = keccak256( - abi.encodePacked( - type(SafeWebAuthnSignerProxy).creationCode, - "01234567891011121314152546", - uint256(uint160(address(SINGLETON))), - x, - y, - uint256(P256.Verifiers.unwrap(verifiers)) - ) - ); - value = uint256(keccak256(abi.encodePacked(hex"ff", address(this), bytes32(0), codeHash))); - } - function castToAddress(uint256 value) public pure returns (address addr){ - addr = address(uint160(value)); + /** + munge to pass the SignerCreationCantOverride rule. + */ + function _hasNoCode(address account) internal view override returns (bool result) { + return account.code.length > 0; } } diff --git a/certora/specs/GetSigner.spec b/certora/specs/GetSigner.spec new file mode 100644 index 000000000..35c4bfb9a --- /dev/null +++ b/certora/specs/GetSigner.spec @@ -0,0 +1,70 @@ +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ + getSigner is unique for every x,y and verifier combination, proved with assumptions: + 1.) value before cast to address <= max_uint160. + 2.) munging required to complete signer data to be constructed from full 32bytes size arrays + function getSignerHarnessed(uint256 x, uint256 y, P256.Verifiers verifiers) public view returns (uint256 value) { + bytes32 codeHash = keccak256( + abi.encodePacked( + type(SafeWebAuthnSignerProxy).creationCode, + "01234567891011121314152546", <--------------- HERE! + uint256(uint160(address(SINGLETON))), + x, + y, + uint256(P256.Verifiers.unwrap(verifiers)) + ) + ); + value = uint256(keccak256(abi.encodePacked(hex"ff", address(this), bytes32(0), codeHash))); + } +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ + +// helper rule to justify the use of the harnessed implementation (proved). +rule mungedEquivalence() +{ + env e1; + env e2; + + require e1.msg.value == 0 && e2.msg.value == 0; + uint256 x; + uint256 y; + P256.Verifiers verifier; + + storage s = lastStorage; + + uint256 harnessedSignerValue = getSignerHarnessed@withrevert(e1, x, y, verifier); + bool harnessedSignerRevert1 = lastReverted; + + address harnessedSigner = castToAddress@withrevert(e1, harnessedSignerValue); + bool harnessedSignerRevert2 = harnessedSignerRevert1 && lastReverted; + + address signer = getSigner@withrevert(e2, x, y, verifier) at s; + bool signerRevert = lastReverted; + + assert (harnessedSignerRevert2 == signerRevert); + assert (!harnessedSignerRevert2 && !signerRevert) => (harnessedSigner == signer); +} + +rule uniqueSigner(){ + env e; + + uint256 firstX; + uint256 firstY; + P256.Verifiers firstVerifier; + + uint256 firstSignerValue = getSignerHarnessed(e, firstX, firstY, firstVerifier); + require firstSignerValue <= max_uint160; // <=== needed assumption + + address firstSigner = castToAddress(e, firstSignerValue); + + uint256 secondX; + uint256 secondY; + P256.Verifiers secondVerifier; + + uint256 secondSignerValue = getSignerHarnessed(e, secondX, secondY, secondVerifier); + require secondSignerValue <= max_uint160; // <=== needed assumption + + address secondSigner = castToAddress(e, secondSignerValue); + + assert firstSigner == secondSigner <=> (firstX == secondX && firstY == secondY && firstVerifier == secondVerifier); +} \ No newline at end of file diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index 61964356e..e4c403394 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -7,6 +7,13 @@ methods{ function hasNoCode(address) external returns (bool) envfree; } +// Summary is correct only if the unique signer rule is proved spec GetSigner +ghost getSignerGhost(uint256, uint256, P256.Verifiers) returns address { + axiom forall uint256 x1. forall uint256 y1. forall P256.Verifiers v1. + forall uint256 x2. forall uint256 y2. forall P256.Verifiers v2. + (getSignerGhost(x1, y1, v1) == getSignerGhost(x2, y2, v2)) <=> (x1 == x2 && y1 == y2 && v1 == v2); +} + definition MAGIC_VALUE() returns bytes4 = to_bytes4(0x1626ba7e); /* @@ -26,94 +33,12 @@ rule singletonNeverChanges() assert currentSingleton == currentContract.SINGLETON; } - - -/* -┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ - getSigner is unique for every x,y and verifier combination, proved with assumptions: - 1.) value before cast to address <= max_uint160. - 2.) munging required to complete signer data to be constructed from full 32bytes size arrays - function getSignerHarnessed(uint256 x, uint256 y, P256.Verifiers verifiers) public view returns (uint256 value) { - bytes32 codeHash = keccak256( - abi.encodePacked( - type(SafeWebAuthnSignerProxy).creationCode, - "01234567891011121314152546", <--------------- HERE! - uint256(uint160(address(SINGLETON))), - x, - y, - uint256(P256.Verifiers.unwrap(verifiers)) - ) - ); - value = uint256(keccak256(abi.encodePacked(hex"ff", address(this), bytes32(0), codeHash))); - } -└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ -*/ - -// helper rule to justify the use of the munged implementation (proved) need to drop getSigner summary before execution. -rule mungedEquivalence() -{ - env e1; - env e2; - - require e1.msg.value == 0 && e2.msg.value == 0; - uint256 x; - uint256 y; - P256.Verifiers verifier; - - storage s = lastStorage; - - uint256 harnessedSignerValue = getSignerHarnessed@withrevert(e1, x, y, verifier); - bool harnessedSignerRevert1 = lastReverted; - - address harnessedSigner = castToAddress@withrevert(e1, harnessedSignerValue); - bool harnessedSignerRevert2 = harnessedSignerRevert1 && lastReverted; - - address signer = getSigner@withrevert(e2, x, y, verifier) at s; - bool signerRevert = lastReverted; - - assert (harnessedSignerRevert2 == signerRevert); - assert (!harnessedSignerRevert2 && !signerRevert) => (harnessedSigner == signer); -} - - -rule uniqueSigner(){ - env e; - - uint256 firstX; - uint256 firstY; - P256.Verifiers firstVerifier; - - uint256 firstSignerValue = getSignerHarnessed(e, firstX, firstY, firstVerifier); - require firstSignerValue <= max_uint160; - - address firstSigner = castToAddress(e, firstSignerValue); - - uint256 secondX; - uint256 secondY; - P256.Verifiers secondVerifier; - - uint256 secondSignerValue = getSignerHarnessed(e, secondX, secondY, secondVerifier); - require secondSignerValue <= max_uint160; - - address secondSigner = castToAddress(e, secondSignerValue); - - - assert firstSigner == secondSigner <=> (firstX == secondX && firstY == secondY && firstVerifier == secondVerifier); -} - /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ createSigner and getSigner always returns the same address (Proved under assumption) │ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ -// Summary is correct only if the unique signer rule is proved !!! -ghost getSignerGhost(uint256, uint256, P256.Verifiers) returns address { - axiom forall uint256 x1. forall uint256 y1. forall P256.Verifiers v1. - forall uint256 x2. forall uint256 y2. forall P256.Verifiers v2. - (getSignerGhost(x1, y1, v1) == getSignerGhost(x2, y2, v2)) <=> (x1 == x2 && y1 == y2 && v1 == v2); -} - rule createAndGetSignerEquivalence(){ env e; @@ -224,12 +149,16 @@ rule createAndVerifyEQtoIsValidSignatureForSigner() /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ isValidSignatureForSigner Consistency │ +│ isValidSignatureForSigner Consistency Proved │ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ rule isValidSignatureForSignerConsistency() { env e; + env e1; + env e2; + require e1.msg.value == 0 && e2.msg.value == 0; + method f; calldataarg args; @@ -240,11 +169,14 @@ rule isValidSignatureForSignerConsistency() bytes signature; bytes32 message; - bytes4 magic1 = isValidSignatureForSigner(e, message, signature, x, y, verifier); + bytes4 magic1 = isValidSignatureForSigner@withrevert(e1, message, signature, x, y, verifier); + bool firstRevert = lastReverted; f(e, args); - bytes4 magic2 = isValidSignatureForSigner(e, message, signature, x, y, verifier); + bytes4 magic2 = isValidSignatureForSigner@withrevert(e2, message, signature, x, y, verifier); + bool secondRevert = lastReverted; - assert (magic1 == MAGIC_VALUE()) <=> (magic2 == MAGIC_VALUE()); + assert firstRevert == secondRevert; + assert (!firstRevert && !secondRevert) => (magic1 == MAGIC_VALUE()) <=> (magic2 == MAGIC_VALUE()); } \ No newline at end of file diff --git a/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol b/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol index cb39fe9ed..8651a378f 100644 --- a/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol +++ b/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol @@ -32,11 +32,11 @@ contract SafeWebAuthnSignerFactory is ISafeSignerFactory { /** * @inheritdoc ISafeSignerFactory */ - function getSigner(uint256 x, uint256 y, P256.Verifiers verifiers) public view override returns (address signer) { + // funtion is not really virtual, Munged! + function getSigner(uint256 x, uint256 y, P256.Verifiers verifiers) public view virtual override returns (address signer) { bytes32 codeHash = keccak256( abi.encodePacked( type(SafeWebAuthnSignerProxy).creationCode, - "01234567891011121314152546", // munged for word alignment workaround (32 bytes) uint256(uint160(address(SINGLETON))), x, y, @@ -92,17 +92,11 @@ contract SafeWebAuthnSignerFactory is ISafeSignerFactory { * @param account The address of the account to check. * @return result True if the account has no code, false otherwise. */ - function _hasNoCode(address account) internal view returns (bool result) { + // funtion is not really virtual, munged! + function _hasNoCode(address account) internal view virtual returns (bool result) { // solhint-disable-next-line no-inline-assembly assembly ("memory-safe") { result := iszero(extcodesize(account)) } } - - // /** - // possible munge to pass the SignerCreationCantOverride rule, wait for concusion. - // */ - // function _hasNoCode(address account) internal view returns (bool result) { - // return account.code.length > 0; - // } } From f56b19c9ceffd5b0b1cc6399e2d7a5ece944be83 Mon Sep 17 00:00:00 2001 From: yoavelmalem Date: Wed, 5 Jun 2024 16:55:04 +0300 Subject: [PATCH 065/103] Removed harness and cleaned both spec and conf file. --- .../confs/SafeWebAuthnSignerSingleton.conf | 12 +-- .../SafeWebAuthnSignerSingletonHarness.sol | 26 ----- .../specs/SafeWebAuthnSignerSingleton.spec | 98 +++++++------------ 3 files changed, 37 insertions(+), 99 deletions(-) delete mode 100644 certora/harnesses/SafeWebAuthnSignerSingletonHarness.sol diff --git a/certora/confs/SafeWebAuthnSignerSingleton.conf b/certora/confs/SafeWebAuthnSignerSingleton.conf index 5082a520f..cb5d77137 100644 --- a/certora/confs/SafeWebAuthnSignerSingleton.conf +++ b/certora/confs/SafeWebAuthnSignerSingleton.conf @@ -5,18 +5,8 @@ "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", "modules/passkey/contracts/libraries/WebAuthn.sol", "modules/passkey/contracts/libraries/P256.sol", - "certora/harnesses/SafeWebAuthnSignerSingletonHarness.sol", "certora/harnesses/WebAuthnHarness.sol", ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "", - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - //" -useBitVectorTheory" - ], "packages":[ "@safe-global=node_modules/@safe-global", "@account-abstraction=node_modules/@account-abstraction" @@ -29,5 +19,5 @@ "optimistic_hashing": true, "prover_version": "master", "server": "production", - "verify": "SafeWebAuthnSignerSingletonHarness:certora/specs/SafeWebAuthnSignerSingleton.spec" + "verify": "SafeWebAuthnSignerSingleton:certora/specs/SafeWebAuthnSignerSingleton.spec" } \ No newline at end of file diff --git a/certora/harnesses/SafeWebAuthnSignerSingletonHarness.sol b/certora/harnesses/SafeWebAuthnSignerSingletonHarness.sol deleted file mode 100644 index cddb033e0..000000000 --- a/certora/harnesses/SafeWebAuthnSignerSingletonHarness.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity >=0.8.0; - -import {SafeWebAuthnSignerSingleton} from "../../modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol"; -import {SignatureValidator} from "../../modules/passkey/contracts/base/SignatureValidator.sol"; -import {P256, WebAuthn} from "../../modules/passkey/contracts/libraries/WebAuthn.sol"; - -/** - * @title Safe WebAuthn Signer Singleton - * @dev A singleton contract that implements WebAuthn signature verification. This singleton - * contract must be used with the specialized proxy {SafeWebAuthnSignerProxy}, as it encodes the - * credential configuration (public key coordinates and P-256 verifier to use) in calldata, which is - * required by this implementation. - * @custom:security-contact bounty@safe.global - */ -contract SafeWebAuthnSignerSingletonHarness is SafeWebAuthnSignerSingleton { - - function verifySignatureHarnessed(bytes32 message, bytes calldata signature) public view virtual returns (bool success) { - (uint256 x, uint256 y, P256.Verifiers verifiers) = getConfiguration(); - success = WebAuthn.verifySignature(message, signature, WebAuthn.USER_VERIFICATION, x, y, verifiers); - } - - function getConfigurationHarnessed(bytes32 message, bytes calldata signature) public pure returns (uint256 new_x, uint256 new_y, P256.Verifiers new_verifiers) { - (uint256 new_x, uint256 new_y, P256.Verifiers new_verifiers) = getConfiguration(); - } -} diff --git a/certora/specs/SafeWebAuthnSignerSingleton.spec b/certora/specs/SafeWebAuthnSignerSingleton.spec index 8a9d928d4..c6bcfed38 100644 --- a/certora/specs/SafeWebAuthnSignerSingleton.spec +++ b/certora/specs/SafeWebAuthnSignerSingleton.spec @@ -28,6 +28,8 @@ ghost verifySignatureAllowMalleabilityGhost(P256.Verifiers, bytes32, uint256, ui verifySignatureAllowMalleabilityGhost(a, message2, c, d, e, f) => message1 == message2; } +definition MAGIC_VALUE() returns bytes4 = to_bytes4(0x1626ba7e); + /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Implementation of _verifySignature Function (Integrity) │ @@ -37,54 +39,29 @@ ghost verifySignatureAllowMalleabilityGhost(P256.Verifiers, bytes32, uint256, ui rule verifySignatureUniqueness(env e){ bytes32 first_message; bytes32 second_message; - bytes signature; + WebAuthn.Signature sigStruct; + bytes signature = WebAuthnHarness.encodeSignature(e, sigStruct); - bool first_message_verified = verifySignatureHarnessed(e, first_message, signature); - bool second_message_verified = verifySignatureHarnessed(e, second_message, signature); + bytes4 first_message_verified = isValidSignature(e, first_message, signature); + bytes4 second_message_verified = isValidSignature(e, second_message, signature); - assert (first_message != second_message) => !(first_message_verified && second_message_verified); + assert (first_message != second_message) => !(first_message_verified == MAGIC_VALUE() && second_message_verified == MAGIC_VALUE()); } rule verifySignatureIntegrity(env e){ bytes32 first_message; bytes32 second_message; - bytes signature; + WebAuthn.Signature sigStruct; + bytes signature = WebAuthnHarness.encodeSignature(e, sigStruct); - bool first_message_verified = verifySignatureHarnessed(e, first_message, signature); - require (first_message_verified); + bytes4 first_message_verified = isValidSignature(e, first_message, signature); + require (first_message_verified == MAGIC_VALUE()); - bool second_message_verified = verifySignatureHarnessed(e, second_message, signature); + bytes4 second_message_verified = isValidSignature(e, second_message, signature); - assert second_message_verified <=> first_message == second_message; + assert (second_message_verified == MAGIC_VALUE()) <=> (first_message == second_message); } -/* -┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ getConfiguration Function (Integrity) │ -└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ -*/ - -// TODO Not Completed Yet -// rule verifyGetConfigurationIntegrity(env e){ -// bytes32 data; -// bytes first_signature; - -// uint256 x; -// uint256 y; -// P256.Verifiers verifiers; -// uint256 new_x; uint256 new_y; P256.Verifiers new_verifiers; - -// x = proxy.getX(); -// y = proxy.getY(); -// verifiers = proxy.getVerifiers(); -// (new_x, new_y, new_verifiers) = proxy.getConfiguration(e); - -// assert x == new_x; -// assert y == new_y; -// assert verifiers == new_verifiers; -// satisfy true; -// } - /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Both is valid Signature behave the same way │ @@ -94,6 +71,8 @@ rule verifySignatureIntegrity(env e){ rule verifyIsValidSignatureAreEqual(env e){ bytes data; bytes first_signature; + WebAuthn.Signature sigStruct; + first_signature = WebAuthnHarness.encodeSignature(e, sigStruct); bytes4 magicValue_hashed = isValidSignature(e, data, first_signature); @@ -111,39 +90,34 @@ rule verifyIsValidSignatureAreEqual(env e){ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ -rule verifyIsValidSignatureWillContinueToSucceed(env e){ - method f; - calldataarg args; - bytes32 message; - bytes signature; - - bool first_verified = verifySignatureHarnessed(e, message, signature); - require first_verified; - - f(e, args); - - bool second_verified = verifySignatureHarnessed(e, message, signature); - assert second_verified; -} - -/* -┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ If isValidSignature failed, the only method that can make it pass is createSigner │ -└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ -*/ +rule verifyIsValidSignatureWillContinueToSucceed(){ + env e; + env e1; + env e2; + env e3; + require e1.msg.value == 0 && e2.msg.value == 0 && e3.msg.value == 0; -rule IsValidSignatureWillSucceedOnlyAfterCreation(env e){ method f; calldataarg args; bytes32 message; - bytes signature; + WebAuthn.Signature sigStruct; + bytes signature = WebAuthnHarness.encodeSignature(e, sigStruct); + + bytes32 message3; + WebAuthn.Signature sigStruct3; + bytes signature3 = WebAuthnHarness.encodeSignature(e, sigStruct3); - bool first_verified = verifySignatureHarnessed(e, message, signature); - require !first_verified; + bytes4 firstVerified = isValidSignature@withrevert(e1, message, signature); + bool firstReverted = lastReverted; f(e, args); + // isValidSignature(e3, message3, signature3); + + bytes4 secondVerify = isValidSignature@withrevert(e2, message, signature); + bool secondRevert = lastReverted; - bool second_verified = verifySignatureHarnessed(e, message, signature); - assert second_verified => f.selector == sig:SafeWebAuthnSignerFactory.createSigner(uint256, uint256, P256.Verifiers).selector; + assert firstReverted == secondRevert; + assert (!firstReverted && !secondRevert) => (firstVerified == secondVerify); + satisfy true; } From 7d58f27f1055629b55c1ea5c93ebaa55a79699ab Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Wed, 5 Jun 2024 17:08:51 +0300 Subject: [PATCH 066/103] . --- certora/specs/SafeWebAuthnSignerFactory.spec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index e4c403394..4b829c88d 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -149,9 +149,10 @@ rule createAndVerifyEQtoIsValidSignatureForSigner() /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ isValidSignatureForSigner Consistency Proved │ +│ isValidSignatureForSigner Consistency (Proved) │ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ + rule isValidSignatureForSignerConsistency() { env e; From a9e97ea8831d50166c7f76bffcfa82297b4e6552 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Thu, 6 Jun 2024 12:58:09 +0300 Subject: [PATCH 067/103] . --- certora/specs/SafeWebAuthnSignerFactory.spec | 22 ++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index 4b829c88d..b9d5dc833 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -180,4 +180,26 @@ rule isValidSignatureForSignerConsistency() assert firstRevert == secondRevert; assert (!firstRevert && !secondRevert) => (magic1 == MAGIC_VALUE()) <=> (magic2 == MAGIC_VALUE()); +} + + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ isValidSignatureForSigner Integrity (Violated) │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ + +rule isValidSignatureForSignerIntegrity() +{ + env e; + + uint x; + uint y; + P256.Verifiers verifier; + bytes signature; + bytes32 message; + + bytes4 magic1 = isValidSignatureForSigner(e, message, signature, x, y, verifier); + + satisfy magic1 == MAGIC_VALUE(); } \ No newline at end of file From 4d3d0f4030e0ef3454100b3739fc437bb2fd22f5 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Thu, 6 Jun 2024 13:23:21 +0300 Subject: [PATCH 068/103] . --- certora/harnesses/WebAuthnHarness.sol | 2 +- certora/specs/WebAuthn.spec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/certora/harnesses/WebAuthnHarness.sol b/certora/harnesses/WebAuthnHarness.sol index 20f0410ab..68fbaf4ba 100644 --- a/certora/harnesses/WebAuthnHarness.sol +++ b/certora/harnesses/WebAuthnHarness.sol @@ -7,7 +7,7 @@ contract WebAuthnHarness { mapping (bytes32 => mapping (bytes32 => string)) symbolicClientDataJson; - function SencodeDataJson(bytes32 challenge, string calldata clientDataFields) public returns (string memory){ + function summaryEncodeDataJson(bytes32 challenge, string calldata clientDataFields) public returns (string memory){ bytes32 hashClientDataFields = keccak256(abi.encodePacked(clientDataFields)); string memory stringResult = symbolicClientDataJson[challenge][hashClientDataFields]; bytes32 hashResult = keccak256(abi.encodePacked(stringResult)); diff --git a/certora/specs/WebAuthn.spec b/certora/specs/WebAuthn.spec index e52f45415..6c194a077 100644 --- a/certora/specs/WebAuthn.spec +++ b/certora/specs/WebAuthn.spec @@ -13,7 +13,7 @@ methods { function SencodeDataJsonCVL(bytes32 challenge, string clientDataFields) returns string { env e; - return SencodeDataJson(e, challenge, clientDataFields); + return summaryEncodeDataJson(e, challenge, clientDataFields); } ghost checkInjectiveSummary(bytes32, bytes32, bytes32) returns bool { From 34450e216e324f5da61b8f84b1f3182ae5443dfa Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Thu, 6 Jun 2024 13:32:17 +0300 Subject: [PATCH 069/103] Add munging remove changes from safe source code --- certora/harnesses/GetSignerHarness.sol | 2 +- .../SafeWebAuthnSignerFactoryHarness.sol | 2 +- certora/munged/SafeWebAuthnSignerFactory.sol | 102 ++++++++++++++++++ certora/specs/WebAuthn.spec | 2 +- .../contracts/SafeWebAuthnSignerFactory.sol | 6 +- 5 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 certora/munged/SafeWebAuthnSignerFactory.sol diff --git a/certora/harnesses/GetSignerHarness.sol b/certora/harnesses/GetSignerHarness.sol index 940563550..f4df4ff78 100644 --- a/certora/harnesses/GetSignerHarness.sol +++ b/certora/harnesses/GetSignerHarness.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity >=0.8.0; -import {SafeWebAuthnSignerFactory} from "../../modules/passkey/contracts/SafeWebAuthnSignerFactory.sol"; +import {SafeWebAuthnSignerFactory} from "../munged/SafeWebAuthnSignerFactory.sol"; import {P256} from "../../modules/passkey/contracts/libraries/P256.sol"; import {SafeWebAuthnSignerProxy} from "../../modules/passkey/contracts/SafeWebAuthnSignerProxy.sol"; diff --git a/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol b/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol index a6dfb4101..b14b24f23 100644 --- a/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol +++ b/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity >=0.8.0; -import {SafeWebAuthnSignerFactory} from "../../modules/passkey/contracts/SafeWebAuthnSignerFactory.sol"; +import {SafeWebAuthnSignerFactory} from "../munged/SafeWebAuthnSignerFactory.sol"; import {P256} from "../../modules/passkey/contracts/libraries/P256.sol"; import {SafeWebAuthnSignerProxy} from "../../modules/passkey/contracts/SafeWebAuthnSignerProxy.sol"; diff --git a/certora/munged/SafeWebAuthnSignerFactory.sol b/certora/munged/SafeWebAuthnSignerFactory.sol new file mode 100644 index 000000000..bc85d2cfe --- /dev/null +++ b/certora/munged/SafeWebAuthnSignerFactory.sol @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity >=0.8.0; + +import {ISafeSignerFactory} from "../../modules/passkey/contracts/interfaces/ISafeSignerFactory.sol"; +import {SafeWebAuthnSignerProxy} from "../../modules/passkey/contracts/SafeWebAuthnSignerProxy.sol"; +import {SafeWebAuthnSignerSingleton} from "../../modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol"; +import {P256} from "../../modules/passkey/contracts/libraries/P256.sol"; + +/** + * @title Safe WebAuthn Signer Factory + * @dev A factory contract for creating WebAuthn signers. Additionally, the factory supports + * signature verification without deploying a signer proxies. + * @custom:security-contact bounty@safe.global + */ +contract SafeWebAuthnSignerFactory is ISafeSignerFactory { + /** + * @notice The {SafeWebAuthnSignerSingleton} implementation to that is used for signature + * verification by this contract and any proxies it deploys. + */ + SafeWebAuthnSignerSingleton public immutable SINGLETON; + + /** + * @notice Creates a new WebAuthn Safe signer factory contract. + * @dev The {SafeWebAuthnSignerSingleton} singleton implementation is created with as part of + * this constructor. This ensures that the singleton contract is known, and lets us make certain + * assumptions about how it works. + */ + constructor() { + SINGLETON = new SafeWebAuthnSignerSingleton(); + } + + /** + * @inheritdoc ISafeSignerFactory + */ + // funtion is not really virtual, Munged! + function getSigner(uint256 x, uint256 y, P256.Verifiers verifiers) public view virtual override returns (address signer) { + bytes32 codeHash = keccak256( + abi.encodePacked( + type(SafeWebAuthnSignerProxy).creationCode, + uint256(uint160(address(SINGLETON))), + x, + y, + uint256(P256.Verifiers.unwrap(verifiers)) + ) + ); + signer = address(uint160(uint256(keccak256(abi.encodePacked(hex"ff", address(this), bytes32(0), codeHash))))); + } + + /** + * @inheritdoc ISafeSignerFactory + */ + function createSigner(uint256 x, uint256 y, P256.Verifiers verifiers) external returns (address signer) { + signer = getSigner(x, y, verifiers); + + if (_hasNoCode(signer)) { + SafeWebAuthnSignerProxy created = new SafeWebAuthnSignerProxy{salt: bytes32(0)}(address(SINGLETON), x, y, verifiers); + assert(address(created) == signer); + emit Created(signer, x, y, verifiers); + } + } + + /** + * @inheritdoc ISafeSignerFactory + */ + function isValidSignatureForSigner( + bytes32 message, + bytes calldata signature, + uint256 x, + uint256 y, + P256.Verifiers verifiers + ) external view override returns (bytes4 magicValue) { + address singleton = address(SINGLETON); + bytes memory data = abi.encodePacked( + abi.encodeWithSignature("isValidSignature(bytes32,bytes)", message, signature), + x, + y, + verifiers + ); + + // solhint-disable-next-line no-inline-assembly + assembly { + // staticcall to the singleton contract with return size given as 32 bytes. The + // singleton contract is known and immutable so it is safe to specify return size. + if staticcall(gas(), singleton, add(data, 0x20), mload(data), 0, 32) { + magicValue := mload(0) + } + } + } + + /** + * @dev Checks if the provided account has no code. + * @param account The address of the account to check. + * @return result True if the account has no code, false otherwise. + */ + // funtion is not really virtual, munged! + function _hasNoCode(address account) internal view virtual returns (bool result) { + // solhint-disable-next-line no-inline-assembly + assembly ("memory-safe") { + result := iszero(extcodesize(account)) + } + } +} diff --git a/certora/specs/WebAuthn.spec b/certora/specs/WebAuthn.spec index 6c194a077..40a8734cb 100644 --- a/certora/specs/WebAuthn.spec +++ b/certora/specs/WebAuthn.spec @@ -18,7 +18,7 @@ function SencodeDataJsonCVL(bytes32 challenge, string clientDataFields) returns ghost checkInjectiveSummary(bytes32, bytes32, bytes32) returns bool { axiom forall bytes32 x1. forall bytes32 y1. forall bytes32 x2. forall bytes32 y2. forall bytes32 result. - (x1 != x2) => !(checkInjectiveSummary(x1, y1, result) && checkInjectiveSummary(x2, y2, result)); + (checkInjectiveSummary(x1, y1, result) && checkInjectiveSummary(x2, y2, result)) => (x1 == x2); } ghost verifySignatureAllowMalleabilityGhost(P256.Verifiers, bytes32, uint256, uint256, uint256, uint256) returns bool { diff --git a/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol b/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol index 8651a378f..26d9f54c0 100644 --- a/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol +++ b/modules/passkey/contracts/SafeWebAuthnSignerFactory.sol @@ -32,8 +32,7 @@ contract SafeWebAuthnSignerFactory is ISafeSignerFactory { /** * @inheritdoc ISafeSignerFactory */ - // funtion is not really virtual, Munged! - function getSigner(uint256 x, uint256 y, P256.Verifiers verifiers) public view virtual override returns (address signer) { + function getSigner(uint256 x, uint256 y, P256.Verifiers verifiers) public view override returns (address signer) { bytes32 codeHash = keccak256( abi.encodePacked( type(SafeWebAuthnSignerProxy).creationCode, @@ -92,8 +91,7 @@ contract SafeWebAuthnSignerFactory is ISafeSignerFactory { * @param account The address of the account to check. * @return result True if the account has no code, false otherwise. */ - // funtion is not really virtual, munged! - function _hasNoCode(address account) internal view virtual returns (bool result) { + function _hasNoCode(address account) internal view returns (bool result) { // solhint-disable-next-line no-inline-assembly assembly ("memory-safe") { result := iszero(extcodesize(account)) From dc899e6df1cf09b411ea723716e089e62d87e21c Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Thu, 6 Jun 2024 15:59:00 +0300 Subject: [PATCH 070/103] Clean --- certora/specs/SafeWebAuthnSignerFactory.spec | 46 -------------------- certora/specs/WebAuthn.spec | 21 --------- 2 files changed, 67 deletions(-) diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index b9d5dc833..24ee0b423 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -123,30 +123,6 @@ rule hasNoCodeIntegrity() assert (a == proxy) => !hasNoCode(a); } -/* -┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ isValidSignatureForSigner equiv to first deploying the signer with the factory, and then | -| verifying the signature with it directly (CERT-6221) │ -└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ -*/ -rule createAndVerifyEQtoIsValidSignatureForSigner() -{ - env e; - uint x; - uint y; - P256.Verifiers verifier; - bytes signature; - bytes32 message; - - storage s = lastStorage; - - bytes4 magic1 = isValidSignatureForSigner(e, message, signature, x, y, verifier); - - bytes4 magic2 = createAndVerify(e, message, signature, x, y, verifier) at s; - - assert magic1 == magic2; -} - /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ isValidSignatureForSigner Consistency (Proved) │ @@ -180,26 +156,4 @@ rule isValidSignatureForSignerConsistency() assert firstRevert == secondRevert; assert (!firstRevert && !secondRevert) => (magic1 == MAGIC_VALUE()) <=> (magic2 == MAGIC_VALUE()); -} - - -/* -┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ isValidSignatureForSigner Integrity (Violated) │ -└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ -*/ - -rule isValidSignatureForSignerIntegrity() -{ - env e; - - uint x; - uint y; - P256.Verifiers verifier; - bytes signature; - bytes32 message; - - bytes4 magic1 = isValidSignatureForSigner(e, message, signature, x, y, verifier); - - satisfy magic1 == MAGIC_VALUE(); } \ No newline at end of file diff --git a/certora/specs/WebAuthn.spec b/certora/specs/WebAuthn.spec index 4f394a3aa..f59e6818a 100644 --- a/certora/specs/WebAuthn.spec +++ b/certora/specs/WebAuthn.spec @@ -44,27 +44,6 @@ rule shaIntegrity(){ assert (keccak256(input1) != keccak256(input2)) <=> input1_sha != input2_sha; } - -/* -┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ every 2 challenges results in unique message when using encodeSigningMessage (Timeout cert-6290) │ -└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ -*/ -rule uniqueMessagePerChallenge(){ - env e; - - bytes32 challenge1; - bytes32 challenge2; - bytes authenticatorData; - require authenticatorData.length % 32 == 0; - string clientDataField; - - bytes message1 = encodeSigningMessage(e, challenge1, authenticatorData, clientDataField); - bytes message2 = encodeSigningMessage(e, challenge2, authenticatorData, clientDataField); - - assert (challenge1 != challenge2) <=> (getSha256(e, message1) != getSha256(e, message2)); -} - /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ verifySignature functions are equivalent (Proved) │ From 7cdc8ac22b2b860cfe31c3d23741a5126ba8b55a Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Thu, 6 Jun 2024 16:51:01 +0300 Subject: [PATCH 071/103] remove munging --- .../harnesses/SafeWebAuthnSignerFactoryHarness.sol | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol b/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol index b14b24f23..872879470 100644 --- a/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol +++ b/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol @@ -30,10 +30,10 @@ contract SafeWebAuthnSignerFactoryHarness is SafeWebAuthnSignerFactory { magicValue = abi.decode(result, (bytes4)); } - /** - munge to pass the SignerCreationCantOverride rule. - */ - function _hasNoCode(address account) internal view override returns (bool result) { - return account.code.length > 0; - } + // /** + // munge to pass the SignerCreationCantOverride rule. + // */ + // function _hasNoCode(address account) internal view override returns (bool result) { + // return account.code.length > 0; + // } } From 2e1aaf71436a2f7239207a66338c1243d50349d2 Mon Sep 17 00:00:00 2001 From: liav-certora Date: Thu, 6 Jun 2024 17:05:38 +0300 Subject: [PATCH 072/103] . --- certora/confs/SafeWebAuthnSignerFactory.conf | 7 ---- .../confs/SafeWebAuthnSignerSingleton.conf | 3 ++ certora/specs/SafeWebAuthnSignerFactory.spec | 37 ++++++++++++++++++- .../specs/SafeWebAuthnSignerSingleton.spec | 15 ++++++++ 4 files changed, 53 insertions(+), 9 deletions(-) diff --git a/certora/confs/SafeWebAuthnSignerFactory.conf b/certora/confs/SafeWebAuthnSignerFactory.conf index 6dfe5b5f5..00cb576f3 100644 --- a/certora/confs/SafeWebAuthnSignerFactory.conf +++ b/certora/confs/SafeWebAuthnSignerFactory.conf @@ -7,14 +7,9 @@ "certora/harnesses/ERC20Like/DummyWeth.sol", "certora/harnesses/Utilities.sol", ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], "link": [ "SafeWebAuthnSignerFactoryHarness:SINGLETON=SafeWebAuthnSignerSingleton" ], - "msg": "sanity_with_all_default_summaries", - "process": "emv", "packages":[ "@safe-global=node_modules/@safe-global", "@account-abstraction=node_modules/@account-abstraction" @@ -26,7 +21,5 @@ "optimistic_hashing": true, "hashing_length_bound": "4694", "loop_iter": "144", - "prover_version": "master", - "server": "production", "verify": "SafeWebAuthnSignerFactoryHarness:certora/specs/SafeWebAuthnSignerFactory.spec" } \ No newline at end of file diff --git a/certora/confs/SafeWebAuthnSignerSingleton.conf b/certora/confs/SafeWebAuthnSignerSingleton.conf index cb5d77137..6ffa1786d 100644 --- a/certora/confs/SafeWebAuthnSignerSingleton.conf +++ b/certora/confs/SafeWebAuthnSignerSingleton.conf @@ -7,6 +7,9 @@ "modules/passkey/contracts/libraries/P256.sol", "certora/harnesses/WebAuthnHarness.sol", ], + "java_args": [ + " -ea -Dlevel.setup.helpers=info" + ], "packages":[ "@safe-global=node_modules/@safe-global", "@account-abstraction=node_modules/@account-abstraction" diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index 731f268c4..2528c52b1 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -104,16 +104,18 @@ rule deterministicSigner() ghost mathint numOfCreation; ghost mapping(address => uint) address_map; +ghost address createdAddress; ghost bool validValue; -hook EXTCODESIZE(address addr) uint v{ +hook EXTCODESIZE(address addr) uint v { require address_map[addr] == v; validValue = addr <= max_uint160; } -hook CREATE2(uint value, uint offset, uint length, bytes32 salt) address v{ +hook CREATE2(uint value, uint offset, uint length, bytes32 salt) address v { numOfCreation = numOfCreation + 1; address_map[v] = length; + createdAddress = v; } rule SignerCreationCantOverride() @@ -209,4 +211,35 @@ rule isValidSignatureForSignerConsistency() bytes4 magic2 = isValidSignatureForSigner(e, message, signature, x, y, verifier); assert (magic1 == MAGIC_VALUE()) <=> (magic2 == MAGIC_VALUE()); +} + +rule getSignerRevertingConditions { + env e; + uint256 x; + uint256 y; + P256.Verifiers verifiers; + + bool triedTransferringEth = e.msg.value != 0; + + getSigner@withrevert(e, x, y, verifiers); + + assert lastReverted <=> triedTransferringEth; +} + +rule createSignerRevertingConditions { + env e; + uint256 x; + uint256 y; + P256.Verifiers verifiers; + + address signer = getSigner(e, x, y, verifiers); + bool signerHasNoCode = hasNoCode(signer); + + bool triedTransferringEth = e.msg.value != 0; + + createSigner@withrevert(e, x, y, verifiers); + + bool notCreatedSigner = signerHasNoCode && (createdAddress != signer); + + assert lastReverted <=> (triedTransferringEth || notCreatedSigner); } \ No newline at end of file diff --git a/certora/specs/SafeWebAuthnSignerSingleton.spec b/certora/specs/SafeWebAuthnSignerSingleton.spec index c6bcfed38..07b61934f 100644 --- a/certora/specs/SafeWebAuthnSignerSingleton.spec +++ b/certora/specs/SafeWebAuthnSignerSingleton.spec @@ -121,3 +121,18 @@ rule verifyIsValidSignatureWillContinueToSucceed(){ assert (!firstReverted && !secondRevert) => (firstVerified == secondVerify); satisfy true; } + +rule isValidSignatureRevertingConditions { + env e; + bytes32 message; + + WebAuthn.Signature sigStruct; + bytes signature = WebAuthnHarness.encodeSignature(e, sigStruct); + + bool triedTransferringEth = e.msg.value != 0; + bool dataLengthInsufficient = sigStruct.authenticatorData.length <= 32; + + isValidSignature@withrevert(e, message, signature); + + assert lastReverted <=> (triedTransferringEth || dataLengthInsufficient); +} \ No newline at end of file From 8a5a2c7700ab1a2d7ac4ddf16d833f5a706d2add Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Thu, 6 Jun 2024 17:15:16 +0300 Subject: [PATCH 073/103] move signer rules --- certora/specs/GetSigner.spec | 19 +++++++++++++++++++ certora/specs/SafeWebAuthnSignerFactory.spec | 18 ------------------ 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/certora/specs/GetSigner.spec b/certora/specs/GetSigner.spec index 35c4bfb9a..4304f5d9e 100644 --- a/certora/specs/GetSigner.spec +++ b/certora/specs/GetSigner.spec @@ -67,4 +67,23 @@ rule uniqueSigner(){ address secondSigner = castToAddress(e, secondSignerValue); assert firstSigner == secondSigner <=> (firstX == secondX && firstY == secondY && firstVerifier == secondVerifier); +} + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Deterministic address in get signer (Proved) │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ +rule deterministicSigner() +{ + env e1; + env e2; + + uint x; + uint y; + P256.Verifiers verifier; + + address signer = getSigner(e1, x, y, verifier); + + assert signer == getSigner(e2, x, y, verifier); } \ No newline at end of file diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index 24ee0b423..2e2232236 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -57,24 +57,6 @@ rule createAndGetSignerEquivalence(){ assert signer1 == signer2 <=> (createX == getX && createY == getY && createVerifier == getVerifier); } -/* -┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ Deterministic address in get signer (Proved) │ -└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ -*/ -rule deterministicSigner() -{ - env e1; - env e2; - - uint x; - uint y; - P256.Verifiers verifier; - - address signer = getSigner(e1, x, y, verifier); - - assert signer == getSigner(e2, x, y, verifier); -} /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ From df3b264d834db0407ad5531ba7afe35b4e35bdb2 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Thu, 6 Jun 2024 17:23:34 +0300 Subject: [PATCH 074/103] . --- certora/specs/SafeWebAuthnSignerFactory.spec | 37 -------------------- 1 file changed, 37 deletions(-) diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index 2e2232236..1444edcaf 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -57,43 +57,6 @@ rule createAndGetSignerEquivalence(){ assert signer1 == signer2 <=> (createX == getX && createY == getY && createVerifier == getVerifier); } - -/* -┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ Correctness of Signer Creation. (Cant called twice and override) (Bug CERT-6252) │ -└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ -*/ - -ghost mathint numOfCreation; -ghost mapping(address => uint) address_map; - -hook EXTCODESIZE(address addr) uint v{ - require address_map[addr] == v; -} - -hook CREATE2(uint value, uint offset, uint length, bytes32 salt) address v{ - numOfCreation = numOfCreation + 1; - address_map[v] = length; -} - -rule SignerCreationCantOverride() -{ - env e; - require numOfCreation == 0; - - uint x; - uint y; - P256.Verifiers verifier; - - address a = getSigner(e, x, y, verifier); - require address_map[a] == 0; - - createSigner(e, x, y, verifier); - createSigner@withrevert(e, x, y, verifier); - - assert numOfCreation < 2; -} - /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Has no code integrity (Proved) │ From 051e1547cfb31dd475a49edbf2034d728ec251dc Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Thu, 6 Jun 2024 18:21:07 +0300 Subject: [PATCH 075/103] Add missing rules --- certora/specs/SafeWebAuthnSignerFactory.spec | 101 +++++++++++++++++++ certora/specs/WebAuthn.spec | 21 ++++ 2 files changed, 122 insertions(+) diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index 1444edcaf..b9d5dc833 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -57,6 +57,61 @@ rule createAndGetSignerEquivalence(){ assert signer1 == signer2 <=> (createX == getX && createY == getY && createVerifier == getVerifier); } +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Deterministic address in get signer (Proved) │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ +rule deterministicSigner() +{ + env e1; + env e2; + + uint x; + uint y; + P256.Verifiers verifier; + + address signer = getSigner(e1, x, y, verifier); + + assert signer == getSigner(e2, x, y, verifier); +} + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Correctness of Signer Creation. (Cant called twice and override) (Bug CERT-6252) │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ + +ghost mathint numOfCreation; +ghost mapping(address => uint) address_map; + +hook EXTCODESIZE(address addr) uint v{ + require address_map[addr] == v; +} + +hook CREATE2(uint value, uint offset, uint length, bytes32 salt) address v{ + numOfCreation = numOfCreation + 1; + address_map[v] = length; +} + +rule SignerCreationCantOverride() +{ + env e; + require numOfCreation == 0; + + uint x; + uint y; + P256.Verifiers verifier; + + address a = getSigner(e, x, y, verifier); + require address_map[a] == 0; + + createSigner(e, x, y, verifier); + createSigner@withrevert(e, x, y, verifier); + + assert numOfCreation < 2; +} + /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Has no code integrity (Proved) │ @@ -68,6 +123,30 @@ rule hasNoCodeIntegrity() assert (a == proxy) => !hasNoCode(a); } +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ isValidSignatureForSigner equiv to first deploying the signer with the factory, and then | +| verifying the signature with it directly (CERT-6221) │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ +rule createAndVerifyEQtoIsValidSignatureForSigner() +{ + env e; + uint x; + uint y; + P256.Verifiers verifier; + bytes signature; + bytes32 message; + + storage s = lastStorage; + + bytes4 magic1 = isValidSignatureForSigner(e, message, signature, x, y, verifier); + + bytes4 magic2 = createAndVerify(e, message, signature, x, y, verifier) at s; + + assert magic1 == magic2; +} + /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ isValidSignatureForSigner Consistency (Proved) │ @@ -101,4 +180,26 @@ rule isValidSignatureForSignerConsistency() assert firstRevert == secondRevert; assert (!firstRevert && !secondRevert) => (magic1 == MAGIC_VALUE()) <=> (magic2 == MAGIC_VALUE()); +} + + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ isValidSignatureForSigner Integrity (Violated) │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ + +rule isValidSignatureForSignerIntegrity() +{ + env e; + + uint x; + uint y; + P256.Verifiers verifier; + bytes signature; + bytes32 message; + + bytes4 magic1 = isValidSignatureForSigner(e, message, signature, x, y, verifier); + + satisfy magic1 == MAGIC_VALUE(); } \ No newline at end of file diff --git a/certora/specs/WebAuthn.spec b/certora/specs/WebAuthn.spec index f59e6818a..4f394a3aa 100644 --- a/certora/specs/WebAuthn.spec +++ b/certora/specs/WebAuthn.spec @@ -44,6 +44,27 @@ rule shaIntegrity(){ assert (keccak256(input1) != keccak256(input2)) <=> input1_sha != input2_sha; } + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ every 2 challenges results in unique message when using encodeSigningMessage (Timeout cert-6290) │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ +rule uniqueMessagePerChallenge(){ + env e; + + bytes32 challenge1; + bytes32 challenge2; + bytes authenticatorData; + require authenticatorData.length % 32 == 0; + string clientDataField; + + bytes message1 = encodeSigningMessage(e, challenge1, authenticatorData, clientDataField); + bytes message2 = encodeSigningMessage(e, challenge2, authenticatorData, clientDataField); + + assert (challenge1 != challenge2) <=> (getSha256(e, message1) != getSha256(e, message2)); +} + /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ verifySignature functions are equivalent (Proved) │ From 7db212dd7e1316b05d727dcc54caed8c2a68bfdb Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Thu, 6 Jun 2024 18:22:34 +0300 Subject: [PATCH 076/103] Small clean --- certora/specs/SafeWebAuthnSignerFactory.spec | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index b9d5dc833..ed7df337d 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -57,25 +57,6 @@ rule createAndGetSignerEquivalence(){ assert signer1 == signer2 <=> (createX == getX && createY == getY && createVerifier == getVerifier); } -/* -┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ Deterministic address in get signer (Proved) │ -└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ -*/ -rule deterministicSigner() -{ - env e1; - env e2; - - uint x; - uint y; - P256.Verifiers verifier; - - address signer = getSigner(e1, x, y, verifier); - - assert signer == getSigner(e2, x, y, verifier); -} - /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Correctness of Signer Creation. (Cant called twice and override) (Bug CERT-6252) │ From c81f4a6fd1b45014eb90679fedc1288b98335d66 Mon Sep 17 00:00:00 2001 From: liav-certora Date: Sun, 9 Jun 2024 10:53:04 +0300 Subject: [PATCH 077/103] Update SafeWebAuthnSignerSingleton.conf --- certora/confs/SafeWebAuthnSignerSingleton.conf | 3 --- 1 file changed, 3 deletions(-) diff --git a/certora/confs/SafeWebAuthnSignerSingleton.conf b/certora/confs/SafeWebAuthnSignerSingleton.conf index 6ffa1786d..cb5d77137 100644 --- a/certora/confs/SafeWebAuthnSignerSingleton.conf +++ b/certora/confs/SafeWebAuthnSignerSingleton.conf @@ -7,9 +7,6 @@ "modules/passkey/contracts/libraries/P256.sol", "certora/harnesses/WebAuthnHarness.sol", ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], "packages":[ "@safe-global=node_modules/@safe-global", "@account-abstraction=node_modules/@account-abstraction" From d208c5a331fe43a58392b935b955999e325d6e87 Mon Sep 17 00:00:00 2001 From: liav-certora Date: Sun, 9 Jun 2024 12:04:14 +0300 Subject: [PATCH 078/103] Update SafeWebAuthnSignerFactory.spec --- certora/specs/SafeWebAuthnSignerFactory.spec | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index 2528c52b1..e93587d9e 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -104,7 +104,6 @@ rule deterministicSigner() ghost mathint numOfCreation; ghost mapping(address => uint) address_map; -ghost address createdAddress; ghost bool validValue; hook EXTCODESIZE(address addr) uint v { @@ -115,7 +114,6 @@ hook EXTCODESIZE(address addr) uint v { hook CREATE2(uint value, uint offset, uint length, bytes32 salt) address v { numOfCreation = numOfCreation + 1; address_map[v] = length; - createdAddress = v; } rule SignerCreationCantOverride() @@ -225,21 +223,3 @@ rule getSignerRevertingConditions { assert lastReverted <=> triedTransferringEth; } - -rule createSignerRevertingConditions { - env e; - uint256 x; - uint256 y; - P256.Verifiers verifiers; - - address signer = getSigner(e, x, y, verifiers); - bool signerHasNoCode = hasNoCode(signer); - - bool triedTransferringEth = e.msg.value != 0; - - createSigner@withrevert(e, x, y, verifiers); - - bool notCreatedSigner = signerHasNoCode && (createdAddress != signer); - - assert lastReverted <=> (triedTransferringEth || notCreatedSigner); -} \ No newline at end of file From 021d60d803f5a39c5f957fd4ce741cdc4f5c5e70 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Mon, 10 Jun 2024 17:27:30 +0300 Subject: [PATCH 079/103] Fix proxy return rule at least on john branch --- certora/confs/ProxySimulator.conf | 4 ++++ certora/specs/ProxySimulator.spec | 36 ++++++++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/certora/confs/ProxySimulator.conf b/certora/confs/ProxySimulator.conf index bc56bcf06..1dddbee43 100644 --- a/certora/confs/ProxySimulator.conf +++ b/certora/confs/ProxySimulator.conf @@ -5,6 +5,9 @@ "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol", "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", "modules/passkey/contracts/libraries/P256.sol", + "modules/passkey/contracts/libraries/WebAuthn.sol", + "certora/harnesses/WebAuthnHarness.sol", + ], "link": [ "ProxySimulator:_proxy=SafeWebAuthnSignerProxy", @@ -20,6 +23,7 @@ "optimistic_loop": true, "loop_iter": "6", "optimistic_hashing": true, + "hashing_length_bound": "2048", "rule_sanity": "basic", "verify": "ProxySimulator:certora/specs/ProxySimulator.spec" } \ No newline at end of file diff --git a/certora/specs/ProxySimulator.spec b/certora/specs/ProxySimulator.spec index a8405eeff..43cf3a30d 100644 --- a/certora/specs/ProxySimulator.spec +++ b/certora/specs/ProxySimulator.spec @@ -1,4 +1,33 @@ using SafeWebAuthnSignerProxy as SafeWebAuthnSignerProxy; +using WebAuthnHarness as WebAuthnHarness; + +methods { + function P256.verifySignatureAllowMalleability(P256.Verifiers a, bytes32 b, uint256 c, uint256 d, uint256 e, uint256 f) internal returns (bool) => + verifySignatureAllowMalleabilityGhost(a, b, c, d, e, f); + + function WebAuthn.encodeSigningMessage(bytes32 challenge, bytes calldata authenticatorData, string calldata clientDataFields) internal returns (bytes memory) => + GETencodeSigningMessageCVL(challenge, authenticatorData, clientDataFields); + + function WebAuthnHarness.checkInjective(bytes32 challenge, bytes32 authenticatorData, bytes32 clientDataFields, bytes32 result) internal returns (bool) => + checkInjectiveSummary(challenge, authenticatorData, clientDataFields, result); +} + +function GETencodeSigningMessageCVL(bytes32 challenge, bytes authenticatorData, string clientDataFields) returns bytes +{ + env e; + return WebAuthnHarness.GETencodeSigningMessageSummary(e, challenge, authenticatorData, clientDataFields); +} + +ghost checkInjectiveSummary(bytes32, bytes32, bytes32, bytes32) returns bool { + axiom forall bytes32 x1. forall bytes32 y1. forall bytes32 z1. forall bytes32 x2. forall bytes32 y2. forall bytes32 z2. forall bytes32 result. + checkInjectiveSummary(x1, y1, z1, result) && checkInjectiveSummary(x2, y2, z2, result) => x1 == x2; +} + +ghost verifySignatureAllowMalleabilityGhost(P256.Verifiers, bytes32, uint256, uint256, uint256, uint256) returns bool { + axiom forall P256.Verifiers a. forall bytes32 message1. forall bytes32 message2. forall uint256 c. forall uint256 d. forall uint256 e. forall uint256 f. + verifySignatureAllowMalleabilityGhost(a, message1, c, d, e, f) && + verifySignatureAllowMalleabilityGhost(a, message2, c, d, e, f) => message1 == message2; +} // This is the same MAGIC_VALUE constant used in ERC1271. definition MAGIC_VALUE() returns bytes4 = to_bytes4(0x1626ba7e); @@ -6,14 +35,15 @@ definition MAGIC_VALUE() returns bytes4 = to_bytes4(0x1626ba7e); methods { function authenticate(bytes32, bytes) external returns (bytes4) envfree; function _._ external => DISPATCH [ - SafeWebAuthnSignerProxy._ + SafeWebAuthnSignerProxy._, + SafeWebAuthnSignerSingleton._ ] default NONDET; } /* Property 14. Proxy - verify return data from the fallback is only one of the magicNumbers Uses another contract that simulates interaction with the proxy. The reason is that the prover doesn't check all -possible calldata values so this simualtion will make the prover choose different values that will be passed on the calldata. +possible calldata values so this simulation will make the prover choose different values that will be passed on the calldata. Rule stuck. */ rule proxyReturnValue { @@ -22,5 +52,5 @@ rule proxyReturnValue { bytes4 ret = authenticate(message, signature); - assert ret == MAGIC_VALUE(); + satisfy ret == MAGIC_VALUE() || ret == to_bytes4(0); } From aebe38c4bf9e228d248d732dba235d0ccd5ab44e Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Thu, 13 Jun 2024 10:55:09 +0300 Subject: [PATCH 080/103] Add summaries to factory --- certora/confs/SafeWebAuthnSignerFactory.conf | 3 +++ certora/specs/SafeWebAuthnSignerFactory.spec | 28 ++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/certora/confs/SafeWebAuthnSignerFactory.conf b/certora/confs/SafeWebAuthnSignerFactory.conf index ab76c81e4..697e519e2 100644 --- a/certora/confs/SafeWebAuthnSignerFactory.conf +++ b/certora/confs/SafeWebAuthnSignerFactory.conf @@ -4,6 +4,9 @@ "certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol", "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol", + "modules/passkey/contracts/libraries/WebAuthn.sol", + "modules/passkey/contracts/libraries/P256.sol", + "certora/harnesses/WebAuthnHarness.sol", ], "link": [ "SafeWebAuthnSignerFactoryHarness:SINGLETON=SafeWebAuthnSignerSingleton" diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index ada6eeb81..240dc2ee4 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -1,10 +1,38 @@ using SafeWebAuthnSignerProxy as proxy; using SafeWebAuthnSignerSingleton as singleton; +using WebAuthnHarness as WebAuthnHarness; + methods{ function getSigner(uint256 x, uint256 y, P256.Verifiers v) internal returns (address) => getSignerGhost(x, y, v); function createSigner(uint256, uint256, P256.Verifiers) external returns (address); function hasNoCode(address) external returns (bool) envfree; + + function P256.verifySignatureAllowMalleability(P256.Verifiers a, bytes32 b, uint256 c, uint256 d, uint256 e, uint256 f) internal returns (bool) => + verifySignatureAllowMalleabilityGhost(a, b, c, d, e, f); + + function WebAuthn.encodeSigningMessage(bytes32 challenge, bytes calldata authenticatorData, string calldata clientDataFields) internal returns (bytes memory) => + GETencodeSigningMessageCVL(challenge, authenticatorData, clientDataFields); + + function WebAuthnHarness.checkInjective(bytes32 challenge, bytes32 authenticatorData, bytes32 clientDataFields, bytes32 result) internal returns (bool) => + checkInjectiveSummary(challenge, authenticatorData, clientDataFields, result); +} + +function GETencodeSigningMessageCVL(bytes32 challenge, bytes authenticatorData, string clientDataFields) returns bytes +{ + env e; + return WebAuthnHarness.GETencodeSigningMessageSummary(e, challenge, authenticatorData, clientDataFields); +} + +ghost checkInjectiveSummary(bytes32, bytes32, bytes32, bytes32) returns bool { + axiom forall bytes32 x1. forall bytes32 y1. forall bytes32 z1. forall bytes32 x2. forall bytes32 y2. forall bytes32 z2. forall bytes32 result. + checkInjectiveSummary(x1, y1, z1, result) && checkInjectiveSummary(x2, y2, z2, result) => x1 == x2; +} + +ghost verifySignatureAllowMalleabilityGhost(P256.Verifiers, bytes32, uint256, uint256, uint256, uint256) returns bool { + axiom forall P256.Verifiers a. forall bytes32 message1. forall bytes32 message2. forall uint256 c. forall uint256 d. forall uint256 e. forall uint256 f. + verifySignatureAllowMalleabilityGhost(a, message1, c, d, e, f) && + verifySignatureAllowMalleabilityGhost(a, message2, c, d, e, f) => message1 == message2; } // Summary is correct only if the unique signer rule is proved spec GetSigner From b50be18eac775e270156f99aa60ab8ad7ef4b9ea Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Thu, 13 Jun 2024 10:57:08 +0300 Subject: [PATCH 081/103] Fix --- certora/specs/ProxySimulator.spec | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/certora/specs/ProxySimulator.spec b/certora/specs/ProxySimulator.spec index 43cf3a30d..ab8f4ea9f 100644 --- a/certora/specs/ProxySimulator.spec +++ b/certora/specs/ProxySimulator.spec @@ -10,6 +10,12 @@ methods { function WebAuthnHarness.checkInjective(bytes32 challenge, bytes32 authenticatorData, bytes32 clientDataFields, bytes32 result) internal returns (bool) => checkInjectiveSummary(challenge, authenticatorData, clientDataFields, result); + + function authenticate(bytes32, bytes) external returns (bytes4) envfree; + function _._ external => DISPATCH [ + SafeWebAuthnSignerProxy._, + SafeWebAuthnSignerSingleton._ + ] default NONDET; } function GETencodeSigningMessageCVL(bytes32 challenge, bytes authenticatorData, string clientDataFields) returns bytes @@ -32,14 +38,6 @@ ghost verifySignatureAllowMalleabilityGhost(P256.Verifiers, bytes32, uint256, ui // This is the same MAGIC_VALUE constant used in ERC1271. definition MAGIC_VALUE() returns bytes4 = to_bytes4(0x1626ba7e); -methods { - function authenticate(bytes32, bytes) external returns (bytes4) envfree; - function _._ external => DISPATCH [ - SafeWebAuthnSignerProxy._, - SafeWebAuthnSignerSingleton._ - ] default NONDET; -} - /* Property 14. Proxy - verify return data from the fallback is only one of the magicNumbers Uses another contract that simulates interaction with the proxy. The reason is that the prover doesn't check all From 321c050d9890219b793917a15d192ce4a305034f Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Thu, 13 Jun 2024 11:01:56 +0300 Subject: [PATCH 082/103] Try to fix unresolved call to isValidSignature via proxy fallback with dispatch list --- certora/specs/SafeWebAuthnSignerFactory.spec | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index 240dc2ee4..ef22f2627 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -16,6 +16,11 @@ methods{ function WebAuthnHarness.checkInjective(bytes32 challenge, bytes32 authenticatorData, bytes32 clientDataFields, bytes32 result) internal returns (bool) => checkInjectiveSummary(challenge, authenticatorData, clientDataFields, result); + + function _._ external => DISPATCH [ + proxy._, + singleton._ + ] default NONDET; } function GETencodeSigningMessageCVL(bytes32 challenge, bytes authenticatorData, string clientDataFields) returns bytes From ed1bf4d966a1f30b754dba42e15ac3ee0b87fbbb Mon Sep 17 00:00:00 2001 From: liav-certora Date: Thu, 13 Jun 2024 16:58:38 +0300 Subject: [PATCH 083/103] cleanup --- certora/confs/ProxySimulator.conf | 29 ---------- .../SafeWebAuthnSignerFactoryHarness.sol | 7 --- certora/helpers/ProxySimulator.sol | 24 -------- certora/specs/ProxySimulator.spec | 56 ------------------- .../specs/SafeWebAuthnSignerSingleton.spec | 38 ------------- 5 files changed, 154 deletions(-) delete mode 100644 certora/confs/ProxySimulator.conf delete mode 100644 certora/helpers/ProxySimulator.sol delete mode 100644 certora/specs/ProxySimulator.spec diff --git a/certora/confs/ProxySimulator.conf b/certora/confs/ProxySimulator.conf deleted file mode 100644 index 1dddbee43..000000000 --- a/certora/confs/ProxySimulator.conf +++ /dev/null @@ -1,29 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "certora/helpers/ProxySimulator.sol", - "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol", - "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", - "modules/passkey/contracts/libraries/P256.sol", - "modules/passkey/contracts/libraries/WebAuthn.sol", - "certora/harnesses/WebAuthnHarness.sol", - - ], - "link": [ - "ProxySimulator:_proxy=SafeWebAuthnSignerProxy", - "SafeWebAuthnSignerProxy:_VERIFIERS=P256", - "SafeWebAuthnSignerProxy:_SINGLETON=SafeWebAuthnSignerSingleton" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "optimistic_loop": true, - "loop_iter": "6", - "optimistic_hashing": true, - "hashing_length_bound": "2048", - "rule_sanity": "basic", - "verify": "ProxySimulator:certora/specs/ProxySimulator.spec" -} \ No newline at end of file diff --git a/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol b/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol index 872879470..26ae793d7 100644 --- a/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol +++ b/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol @@ -29,11 +29,4 @@ contract SafeWebAuthnSignerFactoryHarness is SafeWebAuthnSignerFactory { require(success); magicValue = abi.decode(result, (bytes4)); } - - // /** - // munge to pass the SignerCreationCantOverride rule. - // */ - // function _hasNoCode(address account) internal view override returns (bool result) { - // return account.code.length > 0; - // } } diff --git a/certora/helpers/ProxySimulator.sol b/certora/helpers/ProxySimulator.sol deleted file mode 100644 index d9decc29f..000000000 --- a/certora/helpers/ProxySimulator.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -/* solhint-disable no-complex-fallback */ -pragma solidity >=0.8.0; - -import {SafeWebAuthnSignerProxy} from "../../modules/passkey/contracts/SafeWebAuthnSignerProxy.sol"; - -contract ProxySimulator { - - address internal _proxy; - - constructor(address proxy) { - _proxy = proxy; - } - - function authenticate(bytes32 message, bytes calldata signature) external returns (bytes4) { - bytes memory data = abi.encodeWithSignature("isValidSignature(bytes32,bytes)", message, signature); - - (bool success, bytes memory result) = _proxy.call(data); - - require(success); - - return abi.decode(result, (bytes4)); - } -} \ No newline at end of file diff --git a/certora/specs/ProxySimulator.spec b/certora/specs/ProxySimulator.spec deleted file mode 100644 index 43cf3a30d..000000000 --- a/certora/specs/ProxySimulator.spec +++ /dev/null @@ -1,56 +0,0 @@ -using SafeWebAuthnSignerProxy as SafeWebAuthnSignerProxy; -using WebAuthnHarness as WebAuthnHarness; - -methods { - function P256.verifySignatureAllowMalleability(P256.Verifiers a, bytes32 b, uint256 c, uint256 d, uint256 e, uint256 f) internal returns (bool) => - verifySignatureAllowMalleabilityGhost(a, b, c, d, e, f); - - function WebAuthn.encodeSigningMessage(bytes32 challenge, bytes calldata authenticatorData, string calldata clientDataFields) internal returns (bytes memory) => - GETencodeSigningMessageCVL(challenge, authenticatorData, clientDataFields); - - function WebAuthnHarness.checkInjective(bytes32 challenge, bytes32 authenticatorData, bytes32 clientDataFields, bytes32 result) internal returns (bool) => - checkInjectiveSummary(challenge, authenticatorData, clientDataFields, result); -} - -function GETencodeSigningMessageCVL(bytes32 challenge, bytes authenticatorData, string clientDataFields) returns bytes -{ - env e; - return WebAuthnHarness.GETencodeSigningMessageSummary(e, challenge, authenticatorData, clientDataFields); -} - -ghost checkInjectiveSummary(bytes32, bytes32, bytes32, bytes32) returns bool { - axiom forall bytes32 x1. forall bytes32 y1. forall bytes32 z1. forall bytes32 x2. forall bytes32 y2. forall bytes32 z2. forall bytes32 result. - checkInjectiveSummary(x1, y1, z1, result) && checkInjectiveSummary(x2, y2, z2, result) => x1 == x2; -} - -ghost verifySignatureAllowMalleabilityGhost(P256.Verifiers, bytes32, uint256, uint256, uint256, uint256) returns bool { - axiom forall P256.Verifiers a. forall bytes32 message1. forall bytes32 message2. forall uint256 c. forall uint256 d. forall uint256 e. forall uint256 f. - verifySignatureAllowMalleabilityGhost(a, message1, c, d, e, f) && - verifySignatureAllowMalleabilityGhost(a, message2, c, d, e, f) => message1 == message2; -} - -// This is the same MAGIC_VALUE constant used in ERC1271. -definition MAGIC_VALUE() returns bytes4 = to_bytes4(0x1626ba7e); - -methods { - function authenticate(bytes32, bytes) external returns (bytes4) envfree; - function _._ external => DISPATCH [ - SafeWebAuthnSignerProxy._, - SafeWebAuthnSignerSingleton._ - ] default NONDET; -} - -/* -Property 14. Proxy - verify return data from the fallback is only one of the magicNumbers -Uses another contract that simulates interaction with the proxy. The reason is that the prover doesn't check all -possible calldata values so this simulation will make the prover choose different values that will be passed on the calldata. -Rule stuck. -*/ -rule proxyReturnValue { - bytes32 message; - bytes signature; - - bytes4 ret = authenticate(message, signature); - - satisfy ret == MAGIC_VALUE() || ret == to_bytes4(0); -} diff --git a/certora/specs/SafeWebAuthnSignerSingleton.spec b/certora/specs/SafeWebAuthnSignerSingleton.spec index 07b61934f..45bef619b 100644 --- a/certora/specs/SafeWebAuthnSignerSingleton.spec +++ b/certora/specs/SafeWebAuthnSignerSingleton.spec @@ -84,44 +84,6 @@ rule verifyIsValidSignatureAreEqual(env e){ (magicValue_hashed == to_bytes4(0) && magicValue_message == to_bytes4(0)); } -/* -┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ Once signer passed isValidSignature it will never fail on it after any call │ -└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ -*/ - -rule verifyIsValidSignatureWillContinueToSucceed(){ - env e; - env e1; - env e2; - env e3; - require e1.msg.value == 0 && e2.msg.value == 0 && e3.msg.value == 0; - - method f; - calldataarg args; - - bytes32 message; - WebAuthn.Signature sigStruct; - bytes signature = WebAuthnHarness.encodeSignature(e, sigStruct); - - bytes32 message3; - WebAuthn.Signature sigStruct3; - bytes signature3 = WebAuthnHarness.encodeSignature(e, sigStruct3); - - bytes4 firstVerified = isValidSignature@withrevert(e1, message, signature); - bool firstReverted = lastReverted; - - f(e, args); - // isValidSignature(e3, message3, signature3); - - bytes4 secondVerify = isValidSignature@withrevert(e2, message, signature); - bool secondRevert = lastReverted; - - assert firstReverted == secondRevert; - assert (!firstReverted && !secondRevert) => (firstVerified == secondVerify); - satisfy true; -} - rule isValidSignatureRevertingConditions { env e; bytes32 message; From 37050dd9af6cb3e016e71314d690f7326bef0830 Mon Sep 17 00:00:00 2001 From: liav-certora Date: Thu, 13 Jun 2024 17:05:16 +0300 Subject: [PATCH 084/103] proxy duplicate --- .../confs/SafeWebAuthnSignerProxyHarness.conf | 23 -------- .../SafeWebAuthnSignerProxyHarness.sol | 53 ------------------- .../specs/SafeWebAuthnSignerProxyHarness.spec | 21 -------- 3 files changed, 97 deletions(-) delete mode 100644 certora/confs/SafeWebAuthnSignerProxyHarness.conf delete mode 100644 certora/harnesses/SafeWebAuthnSignerProxyHarness.sol delete mode 100644 certora/specs/SafeWebAuthnSignerProxyHarness.spec diff --git a/certora/confs/SafeWebAuthnSignerProxyHarness.conf b/certora/confs/SafeWebAuthnSignerProxyHarness.conf deleted file mode 100644 index 88193a0dd..000000000 --- a/certora/confs/SafeWebAuthnSignerProxyHarness.conf +++ /dev/null @@ -1,23 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "certora/harnesses/SafeWebAuthnSignerProxyHarness.sol", - "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", - "modules/passkey/contracts/libraries/P256.sol" - ], - "link": [ - "SafeWebAuthnSignerProxyHarness:_VERIFIERS=P256", - "SafeWebAuthnSignerProxyHarness:_SINGLETON=SafeWebAuthnSignerSingleton" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "optimistic_loop": true, - "loop_iter": "6", - "optimistic_hashing": true, - "rule_sanity": "basic", - "verify": "SafeWebAuthnSignerProxyHarness:certora/specs/SafeWebAuthnSignerProxyHarness.spec" -} \ No newline at end of file diff --git a/certora/harnesses/SafeWebAuthnSignerProxyHarness.sol b/certora/harnesses/SafeWebAuthnSignerProxyHarness.sol deleted file mode 100644 index 29f9500f9..000000000 --- a/certora/harnesses/SafeWebAuthnSignerProxyHarness.sol +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -/* solhint-disable no-complex-fallback */ -pragma solidity >=0.8.0; - -import {P256} from "../../modules/passkey/contracts/libraries/WebAuthn.sol"; -import {SafeWebAuthnSignerProxy} from "../../modules/passkey/contracts/SafeWebAuthnSignerProxy.sol"; - -/** - * @title Safe WebAuthn Signer Proxy Harness - * @dev This harness is written to be able to prove a certain property on the fallback function. - * The property we are proving using this harness is that no combination of x, y and verifiers can make the fallback revert - * due the problems with the untrivial data appanding. - * It adds another function `fallbackButNotDelegating which has the exact same functionality as the original fallback - * but it gets the x, y, and verifiers parameters instead of reading the immutable ones and does not make a delegatecall. - */ -contract SafeWebAuthnSignerProxyHarness is SafeWebAuthnSignerProxy { - /** - * @notice Creates a new WebAuthn Safe Signer Proxy. - * @param singleton The {SafeWebAuthnSignerSingleton} implementation to proxy to. - * @param x The x coordinate of the P-256 public key of the WebAuthn credential. - * @param y The y coordinate of the P-256 public key of the WebAuthn credential. - * @param verifiers The P-256 verifiers used for ECDSA signature verification. - */ - constructor(address singleton, uint256 x, uint256 y, P256.Verifiers verifiers) - SafeWebAuthnSignerProxy(singleton, x, y, verifiers) { } - - /** - * @dev Fallback function forwards all transactions and returns all received return data. - */ - function fallbackButNotDelegating(uint256 x, uint256 y, P256.Verifiers verifiers) external payable returns (bytes4) { - // solhint-disable-next-line no-inline-assembly - assembly { - // Forward the call to the singleton implementation. We append the configuration to the - // calldata instead of having the singleton implementation read it from storage. This is - // both more gas efficient and required for ERC-4337 compatibility. Note that we append - // the configuration fields in reverse order since the fields are packed, and this makes - // it so we don't need to mask any bits from the `verifiers` value. This computes `data` - // to be `abi.encodePacked(msg.data, x, y, verifiers)`. - let data := mload(0x40) - mstore(add(data, add(calldatasize(), 0x36)), verifiers) - mstore(add(data, add(calldatasize(), 0x20)), y) - mstore(add(data, calldatasize()), x) - calldatacopy(data, 0x00, calldatasize()) - - let success := true - returndatacopy(0, 0, returndatasize()) - if iszero(success) { - revert(0, returndatasize()) - } - return(0, returndatasize()) - } - } -} diff --git a/certora/specs/SafeWebAuthnSignerProxyHarness.spec b/certora/specs/SafeWebAuthnSignerProxyHarness.spec deleted file mode 100644 index 05f8b9302..000000000 --- a/certora/specs/SafeWebAuthnSignerProxyHarness.spec +++ /dev/null @@ -1,21 +0,0 @@ -methods { - function fallbackButNotDelegating(uint256,uint256,P256.Verifiers) external returns (bytes4); -} - -/* -Property 13. Proxy - Fallback data corruption does not revert the fallback (uses data appending that needed to be verified). -The fallback will only revert due to payable modifier. -Rule Verified. -*/ -rule fallbackDoesNotRevert { - env e; - uint256 x; - uint256 y; - P256.Verifiers verifiers; - - bool notEnoughEthSender = e.msg.value > nativeBalances[e.msg.sender]; - - fallbackButNotDelegating@withrevert(e, x, y, verifiers); - - assert lastReverted <=> notEnoughEthSender; -} \ No newline at end of file From e6f8e21f3ed02880597023e8a4dc6946bf210eb3 Mon Sep 17 00:00:00 2001 From: liav-certora Date: Thu, 13 Jun 2024 19:30:48 +0300 Subject: [PATCH 085/103] . --- certora/confs/SafeWebAuthnSignerSingleton.conf | 2 -- certora/confs/WebAuthn.conf | 7 ------- 2 files changed, 9 deletions(-) diff --git a/certora/confs/SafeWebAuthnSignerSingleton.conf b/certora/confs/SafeWebAuthnSignerSingleton.conf index cb5d77137..2d8edd7c1 100644 --- a/certora/confs/SafeWebAuthnSignerSingleton.conf +++ b/certora/confs/SafeWebAuthnSignerSingleton.conf @@ -17,7 +17,5 @@ "optimistic_loop": true, "loop_iter": "6", "optimistic_hashing": true, - "prover_version": "master", - "server": "production", "verify": "SafeWebAuthnSignerSingleton:certora/specs/SafeWebAuthnSignerSingleton.spec" } \ No newline at end of file diff --git a/certora/confs/WebAuthn.conf b/certora/confs/WebAuthn.conf index 06ce73680..c6134961c 100644 --- a/certora/confs/WebAuthn.conf +++ b/certora/confs/WebAuthn.conf @@ -3,11 +3,6 @@ "files": [ "certora/harnesses/WebAuthnHarness.sol" ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity_with_all_default_summaries", - "process": "emv", "packages":[ "@safe-global=node_modules/@safe-global", "@account-abstraction=node_modules/@account-abstraction" @@ -16,8 +11,6 @@ "solc_via_ir": false, "optimistic_loop": true, "optimistic_hashing": true, - "prover_version": "master", - "server": "production", "rule_sanity": "basic", "loop_iter": "6", "verify": "WebAuthnHarness:certora/specs/WebAuthn.spec" From dd5d83cfd1cdba505b2c2ee18053ec8e5b5f146f Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Sun, 16 Jun 2024 10:56:01 +0300 Subject: [PATCH 086/103] Add john requirements, proving the rule only for the first time proxy is created. --- certora/harnesses/WebAuthnHarness.sol | 5 +++++ certora/specs/SafeWebAuthnSignerFactory.spec | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/certora/harnesses/WebAuthnHarness.sol b/certora/harnesses/WebAuthnHarness.sol index 4ac43aab0..da7509a54 100644 --- a/certora/harnesses/WebAuthnHarness.sol +++ b/certora/harnesses/WebAuthnHarness.sol @@ -45,6 +45,11 @@ contract WebAuthnHarness { return WebAuthn.castSignature(signature); } + function castSignatureSuccess(bytes32 unused, bytes calldata signature) external pure returns (bool) { + (bool isValid, ) = WebAuthn.castSignature(signature); + return isValid; + } + function castSignature_notreturns(bytes calldata signature) external pure { WebAuthn.castSignature(signature); } diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index ef22f2627..2a18c0066 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -152,13 +152,19 @@ rule createAndVerifyEQtoIsValidSignatureForSigner() bytes signature; bytes32 message; + signerAddress = getSigner(e, x, y, verifier); + require(numOfCreation == 0); + require(hasNoCode(e, signerAddress)); + require(WebAuthnHarness.castSignatureSuccess(e, message, signature)); + + storage s = lastStorage; bytes4 magic1 = isValidSignatureForSigner(e, message, signature, x, y, verifier); bytes4 magic2 = createAndVerify(e, message, signature, x, y, verifier) at s; - assert magic1 == magic2; + assert magic1 == magic2 && numOfCreation == 1; } /* From 306ff0d02d892ee9e5880468e0f80cd541271666 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Sun, 16 Jun 2024 10:57:42 +0300 Subject: [PATCH 087/103] small fix --- certora/specs/SafeWebAuthnSignerFactory.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index 2a18c0066..9295f982b 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -152,7 +152,7 @@ rule createAndVerifyEQtoIsValidSignatureForSigner() bytes signature; bytes32 message; - signerAddress = getSigner(e, x, y, verifier); + address signerAddress = getSigner(e, x, y, verifier); require(numOfCreation == 0); require(hasNoCode(e, signerAddress)); require(WebAuthnHarness.castSignatureSuccess(e, message, signature)); From 25686915bc0dd0af51326049969e1f2677552139 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Sun, 16 Jun 2024 12:05:20 +0300 Subject: [PATCH 088/103] Add conf with john configuration for createAndVerifyEQtoIsValidSignatureForSigner ruel --- ...ndVerifyEQtoIsValidSignatureForSigner.conf | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 certora/confs/createAndVerifyEQtoIsValidSignatureForSigner.conf diff --git a/certora/confs/createAndVerifyEQtoIsValidSignatureForSigner.conf b/certora/confs/createAndVerifyEQtoIsValidSignatureForSigner.conf new file mode 100644 index 000000000..d02af0b96 --- /dev/null +++ b/certora/confs/createAndVerifyEQtoIsValidSignatureForSigner.conf @@ -0,0 +1,34 @@ +{ + "assert_autofinder_success": true, + "dynamic_bound": "1", + "files": [ + "certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol", + "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", + "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol", + "modules/passkey/contracts/libraries/WebAuthn.sol", + "modules/passkey/contracts/libraries/P256.sol", + "certora/harnesses/WebAuthnHarness.sol" + ], + "hashing_length_bound": "906", + "link": [ + "SafeWebAuthnSignerFactoryHarness:SINGLETON=SafeWebAuthnSignerSingleton" + ], + "loop_iter": "6", + "optimistic_hashing": true, + "optimistic_loop": true, + "packages": [ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "process": "emv", + "prover_args": [ + "-depth 12" + ], + "rule": [ + "createAndVerifyEQtoIsValidSignatureForSigner" + ], + "rule_sanity": "basic", + "smt_timeout": "600", + "solc": "solc8.23", + "verify": "SafeWebAuthnSignerFactoryHarness:certora/specs/SafeWebAuthnSignerFactory.spec" +} \ No newline at end of file From 3b5bfd2eb0c763f27be343b5a758fdca3569a26e Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Tue, 18 Jun 2024 09:28:13 +0300 Subject: [PATCH 089/103] Update on progress --- certora/confs/SafeWebAuthnSignerFactory.conf | 1 + .../harnesses/SafeWebAuthnSignerFactoryHarness.sol | 12 ++++++------ certora/specs/SafeWebAuthnSignerFactory.spec | 4 +++- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/certora/confs/SafeWebAuthnSignerFactory.conf b/certora/confs/SafeWebAuthnSignerFactory.conf index 697e519e2..a239eaeca 100644 --- a/certora/confs/SafeWebAuthnSignerFactory.conf +++ b/certora/confs/SafeWebAuthnSignerFactory.conf @@ -15,6 +15,7 @@ "@safe-global=node_modules/@safe-global", "@account-abstraction=node_modules/@account-abstraction" ], + "exclude_rule": ["createAndVerifyEQtoIsValidSignatureForSigner"], "rule_sanity": "basic", "solc": "solc8.23", "solc_via_ir": false, diff --git a/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol b/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol index 872879470..b14b24f23 100644 --- a/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol +++ b/certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol @@ -30,10 +30,10 @@ contract SafeWebAuthnSignerFactoryHarness is SafeWebAuthnSignerFactory { magicValue = abi.decode(result, (bytes4)); } - // /** - // munge to pass the SignerCreationCantOverride rule. - // */ - // function _hasNoCode(address account) internal view override returns (bool result) { - // return account.code.length > 0; - // } + /** + munge to pass the SignerCreationCantOverride rule. + */ + function _hasNoCode(address account) internal view override returns (bool result) { + return account.code.length > 0; + } } diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index 9295f982b..22f78b958 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -98,12 +98,14 @@ rule createAndGetSignerEquivalence(){ ghost mathint numOfCreation; ghost mapping(address => uint) address_map; +ghost address signerAddress; hook EXTCODESIZE(address addr) uint v{ require address_map[addr] == v; } hook CREATE2(uint value, uint offset, uint length, bytes32 salt) address v{ + require(v == signerAddress); numOfCreation = numOfCreation + 1; address_map[v] = length; } @@ -152,7 +154,7 @@ rule createAndVerifyEQtoIsValidSignatureForSigner() bytes signature; bytes32 message; - address signerAddress = getSigner(e, x, y, verifier); + signerAddress = getSigner(e, x, y, verifier); require(numOfCreation == 0); require(hasNoCode(e, signerAddress)); require(WebAuthnHarness.castSignatureSuccess(e, message, signature)); From 3d670eb860d91a8e546bb227f8ea05f4a6027871 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Wed, 19 Jun 2024 09:52:56 +0300 Subject: [PATCH 090/103] Fix isValidSignerVerifyFor signure --- certora/munged/SafeWebAuthnSignerFactory.sol | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/certora/munged/SafeWebAuthnSignerFactory.sol b/certora/munged/SafeWebAuthnSignerFactory.sol index bc85d2cfe..d942bbcd2 100644 --- a/certora/munged/SafeWebAuthnSignerFactory.sol +++ b/certora/munged/SafeWebAuthnSignerFactory.sol @@ -62,6 +62,8 @@ contract SafeWebAuthnSignerFactory is ISafeSignerFactory { /** * @inheritdoc ISafeSignerFactory */ + // munged change magicValue := mload(0) => mload(mload(0x40)) + // staticcall(gas(), singleton, add(data, 0x20), mload(data), 0, 32) => staticcall(gas(), singleton, add(data, 0x20), mload(data), mload(0x40), 32) function isValidSignatureForSigner( bytes32 message, bytes calldata signature, @@ -81,8 +83,8 @@ contract SafeWebAuthnSignerFactory is ISafeSignerFactory { assembly { // staticcall to the singleton contract with return size given as 32 bytes. The // singleton contract is known and immutable so it is safe to specify return size. - if staticcall(gas(), singleton, add(data, 0x20), mload(data), 0, 32) { - magicValue := mload(0) + if staticcall(gas(), singleton, add(data, 0x20), mload(data), mload(0x40), 32) { + magicValue := mload(mload(0x40)) } } } From e81f28b72194e2acfa2def74c220cd0550b49016 Mon Sep 17 00:00:00 2001 From: yoavelmalem Date: Sun, 23 Jun 2024 11:38:32 +0300 Subject: [PATCH 091/103] Some small changes and verifyIsValidSignatureWillContinueToSucceed working --- .../confs/SafeWebAuthnSignerSingleton.conf | 3 ++ .../specs/SafeWebAuthnSignerSingleton.spec | 34 +++++++++++-------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/certora/confs/SafeWebAuthnSignerSingleton.conf b/certora/confs/SafeWebAuthnSignerSingleton.conf index cb5d77137..ec204d76b 100644 --- a/certora/confs/SafeWebAuthnSignerSingleton.conf +++ b/certora/confs/SafeWebAuthnSignerSingleton.conf @@ -7,6 +7,9 @@ "modules/passkey/contracts/libraries/P256.sol", "certora/harnesses/WebAuthnHarness.sol", ], + "link": [ + "SafeWebAuthnSignerFactory:SINGLETON=SafeWebAuthnSignerSingleton" + ], "packages":[ "@safe-global=node_modules/@safe-global", "@account-abstraction=node_modules/@account-abstraction" diff --git a/certora/specs/SafeWebAuthnSignerSingleton.spec b/certora/specs/SafeWebAuthnSignerSingleton.spec index c6bcfed38..14a86fea2 100644 --- a/certora/specs/SafeWebAuthnSignerSingleton.spec +++ b/certora/specs/SafeWebAuthnSignerSingleton.spec @@ -9,6 +9,7 @@ methods { function WebAuthnHarness.checkInjective(bytes32 challenge, bytes32 authenticatorData, bytes32 clientDataFields, bytes32 result) internal returns (bool) => checkInjectiveSummary(challenge, authenticatorData, clientDataFields, result); + function SafeWebAuthnSignerFactory.getSigner(uint256 x, uint256 y, P256.Verifiers v) internal returns (address) => getSignerGhost(x, y, v); } function GETencodeSigningMessageCVL(bytes32 challenge, bytes authenticatorData, string clientDataFields) returns bytes @@ -28,11 +29,18 @@ ghost verifySignatureAllowMalleabilityGhost(P256.Verifiers, bytes32, uint256, ui verifySignatureAllowMalleabilityGhost(a, message2, c, d, e, f) => message1 == message2; } +// Summary is correct only if the unique signer rule is proved spec GetSigner +ghost getSignerGhost(uint256, uint256, P256.Verifiers) returns address { + axiom forall uint256 x1. forall uint256 y1. forall P256.Verifiers v1. + forall uint256 x2. forall uint256 y2. forall P256.Verifiers v2. + (getSignerGhost(x1, y1, v1) == getSignerGhost(x2, y2, v2)) <=> (x1 == x2 && y1 == y2 && v1 == v2); +} + definition MAGIC_VALUE() returns bytes4 = to_bytes4(0x1626ba7e); /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ Implementation of _verifySignature Function (Integrity) │ +│ Implementation of isValidSignature Function (Integrity) │ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ @@ -46,6 +54,7 @@ rule verifySignatureUniqueness(env e){ bytes4 second_message_verified = isValidSignature(e, second_message, signature); assert (first_message != second_message) => !(first_message_verified == MAGIC_VALUE() && second_message_verified == MAGIC_VALUE()); + satisfy true; } rule verifySignatureIntegrity(env e){ @@ -60,11 +69,12 @@ rule verifySignatureIntegrity(env e){ bytes4 second_message_verified = isValidSignature(e, second_message, signature); assert (second_message_verified == MAGIC_VALUE()) <=> (first_message == second_message); + satisfy true; } /* ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ Both is valid Signature behave the same way │ +│ Both isValidSignature behave the same way │ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ @@ -82,6 +92,7 @@ rule verifyIsValidSignatureAreEqual(env e){ assert (magicValue_hashed == to_bytes4(0x20c13b0b) && magicValue_message == to_bytes4(0x1626ba7e)) => message == keccak256(data); assert message == keccak256(data) => (magicValue_hashed == to_bytes4(0x20c13b0b) && magicValue_message == to_bytes4(0x1626ba7e)) || (magicValue_hashed == to_bytes4(0) && magicValue_message == to_bytes4(0)); + satisfy (magicValue_hashed == to_bytes4(0x20c13b0b) && magicValue_message == to_bytes4(0x1626ba7e)); } /* @@ -92,32 +103,25 @@ rule verifyIsValidSignatureAreEqual(env e){ rule verifyIsValidSignatureWillContinueToSucceed(){ env e; - env e1; - env e2; - env e3; - require e1.msg.value == 0 && e2.msg.value == 0 && e3.msg.value == 0; + require e.msg.value == 0; method f; calldataarg args; bytes32 message; - WebAuthn.Signature sigStruct; - bytes signature = WebAuthnHarness.encodeSignature(e, sigStruct); - - bytes32 message3; - WebAuthn.Signature sigStruct3; - bytes signature3 = WebAuthnHarness.encodeSignature(e, sigStruct3); + bytes signature; - bytes4 firstVerified = isValidSignature@withrevert(e1, message, signature); + bytes4 firstVerified = isValidSignature@withrevert(e, message, signature); bool firstReverted = lastReverted; f(e, args); - // isValidSignature(e3, message3, signature3); - bytes4 secondVerify = isValidSignature@withrevert(e2, message, signature); + bytes4 secondVerify = isValidSignature@withrevert(e, message, signature); bool secondRevert = lastReverted; assert firstReverted == secondRevert; assert (!firstReverted && !secondRevert) => (firstVerified == secondVerify); + + satisfy (!firstReverted && firstVerified == to_bytes4(0x1626ba7e)); satisfy true; } From 99c9c6c32288b6aedbe97695bd038e2363516ccd Mon Sep 17 00:00:00 2001 From: yoavelmalem Date: Sun, 23 Jun 2024 11:39:42 +0300 Subject: [PATCH 092/103] Added getConfiguration Rule --- certora/confs/GetConfigurationConf.conf | 26 +++++++++++++++ certora/harnesses/Utilities.sol | 11 +++++++ certora/specs/GetConfigurationSpec.spec | 33 +++++++++++++++++++ .../contracts/SafeWebAuthnSignerSingleton.sol | 4 ++- 4 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 certora/confs/GetConfigurationConf.conf create mode 100644 certora/specs/GetConfigurationSpec.spec diff --git a/certora/confs/GetConfigurationConf.conf b/certora/confs/GetConfigurationConf.conf new file mode 100644 index 000000000..deb5baf35 --- /dev/null +++ b/certora/confs/GetConfigurationConf.conf @@ -0,0 +1,26 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol", + "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", + "certora/harnesses/Utilities.sol", + "modules/passkey/contracts/libraries/P256.sol" + ], + "link": [ + "SafeWebAuthnSignerProxy:_VERIFIERS=P256", + "SafeWebAuthnSignerProxy:_SINGLETON=SafeWebAuthnSignerSingleton" + ], + "packages":[ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "solc": "solc8.23", + "rule_sanity": "basic", + "solc_via_ir": false, + "optimistic_loop": true, + "loop_iter": "6", + "optimistic_hashing": true, + "prover_version": "jtoman/CERT-6221", + "server": "production", + "verify": "SafeWebAuthnSignerProxy:certora/specs/GetConfigurationSpec.spec" +} \ No newline at end of file diff --git a/certora/harnesses/Utilities.sol b/certora/harnesses/Utilities.sol index 608595af7..49ffd8cbc 100644 --- a/certora/harnesses/Utilities.sol +++ b/certora/harnesses/Utilities.sol @@ -1,3 +1,10 @@ +import {P256} from "../../modules/passkey/contracts/libraries/WebAuthn.sol"; + + +interface IConfigHolder { + function getConfiguration() external pure returns (uint256 x, uint256 y, P256.Verifiers verifiers); +} + contract Utilities { function havocAll() external { (bool success, ) = address(0xdeadbeef).call(abi.encodeWithSelector(0x12345678)); @@ -7,4 +14,8 @@ contract Utilities { function justRevert() external { revert(); } + + function getConfiguration(address proxy) external view returns (uint256 x, uint256 y, P256.Verifiers verifiers) { + return IConfigHolder(proxy).getConfiguration(); + } } \ No newline at end of file diff --git a/certora/specs/GetConfigurationSpec.spec b/certora/specs/GetConfigurationSpec.spec new file mode 100644 index 000000000..75f03311f --- /dev/null +++ b/certora/specs/GetConfigurationSpec.spec @@ -0,0 +1,33 @@ +using Utilities as utilities; +using SafeWebAuthnSignerProxy as proxy; + +methods { + function _._ external => DISPATCH [ SafeWebAuthnSignerProxy._, SafeWebAuthnSignerSingleton._ ] default HAVOC_ALL; +} + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ getConfiguration Function (Integrity) │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ + +rule configGetConfiguration { + env e; + + uint256 xBefore = currentContract._X; + uint256 yBefore = currentContract._Y; + P256.Verifiers verifiersBefore = currentContract._VERIFIERS; + + uint256 xAfter; + uint256 yAfter; + P256.Verifiers verifiersAfter; + + xAfter, yAfter, verifiersAfter = utilities.getConfiguration(e, proxy); + + + assert xBefore == xAfter && + yBefore == yAfter && + verifiersBefore == verifiersAfter; + satisfy true; +} + diff --git a/modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol b/modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol index 799f5450a..d83d8bbf7 100644 --- a/modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol +++ b/modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol @@ -31,10 +31,12 @@ contract SafeWebAuthnSignerSingleton is SignatureValidator { */ function getConfiguration() public pure returns (uint256 x, uint256 y, P256.Verifiers verifiers) { // solhint-disable-next-line no-inline-assembly + uint256 mask = type(uint176).max; assembly ("memory-safe") { x := calldataload(sub(calldatasize(), 86)) y := calldataload(sub(calldatasize(), 54)) - verifiers := shr(80, calldataload(sub(calldatasize(), 22))) + // verifiers := shr(80, calldataload(sub(calldatasize(), 22))) + verifiers := and(mask, calldataload(sub(calldatasize(), 32))) } } } From 767bfcc02ebbd32d90f22eeb29ef9d3725490fc0 Mon Sep 17 00:00:00 2001 From: yoavelmalem Date: Wed, 21 Aug 2024 11:47:12 +0300 Subject: [PATCH 093/103] remove munging --- modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol b/modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol index d83d8bbf7..799f5450a 100644 --- a/modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol +++ b/modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol @@ -31,12 +31,10 @@ contract SafeWebAuthnSignerSingleton is SignatureValidator { */ function getConfiguration() public pure returns (uint256 x, uint256 y, P256.Verifiers verifiers) { // solhint-disable-next-line no-inline-assembly - uint256 mask = type(uint176).max; assembly ("memory-safe") { x := calldataload(sub(calldatasize(), 86)) y := calldataload(sub(calldatasize(), 54)) - // verifiers := shr(80, calldataload(sub(calldatasize(), 22))) - verifiers := and(mask, calldataload(sub(calldatasize(), 32))) + verifiers := shr(80, calldataload(sub(calldatasize(), 22))) } } } From d96881d0bf0b30815e105abf3788f70a9685a27e Mon Sep 17 00:00:00 2001 From: yoavelmalem Date: Wed, 21 Aug 2024 12:02:07 +0300 Subject: [PATCH 094/103] remove john branch --- certora/confs/GetConfigurationConf.conf | 1 - 1 file changed, 1 deletion(-) diff --git a/certora/confs/GetConfigurationConf.conf b/certora/confs/GetConfigurationConf.conf index deb5baf35..4117892da 100644 --- a/certora/confs/GetConfigurationConf.conf +++ b/certora/confs/GetConfigurationConf.conf @@ -20,7 +20,6 @@ "optimistic_loop": true, "loop_iter": "6", "optimistic_hashing": true, - "prover_version": "jtoman/CERT-6221", "server": "production", "verify": "SafeWebAuthnSignerProxy:certora/specs/GetConfigurationSpec.spec" } \ No newline at end of file From d4a9be64790cccc8e7d095a987c8e97b8f28ed55 Mon Sep 17 00:00:00 2001 From: niv vaknin Date: Wed, 21 Aug 2024 12:36:20 +0300 Subject: [PATCH 095/103] Add john fixes --- certora/specs/SafeWebAuthnSignerFactory.spec | 20 +++++++++++++++---- .../specs/SafeWebAuthnSignerSingleton.spec | 19 ++++++++++++++---- certora/specs/WebAuthn.spec | 4 ---- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index 22f78b958..bb5cf3a1c 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -16,6 +16,7 @@ methods{ function WebAuthnHarness.checkInjective(bytes32 challenge, bytes32 authenticatorData, bytes32 clientDataFields, bytes32 result) internal returns (bool) => checkInjectiveSummary(challenge, authenticatorData, clientDataFields, result); + function _.isValidSignature(bytes32,bytes) external => DISPATCHER(optimistic=true, use_fallback=true); function _._ external => DISPATCH [ proxy._, @@ -23,10 +24,21 @@ methods{ ] default NONDET; } -function GETencodeSigningMessageCVL(bytes32 challenge, bytes authenticatorData, string clientDataFields) returns bytes -{ - env e; - return WebAuthnHarness.GETencodeSigningMessageSummary(e, challenge, authenticatorData, clientDataFields); +ghost mapping(bytes32 => mapping(bytes32 => mapping(bytes32 => bytes32))) componentToEncodeHash; +ghost mapping(bytes32 => bytes32) revChallenge; +ghost mapping(bytes32 => bytes32) revAuthenticator; +ghost mapping(bytes32 => bytes32) revClientData; + +function GETencodeSigningMessageCVL(bytes32 challenge, bytes authenticatorData, string clientDataFields) returns bytes { + bytes32 authHash = keccak256(authenticatorData); + bytes32 clientHash = keccak256(clientDataFields); + bytes32 toRetHash = componentToEncodeHash[challenge][authHash][clientHash]; + require(revChallenge[toRetHash] == challenge); + require(revAuthenticator[toRetHash] == authHash); + require(revClientData[toRetHash] == clientHash); + bytes toRet; + require keccak256(toRet) == toRetHash; + return toRet; } ghost checkInjectiveSummary(bytes32, bytes32, bytes32, bytes32) returns bool { diff --git a/certora/specs/SafeWebAuthnSignerSingleton.spec b/certora/specs/SafeWebAuthnSignerSingleton.spec index bfdf7b3fd..753804415 100644 --- a/certora/specs/SafeWebAuthnSignerSingleton.spec +++ b/certora/specs/SafeWebAuthnSignerSingleton.spec @@ -12,10 +12,21 @@ methods { function SafeWebAuthnSignerFactory.getSigner(uint256 x, uint256 y, P256.Verifiers v) internal returns (address) => getSignerGhost(x, y, v); } -function GETencodeSigningMessageCVL(bytes32 challenge, bytes authenticatorData, string clientDataFields) returns bytes -{ - env e; - return WebAuthnHarness.GETencodeSigningMessageSummary(e, challenge, authenticatorData, clientDataFields); +ghost mapping(bytes32 => mapping(bytes32 => mapping(bytes32 => bytes32))) componentToEncodeHash; +ghost mapping(bytes32 => bytes32) revChallenge; +ghost mapping(bytes32 => bytes32) revAuthenticator; +ghost mapping(bytes32 => bytes32) revClientData; + +function GETencodeSigningMessageCVL(bytes32 challenge, bytes authenticatorData, string clientDataFields) returns bytes { + bytes32 authHash = keccak256(authenticatorData); + bytes32 clientHash = keccak256(clientDataFields); + bytes32 toRetHash = componentToEncodeHash[challenge][authHash][clientHash]; + require(revChallenge[toRetHash] == challenge); + require(revAuthenticator[toRetHash] == authHash); + require(revClientData[toRetHash] == clientHash); + bytes toRet; + require keccak256(toRet) == toRetHash; + return toRet; } ghost checkInjectiveSummary(bytes32, bytes32, bytes32, bytes32) returns bool { diff --git a/certora/specs/WebAuthn.spec b/certora/specs/WebAuthn.spec index 4f394a3aa..d45864dc3 100644 --- a/certora/specs/WebAuthn.spec +++ b/certora/specs/WebAuthn.spec @@ -113,7 +113,6 @@ rule verifySignatureConsistent(){ env e1; env e2; require e1.msg.value == 0 && e2.msg.value == 0; - method f; calldataarg args; bytes32 challenge; @@ -127,7 +126,6 @@ rule verifySignatureConsistent(){ bool result1 = verifySignature@withrevert(e1, challenge, bytesSignature, authenticatorFlags, x, y, verifiers); bool firstCallRevert = lastReverted; - f(e, args); bool result2 = verifySignature@withrevert(e2, challenge, bytesSignature, authenticatorFlags, x, y, verifiers); bool secondCallRevert = lastReverted; @@ -150,7 +148,6 @@ rule castSignatureConsistent(){ require (e1.msg.value == e2.msg.value) && (e1.msg.value == e.msg.value) && (e.msg.value == 0); - method f; calldataarg args; bytes signature; @@ -164,7 +161,6 @@ rule castSignatureConsistent(){ firstIsValid, firstData = castSignature@withrevert(e1, signature); bool firstRevert = lastReverted; - f(e, args); secondIsValid, secondData = castSignature@withrevert(e2, signature); bool secondRevert = lastReverted; From 62742d05054d4607ca629ef7d5400a01b6315fba Mon Sep 17 00:00:00 2001 From: yoavelmalem Date: Thu, 22 Aug 2024 11:26:37 +0300 Subject: [PATCH 096/103] Added conf files and fixed all issues --- certora/confs/GetConfigurationConf.conf | 46 +++++----- certora/confs/GetSigner.conf | 12 +-- certora/confs/ProxySimulator.conf | 28 ++++++ certora/confs/SafeWebAuthnSignerFactory.conf | 24 +++--- certora/confs/SafeWebAuthnSignerProxy.conf | 12 +-- .../confs/SafeWebAuthnSignerSingleton.conf | 17 ++-- certora/confs/SignerCreationCantOverride.conf | 32 +++++++ ...onIsValidSignatureRevertingConditions.conf | 24 ++++++ ...IsValidSignatureWillContinueToSucceed.conf | 27 ++++++ .../ValidSignatureForSignerIntegrity.conf | 26 ++++++ ... VerifyEQtoIsValidSignatureForSigner.conf} | 0 certora/confs/WebAuthn.conf | 14 +-- .../historical_confs/AllowanceModule.conf | 28 ------ .../AllowanceModule_builtin_assertions.conf | 25 ------ .../AllowanceModule_sanity.conf | 26 ------ ...ule_sanity_with_all_default_summaries.conf | 28 ------ .../AllowanceModule_sanity_with_erc20cvl.conf | 28 ------ ...nceModule_sanity_with_erc20dispatched.conf | 29 ------- .../historical_confs/Safe4337Module.conf | 29 ------- .../Safe4337Module_builtin_assertions.conf | 26 ------ .../Safe4337Module_sanity.conf | 23 ----- ...ule_sanity_with_all_default_summaries.conf | 28 ------ .../Safe4337Module_sanity_with_erc20cvl.conf | 26 ------ ...337Module_sanity_with_erc20dispatched.conf | 27 ------ .../historical_confs/SafeModuleSetup.conf | 26 ------ .../SafeModuleSetup_builtin_assertions.conf | 25 ------ .../SafeModuleSetup_sanity.conf | 23 ----- ...tup_sanity_with_all_default_summaries.conf | 26 ------ .../SafeModuleSetup_sanity_with_erc20cvl.conf | 26 ------ ...duleSetup_sanity_with_erc20dispatched.conf | 27 ------ .../historical_confs/SafeSignerLaunchpad.conf | 28 ------ ...afeSignerLaunchpad_builtin_assertions.conf | 24 ------ .../SafeSignerLaunchpad_sanity.conf | 23 ----- ...pad_sanity_with_all_default_summaries.conf | 26 ------ ...eSignerLaunchpad_sanity_with_erc20cvl.conf | 26 ------ ...Launchpad_sanity_with_erc20dispatched.conf | 27 ------ ...AuthnSignerFactory_builtin_assertions.conf | 26 ------ .../SafeWebAuthnSignerFactory_sanity.conf | 25 ------ ...ory_sanity_with_all_default_summaries.conf | 27 ------ ...thnSignerFactory_sanity_with_erc20cvl.conf | 26 ------ ...erFactory_sanity_with_erc20dispatched.conf | 27 ------ ...SafeWebAuthnSigner_builtin_assertions.conf | 28 ------ .../SafeWebAuthnSigner_sanity.conf | 25 ------ ...ner_sanity_with_all_default_summaries.conf | 27 ------ ...feWebAuthnSigner_sanity_with_erc20cvl.conf | 26 ------ ...thnSigner_sanity_with_erc20dispatched.conf | 27 ------ certora/harnesses/ERC20Like/DummyERC20A.sol | 55 ------------ certora/harnesses/ERC20Like/DummyERC20B.sol | 55 ------------ certora/harnesses/ERC20Like/DummyWeth.sol | 64 -------------- .../GetConfigurationProxyHarness.sol | 86 +++++++++++++++++++ certora/harnesses/ProxySimulator.sol | 24 ++++++ certora/munged/SafeWebAuthnSignerFactory.sol | 8 +- .../munged/SafeWebAuthnSignerSingleton.sol | 44 ++++++++++ certora/specs/GetConfigurationSpec.spec | 39 +++++---- certora/specs/ProxySimulator.spec | 4 +- certora/specs/SafeWebAuthnSignerFactory.spec | 5 -- 56 files changed, 380 insertions(+), 1160 deletions(-) create mode 100644 certora/confs/ProxySimulator.conf create mode 100644 certora/confs/SignerCreationCantOverride.conf create mode 100644 certora/confs/SingletonIsValidSignatureRevertingConditions.conf create mode 100644 certora/confs/SingletonVerifyIsValidSignatureWillContinueToSucceed.conf create mode 100644 certora/confs/ValidSignatureForSignerIntegrity.conf rename certora/confs/{createAndVerifyEQtoIsValidSignatureForSigner.conf => VerifyEQtoIsValidSignatureForSigner.conf} (100%) delete mode 100644 certora/confs/historical_confs/AllowanceModule.conf delete mode 100644 certora/confs/historical_confs/AllowanceModule_builtin_assertions.conf delete mode 100644 certora/confs/historical_confs/AllowanceModule_sanity.conf delete mode 100644 certora/confs/historical_confs/AllowanceModule_sanity_with_all_default_summaries.conf delete mode 100644 certora/confs/historical_confs/AllowanceModule_sanity_with_erc20cvl.conf delete mode 100644 certora/confs/historical_confs/AllowanceModule_sanity_with_erc20dispatched.conf delete mode 100644 certora/confs/historical_confs/Safe4337Module.conf delete mode 100644 certora/confs/historical_confs/Safe4337Module_builtin_assertions.conf delete mode 100644 certora/confs/historical_confs/Safe4337Module_sanity.conf delete mode 100644 certora/confs/historical_confs/Safe4337Module_sanity_with_all_default_summaries.conf delete mode 100644 certora/confs/historical_confs/Safe4337Module_sanity_with_erc20cvl.conf delete mode 100644 certora/confs/historical_confs/Safe4337Module_sanity_with_erc20dispatched.conf delete mode 100644 certora/confs/historical_confs/SafeModuleSetup.conf delete mode 100644 certora/confs/historical_confs/SafeModuleSetup_builtin_assertions.conf delete mode 100644 certora/confs/historical_confs/SafeModuleSetup_sanity.conf delete mode 100644 certora/confs/historical_confs/SafeModuleSetup_sanity_with_all_default_summaries.conf delete mode 100644 certora/confs/historical_confs/SafeModuleSetup_sanity_with_erc20cvl.conf delete mode 100644 certora/confs/historical_confs/SafeModuleSetup_sanity_with_erc20dispatched.conf delete mode 100644 certora/confs/historical_confs/SafeSignerLaunchpad.conf delete mode 100644 certora/confs/historical_confs/SafeSignerLaunchpad_builtin_assertions.conf delete mode 100644 certora/confs/historical_confs/SafeSignerLaunchpad_sanity.conf delete mode 100644 certora/confs/historical_confs/SafeSignerLaunchpad_sanity_with_all_default_summaries.conf delete mode 100644 certora/confs/historical_confs/SafeSignerLaunchpad_sanity_with_erc20cvl.conf delete mode 100644 certora/confs/historical_confs/SafeSignerLaunchpad_sanity_with_erc20dispatched.conf delete mode 100644 certora/confs/historical_confs/SafeWebAuthnSignerFactory_builtin_assertions.conf delete mode 100644 certora/confs/historical_confs/SafeWebAuthnSignerFactory_sanity.conf delete mode 100644 certora/confs/historical_confs/SafeWebAuthnSignerFactory_sanity_with_all_default_summaries.conf delete mode 100644 certora/confs/historical_confs/SafeWebAuthnSignerFactory_sanity_with_erc20cvl.conf delete mode 100644 certora/confs/historical_confs/SafeWebAuthnSignerFactory_sanity_with_erc20dispatched.conf delete mode 100644 certora/confs/historical_confs/SafeWebAuthnSigner_builtin_assertions.conf delete mode 100644 certora/confs/historical_confs/SafeWebAuthnSigner_sanity.conf delete mode 100644 certora/confs/historical_confs/SafeWebAuthnSigner_sanity_with_all_default_summaries.conf delete mode 100644 certora/confs/historical_confs/SafeWebAuthnSigner_sanity_with_erc20cvl.conf delete mode 100644 certora/confs/historical_confs/SafeWebAuthnSigner_sanity_with_erc20dispatched.conf delete mode 100644 certora/harnesses/ERC20Like/DummyERC20A.sol delete mode 100644 certora/harnesses/ERC20Like/DummyERC20B.sol delete mode 100644 certora/harnesses/ERC20Like/DummyWeth.sol create mode 100644 certora/harnesses/GetConfigurationProxyHarness.sol create mode 100644 certora/harnesses/ProxySimulator.sol create mode 100644 certora/munged/SafeWebAuthnSignerSingleton.sol diff --git a/certora/confs/GetConfigurationConf.conf b/certora/confs/GetConfigurationConf.conf index 4117892da..20aebd2f4 100644 --- a/certora/confs/GetConfigurationConf.conf +++ b/certora/confs/GetConfigurationConf.conf @@ -1,25 +1,25 @@ { - "assert_autofinder_success": true, - "files": [ - "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol", - "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", - "certora/harnesses/Utilities.sol", - "modules/passkey/contracts/libraries/P256.sol" - ], - "link": [ - "SafeWebAuthnSignerProxy:_VERIFIERS=P256", - "SafeWebAuthnSignerProxy:_SINGLETON=SafeWebAuthnSignerSingleton" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "rule_sanity": "basic", - "solc_via_ir": false, - "optimistic_loop": true, - "loop_iter": "6", - "optimistic_hashing": true, - "server": "production", - "verify": "SafeWebAuthnSignerProxy:certora/specs/GetConfigurationSpec.spec" + "assert_autofinder_success": true, + "files": [ + "certora/munged/SafeWebAuthnSignerSingleton.sol", + "certora/harnesses/GetConfigurationProxyHarness.sol", + "modules/passkey/contracts/SafeWebAuthnSignerFactory.sol" + ], + "link": [ + "GetConfigurationProxyHarness:_SINGLETON=SafeWebAuthnSignerSingleton" + ], + "loop_iter": "6", + "optimistic_hashing": true, + "optimistic_loop": true, + "packages": [ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "process": "emv", + "prover_args": [ + " -split false" + ], + "rule_sanity": "basic", + "solc": "solc8.23", + "verify": "GetConfigurationProxyHarness:certora/specs/GetConfigurationSpec.spec" } \ No newline at end of file diff --git a/certora/confs/GetSigner.conf b/certora/confs/GetSigner.conf index 4484a3502..b5e6ef93a 100644 --- a/certora/confs/GetSigner.conf +++ b/certora/confs/GetSigner.conf @@ -5,19 +5,19 @@ "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol" ], + "hashing_length_bound": "4694", "link": [ "GetSignerHarness:SINGLETON=SafeWebAuthnSignerSingleton" ], - "packages":[ + "loop_iter": "144", + "optimistic_hashing": true, + "optimistic_loop": true, + "packages": [ "@safe-global=node_modules/@safe-global", "@account-abstraction=node_modules/@account-abstraction" ], + "process": "emv", "rule_sanity": "basic", "solc": "solc8.23", - "solc_via_ir": false, - "optimistic_loop": true, - "optimistic_hashing": true, - "hashing_length_bound": "4694", - "loop_iter": "144", "verify": "GetSignerHarness:certora/specs/GetSigner.spec" } \ No newline at end of file diff --git a/certora/confs/ProxySimulator.conf b/certora/confs/ProxySimulator.conf new file mode 100644 index 000000000..f8edf10ca --- /dev/null +++ b/certora/confs/ProxySimulator.conf @@ -0,0 +1,28 @@ +{ + "assert_autofinder_success": true, + "files": [ + "certora/harnesses/ProxySimulator.sol", + "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol", + "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", + "modules/passkey/contracts/libraries/P256.sol", + "modules/passkey/contracts/libraries/WebAuthn.sol", + "certora/harnesses/WebAuthnHarness.sol" + ], + "hashing_length_bound": "2048", + "link": [ + "ProxySimulator:_proxy=SafeWebAuthnSignerProxy", + "SafeWebAuthnSignerProxy:_VERIFIERS=P256", + "SafeWebAuthnSignerProxy:_SINGLETON=SafeWebAuthnSignerSingleton" + ], + "loop_iter": "6", + "optimistic_hashing": true, + "optimistic_loop": true, + "packages": [ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "process": "emv", + "rule_sanity": "basic", + "solc": "solc8.23", + "verify": "ProxySimulator:certora/specs/ProxySimulator.spec" +} \ No newline at end of file diff --git a/certora/confs/SafeWebAuthnSignerFactory.conf b/certora/confs/SafeWebAuthnSignerFactory.conf index a239eaeca..8115966d7 100644 --- a/certora/confs/SafeWebAuthnSignerFactory.conf +++ b/certora/confs/SafeWebAuthnSignerFactory.conf @@ -2,25 +2,29 @@ "assert_autofinder_success": true, "files": [ "certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol", - "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", - "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol", - "modules/passkey/contracts/libraries/WebAuthn.sol", - "modules/passkey/contracts/libraries/P256.sol", "certora/harnesses/WebAuthnHarness.sol", + "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", + "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol" ], "link": [ "SafeWebAuthnSignerFactoryHarness:SINGLETON=SafeWebAuthnSignerSingleton" ], - "packages":[ + "loop_iter": "6", + "optimistic_hashing": true, + "optimistic_loop": true, + "packages": [ "@safe-global=node_modules/@safe-global", "@account-abstraction=node_modules/@account-abstraction" ], - "exclude_rule": ["createAndVerifyEQtoIsValidSignatureForSigner"], + "rule": [ + "createAndGetSignerEquivalence", + "getSignerRevertingConditions", + "hasNoCodeIntegrity", + "isValidSignatureForSignerConsistency", + "singletonNeverChanges" + ], + "process": "emv", "rule_sanity": "basic", "solc": "solc8.23", - "solc_via_ir": false, - "optimistic_loop": true, - "optimistic_hashing": true, - "loop_iter": "6", "verify": "SafeWebAuthnSignerFactoryHarness:certora/specs/SafeWebAuthnSignerFactory.spec" } \ No newline at end of file diff --git a/certora/confs/SafeWebAuthnSignerProxy.conf b/certora/confs/SafeWebAuthnSignerProxy.conf index e6880f9f0..2affd1527 100644 --- a/certora/confs/SafeWebAuthnSignerProxy.conf +++ b/certora/confs/SafeWebAuthnSignerProxy.conf @@ -9,15 +9,15 @@ "SafeWebAuthnSignerProxy:_VERIFIERS=P256", "SafeWebAuthnSignerProxy:_SINGLETON=SafeWebAuthnSignerSingleton" ], - "packages":[ + "loop_iter": "6", + "optimistic_hashing": true, + "optimistic_loop": true, + "packages": [ "@safe-global=node_modules/@safe-global", "@account-abstraction=node_modules/@account-abstraction" ], - "solc": "solc8.23", - "solc_via_ir": false, - "optimistic_loop": true, - "loop_iter": "6", - "optimistic_hashing": true, + "process": "emv", "rule_sanity": "basic", + "solc": "solc8.23", "verify": "SafeWebAuthnSignerProxy:certora/specs/SafeWebAuthnSignerProxy.spec" } \ No newline at end of file diff --git a/certora/confs/SafeWebAuthnSignerSingleton.conf b/certora/confs/SafeWebAuthnSignerSingleton.conf index 8c830b7a1..2c869e120 100644 --- a/certora/confs/SafeWebAuthnSignerSingleton.conf +++ b/certora/confs/SafeWebAuthnSignerSingleton.conf @@ -5,20 +5,17 @@ "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", "modules/passkey/contracts/libraries/WebAuthn.sol", "modules/passkey/contracts/libraries/P256.sol", - "certora/harnesses/WebAuthnHarness.sol", + "certora/harnesses/WebAuthnHarness.sol" ], - "link": [ - "SafeWebAuthnSignerFactory:SINGLETON=SafeWebAuthnSignerSingleton" - ], - "packages":[ + "loop_iter": "6", + "optimistic_hashing": true, + "optimistic_loop": true, + "packages": [ "@safe-global=node_modules/@safe-global", "@account-abstraction=node_modules/@account-abstraction" ], - "solc": "solc8.23", + "process": "emv", "rule_sanity": "basic", - "solc_via_ir": false, - "optimistic_loop": true, - "loop_iter": "6", - "optimistic_hashing": true, + "solc": "solc8.23", "verify": "SafeWebAuthnSignerSingleton:certora/specs/SafeWebAuthnSignerSingleton.spec" } \ No newline at end of file diff --git a/certora/confs/SignerCreationCantOverride.conf b/certora/confs/SignerCreationCantOverride.conf new file mode 100644 index 000000000..1478024f2 --- /dev/null +++ b/certora/confs/SignerCreationCantOverride.conf @@ -0,0 +1,32 @@ +{ + "assert_autofinder_success": true, + "exclude_rule": [ + "createAndVerifyEQtoIsValidSignatureForSigner" + ], + "files": [ + "certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol", + "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", + "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol", + "modules/passkey/contracts/libraries/WebAuthn.sol", + "modules/passkey/contracts/libraries/P256.sol", + "certora/harnesses/WebAuthnHarness.sol" + ], + "hashing_length_bound": "912", + "link": [ + "SafeWebAuthnSignerFactoryHarness:SINGLETON=SafeWebAuthnSignerSingleton" + ], + "loop_iter": "6", + "optimistic_hashing": true, + "optimistic_loop": true, + "packages": [ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "process": "emv", + "rule": [ + "SignerCreationCantOverride" + ], + "rule_sanity": "basic", + "solc": "solc8.23", + "verify": "SafeWebAuthnSignerFactoryHarness:certora/specs/SafeWebAuthnSignerFactory.spec" +} \ No newline at end of file diff --git a/certora/confs/SingletonIsValidSignatureRevertingConditions.conf b/certora/confs/SingletonIsValidSignatureRevertingConditions.conf new file mode 100644 index 000000000..956527949 --- /dev/null +++ b/certora/confs/SingletonIsValidSignatureRevertingConditions.conf @@ -0,0 +1,24 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/passkey/contracts/SafeWebAuthnSignerFactory.sol", + "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", + "modules/passkey/contracts/libraries/WebAuthn.sol", + "modules/passkey/contracts/libraries/P256.sol", + "certora/harnesses/WebAuthnHarness.sol" + ], + "loop_iter": "6", + "optimistic_hashing": true, + "optimistic_loop": true, + "packages": [ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "process": "emv", + "rule": [ + "isValidSignatureRevertingConditions" + ], + "rule_sanity": "basic", + "solc": "solc8.23", + "verify": "SafeWebAuthnSignerSingleton:certora/specs/SafeWebAuthnSignerSingleton.spec" +} \ No newline at end of file diff --git a/certora/confs/SingletonVerifyIsValidSignatureWillContinueToSucceed.conf b/certora/confs/SingletonVerifyIsValidSignatureWillContinueToSucceed.conf new file mode 100644 index 000000000..4b7c9469d --- /dev/null +++ b/certora/confs/SingletonVerifyIsValidSignatureWillContinueToSucceed.conf @@ -0,0 +1,27 @@ +{ + "assert_autofinder_success": true, + "files": [ + "modules/passkey/contracts/SafeWebAuthnSignerFactory.sol", + "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", + "modules/passkey/contracts/libraries/WebAuthn.sol", + "modules/passkey/contracts/libraries/P256.sol", + "certora/harnesses/WebAuthnHarness.sol" + ], + "link": [ + "SafeWebAuthnSignerFactory:SINGLETON=SafeWebAuthnSignerSingleton" + ], + "loop_iter": "6", + "optimistic_hashing": true, + "optimistic_loop": true, + "packages": [ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "process": "emv", + "rule": [ + "verifyIsValidSignatureWillContinueToSucceed" + ], + "rule_sanity": "basic", + "solc": "solc8.23", + "verify": "SafeWebAuthnSignerSingleton:certora/specs/SafeWebAuthnSignerSingleton.spec" +} \ No newline at end of file diff --git a/certora/confs/ValidSignatureForSignerIntegrity.conf b/certora/confs/ValidSignatureForSignerIntegrity.conf new file mode 100644 index 000000000..57d2069f8 --- /dev/null +++ b/certora/confs/ValidSignatureForSignerIntegrity.conf @@ -0,0 +1,26 @@ +{ + "assert_autofinder_success": true, + "files": [ + "certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol", + "certora/harnesses/WebAuthnHarness.sol", + "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", + "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol" + ], + "link": [ + "SafeWebAuthnSignerFactoryHarness:SINGLETON=SafeWebAuthnSignerSingleton" + ], + "loop_iter": "6", + "optimistic_hashing": true, + "optimistic_loop": true, + "packages": [ + "@safe-global=node_modules/@safe-global", + "@account-abstraction=node_modules/@account-abstraction" + ], + "process": "emv", + "rule": [ + "isValidSignatureForSignerIntegrity" + ], + "rule_sanity": "basic", + "solc": "solc8.23", + "verify": "SafeWebAuthnSignerFactoryHarness:certora/specs/SafeWebAuthnSignerFactory.spec" +} \ No newline at end of file diff --git a/certora/confs/createAndVerifyEQtoIsValidSignatureForSigner.conf b/certora/confs/VerifyEQtoIsValidSignatureForSigner.conf similarity index 100% rename from certora/confs/createAndVerifyEQtoIsValidSignatureForSigner.conf rename to certora/confs/VerifyEQtoIsValidSignatureForSigner.conf diff --git a/certora/confs/WebAuthn.conf b/certora/confs/WebAuthn.conf index c6134961c..7a7faf3eb 100644 --- a/certora/confs/WebAuthn.conf +++ b/certora/confs/WebAuthn.conf @@ -2,16 +2,16 @@ "assert_autofinder_success": true, "files": [ "certora/harnesses/WebAuthnHarness.sol" - ], - "packages":[ + ], + "loop_iter": "6", + "optimistic_hashing": true, + "optimistic_loop": true, + "packages": [ "@safe-global=node_modules/@safe-global", "@account-abstraction=node_modules/@account-abstraction" ], - "solc": "solc8.23", - "solc_via_ir": false, - "optimistic_loop": true, - "optimistic_hashing": true, + "process": "emv", "rule_sanity": "basic", - "loop_iter": "6", + "solc": "solc8.23", "verify": "WebAuthnHarness:certora/specs/WebAuthn.spec" } \ No newline at end of file diff --git a/certora/confs/historical_confs/AllowanceModule.conf b/certora/confs/historical_confs/AllowanceModule.conf deleted file mode 100644 index 1cc779d55..000000000 --- a/certora/confs/historical_confs/AllowanceModule.conf +++ /dev/null @@ -1,28 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "modules/allowances/contracts/AllowanceModule.sol", - "certora/harnesses/ERC20Like/DummyWeth.sol", - "certora/harnesses/Utilities.sol", - "modules/allowances/contracts/SignatureDecoder.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity_with_all_default_summaries", - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc7.6", - "solc_via_ir": false, - "optimistic_loop": true, - "loop_iter": "5", - "prover_version": "master", - "server": "production", - "verify": "AllowanceModule:certora/specs/AllowanceModule.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/AllowanceModule_builtin_assertions.conf b/certora/confs/historical_confs/AllowanceModule_builtin_assertions.conf deleted file mode 100644 index 2b5c58654..000000000 --- a/certora/confs/historical_confs/AllowanceModule_builtin_assertions.conf +++ /dev/null @@ -1,25 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "modules/allowances/contracts/AllowanceModule.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "builtin_assertions", - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc7.6", - "solc_via_ir": false, - "optimistic_loop": true, - "loop_iter": "1", - "prover_version": "master", - "server": "production", - "verify": "AllowanceModule:certora/specs/setup/builtin_assertions.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/AllowanceModule_sanity.conf b/certora/confs/historical_confs/AllowanceModule_sanity.conf deleted file mode 100644 index 44a2a845d..000000000 --- a/certora/confs/historical_confs/AllowanceModule_sanity.conf +++ /dev/null @@ -1,26 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "modules/allowances/contracts/AllowanceModule.sol", - "modules/allowances/contracts/SignatureDecoder.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity", - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc7.6", - "solc_via_ir": false, - "optimistic_loop": true, - "loop_iter": "5", - "prover_version": "master", - "server": "production", - "verify": "AllowanceModule:certora/specs/setup/sanity.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/AllowanceModule_sanity_with_all_default_summaries.conf b/certora/confs/historical_confs/AllowanceModule_sanity_with_all_default_summaries.conf deleted file mode 100644 index d8d6a9bb3..000000000 --- a/certora/confs/historical_confs/AllowanceModule_sanity_with_all_default_summaries.conf +++ /dev/null @@ -1,28 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "modules/allowances/contracts/AllowanceModule.sol", - "certora/harnesses/ERC20Like/DummyWeth.sol", - "certora/harnesses/Utilities.sol", - "modules/allowances/contracts/SignatureDecoder.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity_with_all_default_summaries", - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc7.6", - "solc_via_ir": false, - "optimistic_loop": true, - "loop_iter": "5", - "prover_version": "master", - "server": "production", - "verify": "AllowanceModule:certora/specs/setup/sanity_with_all_default_summaries.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/AllowanceModule_sanity_with_erc20cvl.conf b/certora/confs/historical_confs/AllowanceModule_sanity_with_erc20cvl.conf deleted file mode 100644 index d61318992..000000000 --- a/certora/confs/historical_confs/AllowanceModule_sanity_with_erc20cvl.conf +++ /dev/null @@ -1,28 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "certora/harnesses/ERC20Like/DummyWeth.sol", - "certora/harnesses/Utilities.sol", - "modules/allowances/contracts/AllowanceModule.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity_with_erc20cvl", - "optimistic_fallback": true, - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc7.6", - "solc_via_ir": false, - "optimistic_loop": true, - "loop_iter": "5", - "prover_version": "master", - "server": "production", - "verify": "AllowanceModule:certora/specs/setup/sanity_with_erc20cvl.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/AllowanceModule_sanity_with_erc20dispatched.conf b/certora/confs/historical_confs/AllowanceModule_sanity_with_erc20dispatched.conf deleted file mode 100644 index 79ad56da9..000000000 --- a/certora/confs/historical_confs/AllowanceModule_sanity_with_erc20dispatched.conf +++ /dev/null @@ -1,29 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "certora/harnesses/ERC20Like/DummyERC20A.sol", - "certora/harnesses/ERC20Like/DummyERC20B.sol", - "certora/harnesses/ERC20Like/DummyWeth.sol", - "modules/allowances/contracts/AllowanceModule.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity_with_erc20dispatched", - "optimistic_fallback": true, - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc7.6", - "solc_via_ir": false, - "optimistic_loop": true, - "loop_iter": "5", - "prover_version": "master", - "server": "production", - "verify": "AllowanceModule:certora/specs/setup/sanity_with_erc20dispatched.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/Safe4337Module.conf b/certora/confs/historical_confs/Safe4337Module.conf deleted file mode 100644 index e0cee6c09..000000000 --- a/certora/confs/historical_confs/Safe4337Module.conf +++ /dev/null @@ -1,29 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "modules/4337/contracts/Safe4337Module.sol", - "certora/harnesses/ERC20Like/DummyWeth.sol", - "certora/harnesses/Utilities.sol", - "modules/4337/contracts/test/TestSingletonSigner.sol", - "modules/4337/certora/harnesses/Account.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity", - "process": "emv", - "prover_args": [ - " -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "optimistic_hashing": true, - "optimistic_loop": true, - "prover_version": "master", - "server": "production", - "verify": "Safe4337Module:certora/specs/Safe4337Module.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/Safe4337Module_builtin_assertions.conf b/certora/confs/historical_confs/Safe4337Module_builtin_assertions.conf deleted file mode 100644 index 9fc0e9f39..000000000 --- a/certora/confs/historical_confs/Safe4337Module_builtin_assertions.conf +++ /dev/null @@ -1,26 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "modules/4337/contracts/Safe4337Module.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "builtin_assertions", - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "optimistic_hashing": true, - "optimistic_loop": true, - "loop_iter": "1", - "prover_version": "master", - "server": "production", - "verify": "Safe4337Module:certora/specs/setup/builtin_assertions.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/Safe4337Module_sanity.conf b/certora/confs/historical_confs/Safe4337Module_sanity.conf deleted file mode 100644 index 97bcc12a7..000000000 --- a/certora/confs/historical_confs/Safe4337Module_sanity.conf +++ /dev/null @@ -1,23 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "modules/4337/contracts/Safe4337Module.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity", - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "prover_version": "master", - "server": "production", - "verify": "Safe4337Module:certora/specs/setup/sanity.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/Safe4337Module_sanity_with_all_default_summaries.conf b/certora/confs/historical_confs/Safe4337Module_sanity_with_all_default_summaries.conf deleted file mode 100644 index b9753aa5e..000000000 --- a/certora/confs/historical_confs/Safe4337Module_sanity_with_all_default_summaries.conf +++ /dev/null @@ -1,28 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "modules/4337/contracts/Safe4337Module.sol", - "certora/harnesses/ERC20Like/DummyWeth.sol", - "certora/harnesses/Utilities.sol", - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity_with_all_default_summaries", - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "optimistic_hashing": true, - "optimistic_loop": true, - "optimistic_fallback": true, - "prover_version": "master", - "server": "production", - "verify": "Safe4337Module:certora/specs/setup/sanity_with_all_default_summaries.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/Safe4337Module_sanity_with_erc20cvl.conf b/certora/confs/historical_confs/Safe4337Module_sanity_with_erc20cvl.conf deleted file mode 100644 index 9c208e711..000000000 --- a/certora/confs/historical_confs/Safe4337Module_sanity_with_erc20cvl.conf +++ /dev/null @@ -1,26 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "certora/harnesses/ERC20Like/DummyWeth.sol", - "certora/harnesses/Utilities.sol", - "modules/4337/contracts/Safe4337Module.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity_with_erc20cvl", - "optimistic_fallback": true, - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "prover_version": "master", - "server": "production", - "verify": "Safe4337Module:certora/specs/setup/sanity_with_erc20cvl.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/Safe4337Module_sanity_with_erc20dispatched.conf b/certora/confs/historical_confs/Safe4337Module_sanity_with_erc20dispatched.conf deleted file mode 100644 index 3bc0f8556..000000000 --- a/certora/confs/historical_confs/Safe4337Module_sanity_with_erc20dispatched.conf +++ /dev/null @@ -1,27 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "certora/harnesses/ERC20Like/DummyERC20A.sol", - "certora/harnesses/ERC20Like/DummyERC20B.sol", - "certora/harnesses/ERC20Like/DummyWeth.sol", - "modules/4337/contracts/Safe4337Module.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity_with_erc20dispatched", - "optimistic_fallback": true, - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "prover_version": "master", - "server": "production", - "verify": "Safe4337Module:certora/specs/setup/sanity_with_erc20dispatched.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/SafeModuleSetup.conf b/certora/confs/historical_confs/SafeModuleSetup.conf deleted file mode 100644 index e59dce6c1..000000000 --- a/certora/confs/historical_confs/SafeModuleSetup.conf +++ /dev/null @@ -1,26 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "modules/4337/contracts/SafeModuleSetup.sol", - "certora/harnesses/ERC20Like/DummyWeth.sol", - "certora/harnesses/Utilities.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity", - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "optimistic_loop": true, - "prover_version": "master", - "server": "production", - "verify": "SafeModuleSetup:certora/specs/SafeModuleSetup.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/SafeModuleSetup_builtin_assertions.conf b/certora/confs/historical_confs/SafeModuleSetup_builtin_assertions.conf deleted file mode 100644 index e98d54d55..000000000 --- a/certora/confs/historical_confs/SafeModuleSetup_builtin_assertions.conf +++ /dev/null @@ -1,25 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "modules/4337/contracts/SafeModuleSetup.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "builtin_assertions", - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "optimistic_loop": true, - "loop_iter": "1", - "prover_version": "master", - "server": "production", - "verify": "SafeModuleSetup:certora/specs/setup/builtin_assertions.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/SafeModuleSetup_sanity.conf b/certora/confs/historical_confs/SafeModuleSetup_sanity.conf deleted file mode 100644 index 7a952d232..000000000 --- a/certora/confs/historical_confs/SafeModuleSetup_sanity.conf +++ /dev/null @@ -1,23 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "modules/4337/contracts/SafeModuleSetup.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity", - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "prover_version": "master", - "server": "production", - "verify": "SafeModuleSetup:certora/specs/setup/sanity.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/SafeModuleSetup_sanity_with_all_default_summaries.conf b/certora/confs/historical_confs/SafeModuleSetup_sanity_with_all_default_summaries.conf deleted file mode 100644 index e874c3a20..000000000 --- a/certora/confs/historical_confs/SafeModuleSetup_sanity_with_all_default_summaries.conf +++ /dev/null @@ -1,26 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "modules/4337/contracts/SafeModuleSetup.sol", - "certora/harnesses/ERC20Like/DummyWeth.sol", - "certora/harnesses/Utilities.sol", - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity_with_all_default_summaries", - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "optimistic_loop": true, - "prover_version": "master", - "server": "production", - "verify": "SafeModuleSetup:certora/specs/setup/sanity_with_all_default_summaries.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/SafeModuleSetup_sanity_with_erc20cvl.conf b/certora/confs/historical_confs/SafeModuleSetup_sanity_with_erc20cvl.conf deleted file mode 100644 index 6b02ab451..000000000 --- a/certora/confs/historical_confs/SafeModuleSetup_sanity_with_erc20cvl.conf +++ /dev/null @@ -1,26 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "certora/harnesses/ERC20Like/DummyWeth.sol", - "certora/harnesses/Utilities.sol", - "modules/4337/contracts/SafeModuleSetup.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity_with_erc20cvl", - "optimistic_fallback": true, - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "prover_version": "master", - "server": "production", - "verify": "SafeModuleSetup:certora/specs/setup/sanity_with_erc20cvl.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/SafeModuleSetup_sanity_with_erc20dispatched.conf b/certora/confs/historical_confs/SafeModuleSetup_sanity_with_erc20dispatched.conf deleted file mode 100644 index 0272940c7..000000000 --- a/certora/confs/historical_confs/SafeModuleSetup_sanity_with_erc20dispatched.conf +++ /dev/null @@ -1,27 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "certora/harnesses/ERC20Like/DummyERC20A.sol", - "certora/harnesses/ERC20Like/DummyERC20B.sol", - "certora/harnesses/ERC20Like/DummyWeth.sol", - "modules/4337/contracts/SafeModuleSetup.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity_with_erc20dispatched", - "optimistic_fallback": true, - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "prover_version": "master", - "server": "production", - "verify": "SafeModuleSetup:certora/specs/setup/sanity_with_erc20dispatched.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/SafeSignerLaunchpad.conf b/certora/confs/historical_confs/SafeSignerLaunchpad.conf deleted file mode 100644 index d22eb8f5e..000000000 --- a/certora/confs/historical_confs/SafeSignerLaunchpad.conf +++ /dev/null @@ -1,28 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "modules/passkey/contracts/4337/SafeSignerLaunchpad.sol", - "certora/harnesses/ERC20Like/DummyWeth.sol", - "certora/harnesses/Utilities.sol", - "modules/passkey/contracts/SafeWebAuthnSignerFactory.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity_with_all_default_summaries", - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "optimistic_hashing": true, - "optimistic_fallback": true, - "prover_version": "master", - "server": "production", - "verify": "SafeSignerLaunchpad:certora/specs/SafeSignerLaunchpad.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/SafeSignerLaunchpad_builtin_assertions.conf b/certora/confs/historical_confs/SafeSignerLaunchpad_builtin_assertions.conf deleted file mode 100644 index 2d643ebb4..000000000 --- a/certora/confs/historical_confs/SafeSignerLaunchpad_builtin_assertions.conf +++ /dev/null @@ -1,24 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "modules/passkey/contracts/4337/SafeSignerLaunchpad.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "builtin_assertions", - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "optimistic_hashing": true, - "prover_version": "master", - "server": "production", - "verify": "SafeSignerLaunchpad:certora/specs/setup/builtin_assertions.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/SafeSignerLaunchpad_sanity.conf b/certora/confs/historical_confs/SafeSignerLaunchpad_sanity.conf deleted file mode 100644 index dc0d4a8bb..000000000 --- a/certora/confs/historical_confs/SafeSignerLaunchpad_sanity.conf +++ /dev/null @@ -1,23 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "modules/passkey/contracts/4337/SafeSignerLaunchpad.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity", - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "prover_version": "master", - "server": "production", - "verify": "SafeSignerLaunchpad:certora/specs/setup/sanity.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/SafeSignerLaunchpad_sanity_with_all_default_summaries.conf b/certora/confs/historical_confs/SafeSignerLaunchpad_sanity_with_all_default_summaries.conf deleted file mode 100644 index e26e4d86d..000000000 --- a/certora/confs/historical_confs/SafeSignerLaunchpad_sanity_with_all_default_summaries.conf +++ /dev/null @@ -1,26 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "modules/passkey/contracts/4337/SafeSignerLaunchpad.sol", - "certora/harnesses/ERC20Like/DummyWeth.sol", - "certora/harnesses/Utilities.sol", - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity_with_all_default_summaries", - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "optimistic_hashing": true, - "prover_version": "master", - "server": "production", - "verify": "SafeSignerLaunchpad:certora/specs/setup/sanity_with_all_default_summaries.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/SafeSignerLaunchpad_sanity_with_erc20cvl.conf b/certora/confs/historical_confs/SafeSignerLaunchpad_sanity_with_erc20cvl.conf deleted file mode 100644 index 4d46f612e..000000000 --- a/certora/confs/historical_confs/SafeSignerLaunchpad_sanity_with_erc20cvl.conf +++ /dev/null @@ -1,26 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "certora/harnesses/ERC20Like/DummyWeth.sol", - "certora/harnesses/Utilities.sol", - "modules/passkey/contracts/4337/SafeSignerLaunchpad.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity_with_erc20cvl", - "optimistic_fallback": true, - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "prover_version": "master", - "server": "production", - "verify": "SafeSignerLaunchpad:certora/specs/setup/sanity_with_erc20cvl.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/SafeSignerLaunchpad_sanity_with_erc20dispatched.conf b/certora/confs/historical_confs/SafeSignerLaunchpad_sanity_with_erc20dispatched.conf deleted file mode 100644 index f0ea64181..000000000 --- a/certora/confs/historical_confs/SafeSignerLaunchpad_sanity_with_erc20dispatched.conf +++ /dev/null @@ -1,27 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "certora/harnesses/ERC20Like/DummyERC20A.sol", - "certora/harnesses/ERC20Like/DummyERC20B.sol", - "certora/harnesses/ERC20Like/DummyWeth.sol", - "modules/passkey/contracts/4337/SafeSignerLaunchpad.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity_with_erc20dispatched", - "optimistic_fallback": true, - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "prover_version": "master", - "server": "production", - "verify": "SafeSignerLaunchpad:certora/specs/setup/sanity_with_erc20dispatched.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/SafeWebAuthnSignerFactory_builtin_assertions.conf b/certora/confs/historical_confs/SafeWebAuthnSignerFactory_builtin_assertions.conf deleted file mode 100644 index 3005f1650..000000000 --- a/certora/confs/historical_confs/SafeWebAuthnSignerFactory_builtin_assertions.conf +++ /dev/null @@ -1,26 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "modules/passkey/contracts/SafeWebAuthnSignerFactory.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "builtin_assertions", - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "optimistic_hashing": true, - "optimistic_loop": true, - "loop_iter": "1", - "prover_version": "master", - "server": "production", - "verify": "SafeWebAuthnSignerFactory:certora/specs/setup/builtin_assertions.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/SafeWebAuthnSignerFactory_sanity.conf b/certora/confs/historical_confs/SafeWebAuthnSignerFactory_sanity.conf deleted file mode 100644 index 81e53fb85..000000000 --- a/certora/confs/historical_confs/SafeWebAuthnSignerFactory_sanity.conf +++ /dev/null @@ -1,25 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "modules/passkey/contracts/SafeWebAuthnSignerFactory.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity", - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "optimistic_loop": true, - "loop_iter": "6", - "prover_version": "master", - "server": "production", - "verify": "SafeWebAuthnSignerFactory:certora/specs/setup/sanity.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/SafeWebAuthnSignerFactory_sanity_with_all_default_summaries.conf b/certora/confs/historical_confs/SafeWebAuthnSignerFactory_sanity_with_all_default_summaries.conf deleted file mode 100644 index fda18c771..000000000 --- a/certora/confs/historical_confs/SafeWebAuthnSignerFactory_sanity_with_all_default_summaries.conf +++ /dev/null @@ -1,27 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "modules/passkey/contracts/SafeWebAuthnSignerFactory.sol", - "certora/harnesses/ERC20Like/DummyWeth.sol", - "certora/harnesses/Utilities.sol", - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity_with_all_default_summaries", - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "optimistic_loop": true, - "optimistic_hashing": true, - "prover_version": "master", - "server": "production", - "verify": "SafeWebAuthnSignerFactory:certora/specs/setup/sanity_with_all_default_summaries.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/SafeWebAuthnSignerFactory_sanity_with_erc20cvl.conf b/certora/confs/historical_confs/SafeWebAuthnSignerFactory_sanity_with_erc20cvl.conf deleted file mode 100644 index c35f40bc0..000000000 --- a/certora/confs/historical_confs/SafeWebAuthnSignerFactory_sanity_with_erc20cvl.conf +++ /dev/null @@ -1,26 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "certora/harnesses/ERC20Like/DummyWeth.sol", - "certora/harnesses/Utilities.sol", - "modules/passkey/contracts/SafeWebAuthnSignerFactory.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity_with_erc20cvl", - "optimistic_fallback": true, - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "prover_version": "master", - "server": "production", - "verify": "SafeWebAuthnSignerFactory:certora/specs/setup/sanity_with_erc20cvl.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/SafeWebAuthnSignerFactory_sanity_with_erc20dispatched.conf b/certora/confs/historical_confs/SafeWebAuthnSignerFactory_sanity_with_erc20dispatched.conf deleted file mode 100644 index b7c1f658f..000000000 --- a/certora/confs/historical_confs/SafeWebAuthnSignerFactory_sanity_with_erc20dispatched.conf +++ /dev/null @@ -1,27 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "certora/harnesses/ERC20Like/DummyERC20A.sol", - "certora/harnesses/ERC20Like/DummyERC20B.sol", - "certora/harnesses/ERC20Like/DummyWeth.sol", - "modules/passkey/contracts/SafeWebAuthnSignerFactory.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity_with_erc20dispatched", - "optimistic_fallback": true, - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "prover_version": "master", - "server": "production", - "verify": "SafeWebAuthnSignerFactory:certora/specs/setup/sanity_with_erc20dispatched.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/SafeWebAuthnSigner_builtin_assertions.conf b/certora/confs/historical_confs/SafeWebAuthnSigner_builtin_assertions.conf deleted file mode 100644 index 570da61c9..000000000 --- a/certora/confs/historical_confs/SafeWebAuthnSigner_builtin_assertions.conf +++ /dev/null @@ -1,28 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "modules/passkey/contracts/SafeWebAuthnSigner.sol", - "certora/harnesses/ERC20Like/DummyWeth.sol", - "certora/harnesses/Utilities.sol", - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "builtin_assertions", - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "optimistic_hashing": true, - "optimistic_loop": true, - "loop_iter": "1", - "prover_version": "master", - "server": "production", - "verify": "SafeWebAuthnSigner:certora/specs/setup/builtin_assertions.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/SafeWebAuthnSigner_sanity.conf b/certora/confs/historical_confs/SafeWebAuthnSigner_sanity.conf deleted file mode 100644 index f75d289d7..000000000 --- a/certora/confs/historical_confs/SafeWebAuthnSigner_sanity.conf +++ /dev/null @@ -1,25 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "modules/passkey/contracts/SafeWebAuthnSigner.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity", - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "optimistic_loop": true, - "loop_iter": "6", - "prover_version": "master", - "server": "production", - "verify": "SafeWebAuthnSigner:certora/specs/setup/sanity.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/SafeWebAuthnSigner_sanity_with_all_default_summaries.conf b/certora/confs/historical_confs/SafeWebAuthnSigner_sanity_with_all_default_summaries.conf deleted file mode 100644 index c73b8a3d2..000000000 --- a/certora/confs/historical_confs/SafeWebAuthnSigner_sanity_with_all_default_summaries.conf +++ /dev/null @@ -1,27 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "modules/passkey/contracts/SafeWebAuthnSigner.sol", - "certora/harnesses/ERC20Like/DummyWeth.sol", - "certora/harnesses/Utilities.sol", - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity_with_all_default_summaries", - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "optimistic_loop": true, - "optimistic_hashing": true, - "prover_version": "master", - "server": "production", - "verify": "SafeWebAuthnSigner:certora/specs/setup/sanity_with_all_default_summaries.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/SafeWebAuthnSigner_sanity_with_erc20cvl.conf b/certora/confs/historical_confs/SafeWebAuthnSigner_sanity_with_erc20cvl.conf deleted file mode 100644 index da1bff5fd..000000000 --- a/certora/confs/historical_confs/SafeWebAuthnSigner_sanity_with_erc20cvl.conf +++ /dev/null @@ -1,26 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "certora/harnesses/ERC20Like/DummyWeth.sol", - "certora/harnesses/Utilities.sol", - "modules/passkey/contracts/SafeWebAuthnSigner.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity_with_erc20cvl", - "optimistic_fallback": true, - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "prover_version": "master", - "server": "production", - "verify": "SafeWebAuthnSigner:certora/specs/setup/sanity_with_erc20cvl.spec" -} \ No newline at end of file diff --git a/certora/confs/historical_confs/SafeWebAuthnSigner_sanity_with_erc20dispatched.conf b/certora/confs/historical_confs/SafeWebAuthnSigner_sanity_with_erc20dispatched.conf deleted file mode 100644 index e2b142a8c..000000000 --- a/certora/confs/historical_confs/SafeWebAuthnSigner_sanity_with_erc20dispatched.conf +++ /dev/null @@ -1,27 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "certora/harnesses/ERC20Like/DummyERC20A.sol", - "certora/harnesses/ERC20Like/DummyERC20B.sol", - "certora/harnesses/ERC20Like/DummyWeth.sol", - "modules/passkey/contracts/SafeWebAuthnSigner.sol" - ], - "java_args": [ - " -ea -Dlevel.setup.helpers=info" - ], - "msg": "sanity_with_erc20dispatched", - "optimistic_fallback": true, - "process": "emv", - "prover_args": [ - " -verifyCache -verifyTACDumps -testMode -checkRuleDigest -callTraceHardFail on" - ], - "packages":[ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "solc": "solc8.23", - "solc_via_ir": false, - "prover_version": "master", - "server": "production", - "verify": "SafeWebAuthnSigner:certora/specs/setup/sanity_with_erc20dispatched.spec" -} \ No newline at end of file diff --git a/certora/harnesses/ERC20Like/DummyERC20A.sol b/certora/harnesses/ERC20Like/DummyERC20A.sol deleted file mode 100644 index f6138eb04..000000000 --- a/certora/harnesses/ERC20Like/DummyERC20A.sol +++ /dev/null @@ -1,55 +0,0 @@ -// Represents a symbolic/dummy ERC20 token - -// SPDX-License-Identifier: agpl-3.0 -pragma solidity >=0.7.6; - -contract DummyERC20A { - uint256 t; - mapping(address => uint256) b; - mapping(address => mapping(address => uint256)) a; - - string public name; - string public symbol; - uint public decimals; - - function myAddress() external view returns (address) { - return address(this); - } - - function totalSupply() external view returns (uint256) { - return t; - } - - function balanceOf(address account) external view returns (uint256) { - return b[account]; - } - - function transfer(address recipient, uint256 amount) external returns (bool) { - b[msg.sender] -= amount; - b[recipient] += amount; - - return true; - } - - function allowance(address owner, address spender) external view returns (uint256) { - return a[owner][spender]; - } - - function approve(address spender, uint256 amount) external returns (bool) { - a[msg.sender][spender] = amount; - - return true; - } - - function transferFrom( - address sender, - address recipient, - uint256 amount - ) external returns (bool) { - b[sender] -= amount; - b[recipient] += amount; - a[sender][msg.sender] -= amount; - - return true; - } -} diff --git a/certora/harnesses/ERC20Like/DummyERC20B.sol b/certora/harnesses/ERC20Like/DummyERC20B.sol deleted file mode 100644 index 42e2c201b..000000000 --- a/certora/harnesses/ERC20Like/DummyERC20B.sol +++ /dev/null @@ -1,55 +0,0 @@ -// Represents a symbolic/dummy ERC20 token - -// SPDX-License-Identifier: agpl-3.0 -pragma solidity >=0.7.6; - -contract DummyERC20B { - uint256 t; - mapping(address => uint256) b; - mapping(address => mapping(address => uint256)) a; - - string public name; - string public symbol; - uint public decimals; - - function myAddress() external view returns (address) { - return address(this); - } - - function totalSupply() external view returns (uint256) { - return t; - } - - function balanceOf(address account) external view returns (uint256) { - return b[account]; - } - - function transfer(address recipient, uint256 amount) external returns (bool) { - b[msg.sender] -= amount; - b[recipient] += amount; - - return true; - } - - function allowance(address owner, address spender) external view returns (uint256) { - return a[owner][spender]; - } - - function approve(address spender, uint256 amount) external returns (bool) { - a[msg.sender][spender] = amount; - - return true; - } - - function transferFrom( - address sender, - address recipient, - uint256 amount - ) external returns (bool) { - b[sender] -= amount; - b[recipient] += amount; - a[sender][msg.sender] -= amount; - - return true; - } -} diff --git a/certora/harnesses/ERC20Like/DummyWeth.sol b/certora/harnesses/ERC20Like/DummyWeth.sol deleted file mode 100644 index 23f6befa0..000000000 --- a/certora/harnesses/ERC20Like/DummyWeth.sol +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-License-Identifier: agpl-3.0 -pragma solidity >=0.7.0; - -/** - * Dummy Weth token. - */ -contract DummyWeth { - uint256 t; - - mapping(address => uint256) b; - mapping(address => mapping(address => uint256)) a; - - string public name; - string public symbol; - uint public decimals; - - function myAddress() external view returns (address) { - return address(this); - } - - function totalSupply() external view returns (uint256) { - return t; - } - - function balanceOf(address account) external view returns (uint256) { - return b[account]; - } - - function transfer(address recipient, uint256 amount) external returns (bool) { - b[msg.sender] -= amount; - b[recipient] += amount; - return true; - } - - function allowance(address owner, address spender) external view returns (uint256) { - return a[owner][spender]; - } - - function approve(address spender, uint256 amount) external returns (bool) { - a[msg.sender][spender] = amount; - return true; - } - - function transferFrom( - address sender, - address recipient, - uint256 amount - ) external returns (bool) { - b[sender] -= amount; - b[recipient] += amount; - a[sender][msg.sender] -= amount; - return true; - } - - // WETH - function deposit() external payable { - b[msg.sender] += msg.value; - } - - function withdraw(uint256 amt) external { - b[msg.sender] -= amt; - payable(msg.sender).transfer(amt); // use optimistic_fallback here - } -} diff --git a/certora/harnesses/GetConfigurationProxyHarness.sol b/certora/harnesses/GetConfigurationProxyHarness.sol new file mode 100644 index 000000000..6a7d14c2e --- /dev/null +++ b/certora/harnesses/GetConfigurationProxyHarness.sol @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: LGPL-3.0-only +/* solhint-disable no-complex-fallback */ +pragma solidity >=0.8.0; + +import {P256} from "modules/passkey/contracts/libraries/WebAuthn.sol"; + +/** + * @title Safe WebAuthn Signer Proxy + * @dev A specialized proxy to a {SafeWebAuthnSignerSingleton} signature validator implementation + * for Safe accounts. Using a proxy pattern for the signature validator greatly reduces deployment + * gas costs. + * @custom:security-contact bounty@safe.global + */ +contract GetConfigurationProxyHarness { + /** + * @notice The {SafeWebAuthnSignerSingleton} implementation to proxy to. + */ + address internal immutable _SINGLETON; + + /** + * @notice The x coordinate of the P-256 public key of the WebAuthn credential. + */ + uint256 internal immutable _X; + + /** + * @notice The y coordinate of the P-256 public key of the WebAuthn credential. + */ + uint256 internal immutable _Y; + + /** + * @notice The P-256 verifiers used for ECDSA signature verification. + */ + P256.Verifiers internal immutable _VERIFIERS; + + + function getX() external view returns (uint256) { return _X; } + function getY() external view returns (uint256) { return _Y; } + function getVerifiers() external view returns (P256.Verifiers) { return _VERIFIERS; } + + /** + * @notice Creates a new WebAuthn Safe Signer Proxy. + * @param singleton The {SafeWebAuthnSignerSingleton} implementation to proxy to. + * @param x The x coordinate of the P-256 public key of the WebAuthn credential. + * @param y The y coordinate of the P-256 public key of the WebAuthn credential. + * @param verifiers The P-256 verifiers used for ECDSA signature verification. + */ + constructor(address singleton, uint256 x, uint256 y, P256.Verifiers verifiers) { + _SINGLETON = singleton; + _X = x; + _Y = y; + _VERIFIERS = verifiers; + } + + /** + * @dev Fallback function forwards all transactions and returns all received return data. + */ + //fallback() external payable { + function getConfiguration() external returns (uint256 x, uint256 y, P256.Verifiers verifiers) { + address singleton = _SINGLETON; + uint256 x = _X; + uint256 y = _Y; + P256.Verifiers verifiers = _VERIFIERS; + + // solhint-disable-next-line no-inline-assembly + assembly { + // Forward the call to the singleton implementation. We append the configuration to the + // calldata instead of having the singleton implementation read it from storage. This is + // both more gas efficient and required for ERC-4337 compatibility. Note that we append + // the configuration fields in reverse order since the fields are packed, and this makes + // it so we don't need to mask any bits from the `verifiers` value. This computes `data` + // to be `abi.encodePacked(msg.data, x, y, verifiers)`. + let data := mload(0x40) + mstore(add(data, add(calldatasize(), 0x36)), verifiers) + mstore(add(data, add(calldatasize(), 0x20)), y) + mstore(add(data, calldatasize()), x) + calldatacopy(data, 0x00, calldatasize()) + + let success := staticcall(gas(), singleton, data, add(calldatasize(), 0x56), 0, 0) + returndatacopy(0, 0, returndatasize()) + if iszero(success) { + revert(0, returndatasize()) + } + return(0, returndatasize()) + } + } +} diff --git a/certora/harnesses/ProxySimulator.sol b/certora/harnesses/ProxySimulator.sol new file mode 100644 index 000000000..d9decc29f --- /dev/null +++ b/certora/harnesses/ProxySimulator.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: LGPL-3.0-only +/* solhint-disable no-complex-fallback */ +pragma solidity >=0.8.0; + +import {SafeWebAuthnSignerProxy} from "../../modules/passkey/contracts/SafeWebAuthnSignerProxy.sol"; + +contract ProxySimulator { + + address internal _proxy; + + constructor(address proxy) { + _proxy = proxy; + } + + function authenticate(bytes32 message, bytes calldata signature) external returns (bytes4) { + bytes memory data = abi.encodeWithSignature("isValidSignature(bytes32,bytes)", message, signature); + + (bool success, bytes memory result) = _proxy.call(data); + + require(success); + + return abi.decode(result, (bytes4)); + } +} \ No newline at end of file diff --git a/certora/munged/SafeWebAuthnSignerFactory.sol b/certora/munged/SafeWebAuthnSignerFactory.sol index d942bbcd2..0f9de62ca 100644 --- a/certora/munged/SafeWebAuthnSignerFactory.sol +++ b/certora/munged/SafeWebAuthnSignerFactory.sol @@ -62,9 +62,7 @@ contract SafeWebAuthnSignerFactory is ISafeSignerFactory { /** * @inheritdoc ISafeSignerFactory */ - // munged change magicValue := mload(0) => mload(mload(0x40)) - // staticcall(gas(), singleton, add(data, 0x20), mload(data), 0, 32) => staticcall(gas(), singleton, add(data, 0x20), mload(data), mload(0x40), 32) - function isValidSignatureForSigner( + function isValidSignatureForSigner( bytes32 message, bytes calldata signature, uint256 x, @@ -83,8 +81,8 @@ contract SafeWebAuthnSignerFactory is ISafeSignerFactory { assembly { // staticcall to the singleton contract with return size given as 32 bytes. The // singleton contract is known and immutable so it is safe to specify return size. - if staticcall(gas(), singleton, add(data, 0x20), mload(data), mload(0x40), 32) { - magicValue := mload(mload(0x40)) + if staticcall(gas(), singleton, add(data, 0x20), mload(data), 0, 32) { + magicValue := mload(0) } } } diff --git a/certora/munged/SafeWebAuthnSignerSingleton.sol b/certora/munged/SafeWebAuthnSignerSingleton.sol new file mode 100644 index 000000000..4922d0a4a --- /dev/null +++ b/certora/munged/SafeWebAuthnSignerSingleton.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity >=0.8.0; + +import {SignatureValidator} from "../../modules/passkey/contracts/base/SignatureValidator.sol"; +import {P256, WebAuthn} from "../../modules/passkey/contracts/libraries/WebAuthn.sol"; + +/** + * @title Safe WebAuthn Signer Singleton + * @dev A singleton contract that implements WebAuthn signature verification. This singleton + * contract must be used with the specialized proxy {SafeWebAuthnSignerProxy}, as it encodes the + * credential configuration (public key coordinates and P-256 verifier to use) in calldata, which is + * required by this implementation. + * @custom:security-contact bounty@safe.global + */ +contract SafeWebAuthnSignerSingleton is SignatureValidator { + /** + * @inheritdoc SignatureValidator + */ + function _verifySignature(bytes32 message, bytes calldata signature) internal view virtual override returns (bool success) { + (uint256 x, uint256 y, P256.Verifiers verifiers) = getConfiguration(); + success = WebAuthn.verifySignature(message, signature, WebAuthn.USER_VERIFICATION, x, y, verifiers); + } + + /** + * @notice Returns the x coordinate, y coordinate, and P-256 verifiers used for ECDSA signature + * validation. The values are expected to appended to calldata by the caller. See the + * {SafeWebAuthnSignerProxy} contract implementation. + * @return x The x coordinate of the P-256 public key. + * @return y The y coordinate of the P-256 public key. + * @return verifiers The P-256 verifiers. + */ + function getConfiguration() public pure returns (uint256 x, uint256 y, P256.Verifiers verifiers) { + // solhint-disable-next-line no-inline-assembly + // solhint-disable-next-line no-inline-assembly + // MUNGED! - Added new line + uint256 mask = type(uint176).max; + assembly ("memory-safe") { + x := calldataload(sub(calldatasize(), 86)) + y := calldataload(sub(calldatasize(), 54)) + // MUNGED! Original line is - verifiers := shr(80, calldataload(sub(calldatasize(), 22))) + verifiers := and(mask, calldataload(sub(calldatasize(), 32))) + } + } +} diff --git a/certora/specs/GetConfigurationSpec.spec b/certora/specs/GetConfigurationSpec.spec index 75f03311f..bd68bbf7f 100644 --- a/certora/specs/GetConfigurationSpec.spec +++ b/certora/specs/GetConfigurationSpec.spec @@ -1,8 +1,13 @@ -using Utilities as utilities; -using SafeWebAuthnSignerProxy as proxy; +using GetConfigurationProxyHarness as proxy; methods { - function _._ external => DISPATCH [ SafeWebAuthnSignerProxy._, SafeWebAuthnSignerSingleton._ ] default HAVOC_ALL; + function GetConfigurationProxyHarness.getX() external returns (uint256) envfree; + function GetConfigurationProxyHarness.getY() external returns (uint256) envfree; + function GetConfigurationProxyHarness.getVerifiers() external returns (P256.Verifiers) envfree; + + function _._ external => DISPATCH [ + SafeWebAuthnSignerSingleton.getConfiguration() + ] default HAVOC_ALL; } /* @@ -11,23 +16,21 @@ methods { └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ -rule configGetConfiguration { - env e; - - uint256 xBefore = currentContract._X; - uint256 yBefore = currentContract._Y; - P256.Verifiers verifiersBefore = currentContract._VERIFIERS; +rule verifyGetConfigurationIntegrity(env e){ - uint256 xAfter; - uint256 yAfter; - P256.Verifiers verifiersAfter; + uint256 x; + uint256 y; + P256.Verifiers verifiers; + uint256 new_x; uint256 new_y; P256.Verifiers new_verifiers; + bytes32 message; - xAfter, yAfter, verifiersAfter = utilities.getConfiguration(e, proxy); + x = proxy.getX(); + y = proxy.getY(); + verifiers = proxy.getVerifiers(); + (new_x, new_y, new_verifiers) = proxy.getConfiguration(e); - - assert xBefore == xAfter && - yBefore == yAfter && - verifiersBefore == verifiersAfter; + assert x == new_x; + assert y == new_y; + assert verifiers == new_verifiers; satisfy true; } - diff --git a/certora/specs/ProxySimulator.spec b/certora/specs/ProxySimulator.spec index ab8f4ea9f..88d656364 100644 --- a/certora/specs/ProxySimulator.spec +++ b/certora/specs/ProxySimulator.spec @@ -11,7 +11,6 @@ methods { function WebAuthnHarness.checkInjective(bytes32 challenge, bytes32 authenticatorData, bytes32 clientDataFields, bytes32 result) internal returns (bool) => checkInjectiveSummary(challenge, authenticatorData, clientDataFields, result); - function authenticate(bytes32, bytes) external returns (bytes4) envfree; function _._ external => DISPATCH [ SafeWebAuthnSignerProxy._, SafeWebAuthnSignerSingleton._ @@ -45,10 +44,11 @@ possible calldata values so this simulation will make the prover choose differen Rule stuck. */ rule proxyReturnValue { + env e; bytes32 message; bytes signature; - bytes4 ret = authenticate(message, signature); + bytes4 ret = authenticate(e, message, signature); satisfy ret == MAGIC_VALUE() || ret == to_bytes4(0); } diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index bb5cf3a1c..3ae19f23a 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -112,14 +112,9 @@ ghost mathint numOfCreation; ghost mapping(address => uint) address_map; ghost address signerAddress; -hook EXTCODESIZE(address addr) uint v{ - require address_map[addr] == v; -} - hook CREATE2(uint value, uint offset, uint length, bytes32 salt) address v{ require(v == signerAddress); numOfCreation = numOfCreation + 1; - address_map[v] = length; } rule SignerCreationCantOverride() From 161d0596fc6df7f5dc3a45dc983c01584c8e8eb5 Mon Sep 17 00:00:00 2001 From: yoavelmalem Date: Thu, 22 Aug 2024 14:58:59 +0300 Subject: [PATCH 097/103] Last fixes --- certora/confs/SafeWebAuthnSignerFactory.conf | 6 +- ...IsValidSignatureWillContinueToSucceed.conf | 27 ----- .../FactoryHarnessForSignerConsistency.sol | 39 +++++++ .../munged/FactoryForSignerConsistency.sol | 103 ++++++++++++++++++ certora/munged/SafeWebAuthnSignerFactory.sol | 8 +- 5 files changed, 151 insertions(+), 32 deletions(-) delete mode 100644 certora/confs/SingletonVerifyIsValidSignatureWillContinueToSucceed.conf create mode 100644 certora/harnesses/FactoryHarnessForSignerConsistency.sol create mode 100644 certora/munged/FactoryForSignerConsistency.sol diff --git a/certora/confs/SafeWebAuthnSignerFactory.conf b/certora/confs/SafeWebAuthnSignerFactory.conf index 8115966d7..e8a7fd674 100644 --- a/certora/confs/SafeWebAuthnSignerFactory.conf +++ b/certora/confs/SafeWebAuthnSignerFactory.conf @@ -1,13 +1,13 @@ { "assert_autofinder_success": true, "files": [ - "certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol", + "certora/harnesses/FactoryHarnessForSignerConsistency.sol", "certora/harnesses/WebAuthnHarness.sol", "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol" ], "link": [ - "SafeWebAuthnSignerFactoryHarness:SINGLETON=SafeWebAuthnSignerSingleton" + "FactoryHarnessForSignerConsistency:SINGLETON=SafeWebAuthnSignerSingleton" ], "loop_iter": "6", "optimistic_hashing": true, @@ -26,5 +26,5 @@ "process": "emv", "rule_sanity": "basic", "solc": "solc8.23", - "verify": "SafeWebAuthnSignerFactoryHarness:certora/specs/SafeWebAuthnSignerFactory.spec" + "verify": "FactoryHarnessForSignerConsistency:certora/specs/SafeWebAuthnSignerFactory.spec" } \ No newline at end of file diff --git a/certora/confs/SingletonVerifyIsValidSignatureWillContinueToSucceed.conf b/certora/confs/SingletonVerifyIsValidSignatureWillContinueToSucceed.conf deleted file mode 100644 index 4b7c9469d..000000000 --- a/certora/confs/SingletonVerifyIsValidSignatureWillContinueToSucceed.conf +++ /dev/null @@ -1,27 +0,0 @@ -{ - "assert_autofinder_success": true, - "files": [ - "modules/passkey/contracts/SafeWebAuthnSignerFactory.sol", - "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", - "modules/passkey/contracts/libraries/WebAuthn.sol", - "modules/passkey/contracts/libraries/P256.sol", - "certora/harnesses/WebAuthnHarness.sol" - ], - "link": [ - "SafeWebAuthnSignerFactory:SINGLETON=SafeWebAuthnSignerSingleton" - ], - "loop_iter": "6", - "optimistic_hashing": true, - "optimistic_loop": true, - "packages": [ - "@safe-global=node_modules/@safe-global", - "@account-abstraction=node_modules/@account-abstraction" - ], - "process": "emv", - "rule": [ - "verifyIsValidSignatureWillContinueToSucceed" - ], - "rule_sanity": "basic", - "solc": "solc8.23", - "verify": "SafeWebAuthnSignerSingleton:certora/specs/SafeWebAuthnSignerSingleton.spec" -} \ No newline at end of file diff --git a/certora/harnesses/FactoryHarnessForSignerConsistency.sol b/certora/harnesses/FactoryHarnessForSignerConsistency.sol new file mode 100644 index 000000000..925f09939 --- /dev/null +++ b/certora/harnesses/FactoryHarnessForSignerConsistency.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity >=0.8.0; + +import {SafeWebAuthnSignerFactory} from "../munged/FactoryForSignerConsistency.sol"; +import {P256} from "../../modules/passkey/contracts/libraries/P256.sol"; +import {SafeWebAuthnSignerProxy} from "../../modules/passkey/contracts/SafeWebAuthnSignerProxy.sol"; + +contract FactoryHarnessForSignerConsistency is SafeWebAuthnSignerFactory { + + //Harness + function hasNoCode(address account) external view returns (bool result) { + // solhint-disable-next-line no-inline-assembly + return SafeWebAuthnSignerFactory._hasNoCode(account); + } + + function createAndVerify( + bytes32 message, + bytes calldata signature, + uint256 x, + uint256 y, + P256.Verifiers verifiers + ) external returns (bytes4 magicValue) { + address signer = this.createSigner(x, y, verifiers); + + bytes memory data = abi.encodeWithSignature("isValidSignature(bytes32,bytes)", message, signature); + + // Use low-level call to invoke isValidSignature on the signer address + (bool success, bytes memory result) = signer.staticcall(data); + require(success); + magicValue = abi.decode(result, (bytes4)); + } + + /** + munge to pass the SignerCreationCantOverride rule. + */ + function _hasNoCode(address account) internal view override returns (bool result) { + return account.code.length == 0; + } +} diff --git a/certora/munged/FactoryForSignerConsistency.sol b/certora/munged/FactoryForSignerConsistency.sol new file mode 100644 index 000000000..83cfb8744 --- /dev/null +++ b/certora/munged/FactoryForSignerConsistency.sol @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity >=0.8.0; + +import {ISafeSignerFactory} from "../../modules/passkey/contracts/interfaces/ISafeSignerFactory.sol"; +import {SafeWebAuthnSignerProxy} from "../../modules/passkey/contracts/SafeWebAuthnSignerProxy.sol"; +import {SafeWebAuthnSignerSingleton} from "../../modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol"; +import {P256} from "../../modules/passkey/contracts/libraries/P256.sol"; + +/** + * @title Safe WebAuthn Signer Factory + * @dev A factory contract for creating WebAuthn signers. Additionally, the factory supports + * signature verification without deploying a signer proxies. + * @custom:security-contact bounty@safe.global + */ +contract SafeWebAuthnSignerFactory is ISafeSignerFactory { + /** + * @notice The {SafeWebAuthnSignerSingleton} implementation to that is used for signature + * verification by this contract and any proxies it deploys. + */ + SafeWebAuthnSignerSingleton public immutable SINGLETON; + + /** + * @notice Creates a new WebAuthn Safe signer factory contract. + * @dev The {SafeWebAuthnSignerSingleton} singleton implementation is created with as part of + * this constructor. This ensures that the singleton contract is known, and lets us make certain + * assumptions about how it works. + */ + constructor() { + SINGLETON = new SafeWebAuthnSignerSingleton(); + } + + /** + * @inheritdoc ISafeSignerFactory + */ + // funtion is not really virtual, Munged! + function getSigner(uint256 x, uint256 y, P256.Verifiers verifiers) public view virtual override returns (address signer) { + bytes32 codeHash = keccak256( + abi.encodePacked( + type(SafeWebAuthnSignerProxy).creationCode, + uint256(uint160(address(SINGLETON))), + x, + y, + uint256(P256.Verifiers.unwrap(verifiers)) + ) + ); + signer = address(uint160(uint256(keccak256(abi.encodePacked(hex"ff", address(this), bytes32(0), codeHash))))); + } + + /** + * @inheritdoc ISafeSignerFactory + */ + function createSigner(uint256 x, uint256 y, P256.Verifiers verifiers) external returns (address signer) { + signer = getSigner(x, y, verifiers); + + if (_hasNoCode(signer)) { + SafeWebAuthnSignerProxy created = new SafeWebAuthnSignerProxy{salt: bytes32(0)}(address(SINGLETON), x, y, verifiers); + assert(address(created) == signer); + emit Created(signer, x, y, verifiers); + } + } + + /** + * @inheritdoc ISafeSignerFactory + */ + function isValidSignatureForSigner( + bytes32 message, + bytes calldata signature, + uint256 x, + uint256 y, + P256.Verifiers verifiers + ) external view override returns (bytes4 magicValue) { + address singleton = address(SINGLETON); + bytes memory data = abi.encodePacked( + abi.encodeWithSignature("isValidSignature(bytes32,bytes)", message, signature), + x, + y, + verifiers + ); + + // solhint-disable-next-line no-inline-assembly + assembly { + // staticcall to the singleton contract with return size given as 32 bytes. The + // singleton contract is known and immutable so it is safe to specify return size. + // MUNGED!! + if staticcall(gas(), singleton, add(data, 0x20), mload(data), 0, 32) { + magicValue := mload(0) + } + } + } + + /** + * @dev Checks if the provided account has no code. + * @param account The address of the account to check. + * @return result True if the account has no code, false otherwise. + */ + // funtion is not really virtual, munged! + function _hasNoCode(address account) internal view virtual returns (bool result) { + // solhint-disable-next-line no-inline-assembly + assembly ("memory-safe") { + result := iszero(extcodesize(account)) + } + } +} diff --git a/certora/munged/SafeWebAuthnSignerFactory.sol b/certora/munged/SafeWebAuthnSignerFactory.sol index 0f9de62ca..afa122ae4 100644 --- a/certora/munged/SafeWebAuthnSignerFactory.sol +++ b/certora/munged/SafeWebAuthnSignerFactory.sol @@ -81,8 +81,12 @@ contract SafeWebAuthnSignerFactory is ISafeSignerFactory { assembly { // staticcall to the singleton contract with return size given as 32 bytes. The // singleton contract is known and immutable so it is safe to specify return size. - if staticcall(gas(), singleton, add(data, 0x20), mload(data), 0, 32) { - magicValue := mload(0) + // MUNGED!! + // if staticcall(gas(), singleton, add(data, 0x20), mload(data), 0, 32) { + // magicValue := mload(0) + // } + if staticcall(gas(), singleton, add(data, 0x20), mload(data), mload(0x40), 32) { + magicValue := mload(mload(0x40)) } } } From b8427fc7b1fd2288e7a6c00ebdcaeac3aea1a47e Mon Sep 17 00:00:00 2001 From: yoavelmalem Date: Tue, 27 Aug 2024 16:38:27 +0300 Subject: [PATCH 098/103] Add munge --- .../VerifyEQtoIsValidSignatureForSigner.conf | 7 +- certora/harnesses/WebAuthnHarness.sol | 2 +- certora/munged/SafeWebAuthnSignerFactory.sol | 6 +- certora/munged/SafeWebAuthnSignerProxy.sol | 81 ++++ .../munged/SafeWebAuthnSignerSingleton.sol | 2 +- certora/munged/WebAuthn.sol | 359 ++++++++++++++++++ certora/specs/ERC1967/erc1967.spec | 7 - certora/specs/ERC20/WETHcvl.spec | 35 -- certora/specs/ERC20/erc20cvl.spec | 56 --- certora/specs/ERC20/erc20dispatched.spec | 16 - certora/specs/ERC721/erc721.spec | 9 - certora/specs/PriceAggregators/chainlink.spec | 4 - certora/specs/PriceAggregators/tellor.spec | 3 - certora/specs/SafeWebAuthnSignerFactory.spec | 8 +- .../specs/SafeWebAuthnSignerSingleton.spec | 5 +- certora/specs/setup/builtin_assertions.spec | 12 - certora/specs/setup/sanity.spec | 5 - .../sanity_with_all_default_summaries.spec | 14 - certora/specs/setup/sanity_with_erc20cvl.spec | 8 - .../setup/sanity_with_erc20dispatched.spec | 7 - certora/specs/spec_utils/AllowanceModule.spec | 32 -- certora/specs/spec_utils/Safe4337Module.spec | 33 -- certora/specs/spec_utils/SafeModuleSetup.spec | 27 -- .../specs/spec_utils/SafeSignerLaunchpad.spec | 38 -- certora/specs/spec_utils/generic.spec | 88 ----- certora/specs/spec_utils/optimizations.spec | 5 - certora/specs/spec_utils/problems.spec | 1 - certora/specs/spec_utils/unresolved.spec | 4 - 28 files changed, 455 insertions(+), 419 deletions(-) create mode 100644 certora/munged/SafeWebAuthnSignerProxy.sol create mode 100644 certora/munged/WebAuthn.sol delete mode 100644 certora/specs/ERC1967/erc1967.spec delete mode 100644 certora/specs/ERC20/WETHcvl.spec delete mode 100644 certora/specs/ERC20/erc20cvl.spec delete mode 100644 certora/specs/ERC20/erc20dispatched.spec delete mode 100644 certora/specs/ERC721/erc721.spec delete mode 100644 certora/specs/PriceAggregators/chainlink.spec delete mode 100644 certora/specs/PriceAggregators/tellor.spec delete mode 100644 certora/specs/setup/builtin_assertions.spec delete mode 100644 certora/specs/setup/sanity.spec delete mode 100644 certora/specs/setup/sanity_with_all_default_summaries.spec delete mode 100644 certora/specs/setup/sanity_with_erc20cvl.spec delete mode 100644 certora/specs/setup/sanity_with_erc20dispatched.spec delete mode 100644 certora/specs/spec_utils/AllowanceModule.spec delete mode 100644 certora/specs/spec_utils/Safe4337Module.spec delete mode 100644 certora/specs/spec_utils/SafeModuleSetup.spec delete mode 100644 certora/specs/spec_utils/SafeSignerLaunchpad.spec delete mode 100644 certora/specs/spec_utils/generic.spec delete mode 100644 certora/specs/spec_utils/optimizations.spec delete mode 100644 certora/specs/spec_utils/problems.spec delete mode 100644 certora/specs/spec_utils/unresolved.spec diff --git a/certora/confs/VerifyEQtoIsValidSignatureForSigner.conf b/certora/confs/VerifyEQtoIsValidSignatureForSigner.conf index d02af0b96..6b3832095 100644 --- a/certora/confs/VerifyEQtoIsValidSignatureForSigner.conf +++ b/certora/confs/VerifyEQtoIsValidSignatureForSigner.conf @@ -3,10 +3,9 @@ "dynamic_bound": "1", "files": [ "certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol", - "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", - "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol", - "modules/passkey/contracts/libraries/WebAuthn.sol", - "modules/passkey/contracts/libraries/P256.sol", + "certora/munged/SafeWebAuthnSignerSingleton.sol", + "certora/munged/SafeWebAuthnSignerProxy.sol", + "certora/munged/WebAuthn.sol", "certora/harnesses/WebAuthnHarness.sol" ], "hashing_length_bound": "906", diff --git a/certora/harnesses/WebAuthnHarness.sol b/certora/harnesses/WebAuthnHarness.sol index da7509a54..68eda2c56 100644 --- a/certora/harnesses/WebAuthnHarness.sol +++ b/certora/harnesses/WebAuthnHarness.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; -import {P256, WebAuthn} from "../../modules/passkey/contracts/libraries/WebAuthn.sol"; +import {P256, WebAuthn} from "../munged/WebAuthn.sol"; contract WebAuthnHarness { diff --git a/certora/munged/SafeWebAuthnSignerFactory.sol b/certora/munged/SafeWebAuthnSignerFactory.sol index afa122ae4..65f44fec0 100644 --- a/certora/munged/SafeWebAuthnSignerFactory.sol +++ b/certora/munged/SafeWebAuthnSignerFactory.sol @@ -2,8 +2,8 @@ pragma solidity >=0.8.0; import {ISafeSignerFactory} from "../../modules/passkey/contracts/interfaces/ISafeSignerFactory.sol"; -import {SafeWebAuthnSignerProxy} from "../../modules/passkey/contracts/SafeWebAuthnSignerProxy.sol"; -import {SafeWebAuthnSignerSingleton} from "../../modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol"; +import {SafeWebAuthnSignerProxy} from "./SafeWebAuthnSignerProxy.sol"; +import {SafeWebAuthnSignerSingleton} from "./SafeWebAuthnSignerSingleton.sol"; import {P256} from "../../modules/passkey/contracts/libraries/P256.sol"; /** @@ -62,7 +62,7 @@ contract SafeWebAuthnSignerFactory is ISafeSignerFactory { /** * @inheritdoc ISafeSignerFactory */ - function isValidSignatureForSigner( + function isValidSignatureForSigner( bytes32 message, bytes calldata signature, uint256 x, diff --git a/certora/munged/SafeWebAuthnSignerProxy.sol b/certora/munged/SafeWebAuthnSignerProxy.sol new file mode 100644 index 000000000..7dbae9014 --- /dev/null +++ b/certora/munged/SafeWebAuthnSignerProxy.sol @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: LGPL-3.0-only +/* solhint-disable no-complex-fallback */ +pragma solidity >=0.8.0; + +import {P256} from "./WebAuthn.sol"; + +/** + * @title Safe WebAuthn Signer Proxy + * @dev A specialized proxy to a {SafeWebAuthnSignerSingleton} signature validator implementation + * for Safe accounts. Using a proxy pattern for the signature validator greatly reduces deployment + * gas costs. + * @custom:security-contact bounty@safe.global + */ +contract SafeWebAuthnSignerProxy { + /** + * @notice The {SafeWebAuthnSignerSingleton} implementation to proxy to. + */ + address internal immutable _SINGLETON; + + /** + * @notice The x coordinate of the P-256 public key of the WebAuthn credential. + */ + uint256 internal immutable _X; + + /** + * @notice The y coordinate of the P-256 public key of the WebAuthn credential. + */ + uint256 internal immutable _Y; + + /** + * @notice The P-256 verifiers used for ECDSA signature verification. + */ + P256.Verifiers internal immutable _VERIFIERS; + + /** + * @notice Creates a new WebAuthn Safe Signer Proxy. + * @param singleton The {SafeWebAuthnSignerSingleton} implementation to proxy to. + * @param x The x coordinate of the P-256 public key of the WebAuthn credential. + * @param y The y coordinate of the P-256 public key of the WebAuthn credential. + * @param verifiers The P-256 verifiers used for ECDSA signature verification. + */ + constructor(address singleton, uint256 x, uint256 y, P256.Verifiers verifiers) { + _SINGLETON = singleton; + _X = x; + _Y = y; + _VERIFIERS = verifiers; + } + + /** + * @dev Fallback function forwards all transactions and returns all received return data. + */ + fallback() external payable { + address singleton = _SINGLETON; + uint256 x = _X; + uint256 y = _Y; + P256.Verifiers verifiers = _VERIFIERS; + + // solhint-disable-next-line no-inline-assembly + assembly { + // Forward the call to the singleton implementation. We append the configuration to the + // calldata instead of having the singleton implementation read it from storage. This is + // both more gas efficient and required for ERC-4337 compatibility. Note that we append + // the configuration fields in reverse order since the fields are packed, and this makes + // it so we don't need to mask any bits from the `verifiers` value. This computes `data` + // to be `abi.encodePacked(msg.data, x, y, verifiers)`. + let data := mload(0x40) + // MUNGED mstore(add(data, add(calldatasize(), 0x36)), verifiers) + mstore(add(data, add(calldatasize(), 0x40)), shl(80, verifiers)) + mstore(add(data, add(calldatasize(), 0x20)), y) + mstore(add(data, calldatasize()), x) + calldatacopy(data, 0x00, calldatasize()) + + let success := delegatecall(gas(), singleton, data, add(calldatasize(), 0x56), 0, 0) + returndatacopy(0, 0, returndatasize()) + if iszero(success) { + revert(0, returndatasize()) + } + return(0, returndatasize()) + } + } +} diff --git a/certora/munged/SafeWebAuthnSignerSingleton.sol b/certora/munged/SafeWebAuthnSignerSingleton.sol index 4922d0a4a..a16238b03 100644 --- a/certora/munged/SafeWebAuthnSignerSingleton.sol +++ b/certora/munged/SafeWebAuthnSignerSingleton.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.0; import {SignatureValidator} from "../../modules/passkey/contracts/base/SignatureValidator.sol"; -import {P256, WebAuthn} from "../../modules/passkey/contracts/libraries/WebAuthn.sol"; +import {P256, WebAuthn} from "./WebAuthn.sol"; /** * @title Safe WebAuthn Signer Singleton diff --git a/certora/munged/WebAuthn.sol b/certora/munged/WebAuthn.sol new file mode 100644 index 000000000..cd09fb579 --- /dev/null +++ b/certora/munged/WebAuthn.sol @@ -0,0 +1,359 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +import {P256} from "../../modules/passkey/contracts/libraries/P256.sol"; + +/** + * @title WebAuthn Signature Verification + * @dev Library for verifying WebAuthn signatures for public key credentials using the ES256 + * algorithm with the P-256 curve. + * @custom:security-contact bounty@safe.global + */ +library WebAuthn { + using P256 for P256.Verifiers; + + /** + * @notice The WebAuthn signature data format. + * @dev WebAuthn signatures are expected to be the ABI-encoded bytes of the following structure. + * @param authenticatorData The authenticator data from the WebAuthn credential assertion. + * @param clientDataFields The additional fields from the client data JSON. This is the comma + * separated fields as they appear in the client data JSON from the WebAuthn credential + * assertion after the leading {type} and {challenge} fields. + * @param r The ECDSA signature's R component. + * @param s The ECDSA signature's S component. + */ + struct Signature { + bytes authenticatorData; + string clientDataFields; + uint256 r; + uint256 s; + } + + /** + * @notice A WebAuthn authenticator bit-flags + * @dev Represents flags that are included in a WebAuthn assertion's authenticator data and can + * be used to check on-chain how the user was authorized by the device when signing. + */ + type AuthenticatorFlags is bytes1; + + /** + * @notice Authenticator data flag indicating user presence (UP). + * @dev A test of user presence is a simple form of authorization gesture and technical process + * where a user interacts with an authenticator by (typically) simply touching it (other + * modalities may also exist), yielding a Boolean result. Note that this does not constitute + * user verification because a user presence test, by definition, is not capable of biometric + * recognition, nor does it involve the presentation of a shared secret such as a password or + * PIN. + * + * See . + */ + AuthenticatorFlags internal constant USER_PRESENCE = AuthenticatorFlags.wrap(0x01); + + /** + * @notice Authenticator data flag indicating user verification (UV). + * @dev The technical process by which an authenticator locally authorizes the invocation of the + * authenticatorMakeCredential and authenticatorGetAssertion operations. User verification MAY + * be instigated through various authorization gesture modalities; for example, through a touch + * plus pin code, password entry, or biometric recognition (e.g., presenting a fingerprint). The + * intent is to distinguish individual users. + * + * Note that user verification does not give the Relying Party a concrete identification of the + * user, but when 2 or more ceremonies with user verification have been done with that + * credential it expresses that it was the same user that performed all of them. The same user + * might not always be the same natural person, however, if multiple natural persons share + * access to the same authenticator. + * + * See . + */ + AuthenticatorFlags internal constant USER_VERIFICATION = AuthenticatorFlags.wrap(0x04); + + /** + * @notice Casts calldata bytes to a WebAuthn signature data structure. + * @param signature The calldata bytes of the WebAuthn signature. + * @return isValid Whether or not the encoded signature bytes is valid. + * @return data A pointer to the signature data in calldata. + * @dev This method casts the dynamic bytes array to a signature calldata pointer with some + * additional verification. Specifically, we ensure that the signature bytes encoding is no + * larger than standard ABI encoding form, to prevent attacks where valid signatures are padded + * with 0s in order to increase signature verifications the costs for ERC-4337 user operations. + */ + function castSignature(bytes calldata signature) internal pure returns (bool isValid, Signature calldata data) { + uint256 authenticatorDataLength; + uint256 clientDataFieldsLength; + isValid = true; + + // solhint-disable-next-line no-inline-assembly + assembly ("memory-safe") { + data := signature.offset + + // Read the lengths of the dynamic byte arrays in assembly. This is done because + // Solidity generates calldata bounds checks which aren't required for the security of + // the signature verification, as it can only lead to _shorter_ signatures which are + // are less gas expensive anyway. + + // MUNGED To simplify prover execution + let t1 := calldataload(data) + isValid := and(isValid, lt(t1, signature.length)) + authenticatorDataLength := calldataload(add(data, t1)) + let t2 := calldataload(add(data, 0x20)) + isValid := and(isValid, lt(t2, signature.length)) + clientDataFieldsLength := calldataload(add(data, calldataload(add(data, 0x20)))) + } + + // Use of unchecked math as any overflows in dynamic length computations would cause + // out-of-gas reverts when computing the WebAuthn signing message. + unchecked { + // Allow for signature encodings where the dynamic bytes are aligned to 32-byte + // boundaries. This allows for high interoperability (as this is how Solidity and most + // tools `abi.encode`s the `Signature` struct) while setting a strict upper bound to how + // many additional padding bytes can be added to the signature, increasing gas costs. + // Note that we compute the aligned lengths with the formula: `l + 0x1f & ~0x1f`, which + // rounds `l` up to the next 32-byte boundary. + uint256 alignmentMask = ~uint256(0x1f); + uint256 authenticatorDataAlignedLength = (authenticatorDataLength + 0x1f) & alignmentMask; + uint256 clientDataFieldsAlignedLength = (clientDataFieldsLength + 0x1f) & alignmentMask; + + // The fixed parts of the signature length are 6 32-byte words for a total of 192 bytes: + // - offset of the `authenticatorData` bytes + // - offset of the `clientDataFields` string + // - signature `r` value + // - signature `s` value + // - length of the `authenticatorData` bytes + // - length of the `clientDataFields` string + // + // This implies that the signature length must be less than or equal to: + // 192 + authenticatorDataAlignedLength + clientDataFieldsAlignedLength + // which is equivalent to strictly less than: + // 193 + authenticatorDataAlignedLength + clientDataFieldsAlignedLength + + // MUNGED To simplify prover execution + isValid = isValid && signature.length < 193 + authenticatorDataAlignedLength + clientDataFieldsAlignedLength; + } + } + + /** + * @notice Encodes the client data JSON string from the specified challenge, and additional + * client data fields. + * @dev The client data JSON follows a very specific encoding process outlined in the Web + * Authentication standard. See . + * @param challenge The WebAuthn challenge used for the credential assertion. + * @param clientDataFields Client data fields. + * @return clientDataJson The encoded client data JSON. + */ + function encodeClientDataJson( + bytes32 challenge, + string calldata clientDataFields + ) internal pure returns (string memory clientDataJson) { + // solhint-disable-next-line no-inline-assembly + assembly ("memory-safe") { + // The length of the encoded JSON string. This is always 82 plus the length of the + // additional client data fields: + // - 36 bytes for: `{"type":"webauthn.get","challenge":"` + // - 43 bytes for base-64 encoding of 32 bytes of data + // - 2 bytes for: `",` + // - `clientDataFields.length` bytes for the additional client data JSON fields + // - 1 byte for: `}` + let encodedLength := add(82, clientDataFields.length) + + // Set `clientDataJson` return parameter to point to the start of the free memory. + // This is where the encoded JSON will be stored. + clientDataJson := mload(0x40) + + // Write the constant bytes of the encoded client data JSON string as per the JSON + // serialization specification. Note that we write the data backwards, this is to avoid + // overwriting previously written data with zeros. Offsets are computed to account for + // both the leading 32-byte length and leading zeros from the constants. + mstore(add(clientDataJson, encodedLength), 0x7d) // } + mstore(add(clientDataJson, 81), 0x222c) // ", + mstore(add(clientDataJson, 36), 0x2c226368616c6c656e6765223a22) // ,"challenge":" + mstore(add(clientDataJson, 22), 0x7b2274797065223a22776562617574686e2e67657422) // {"type":"webauthn.get" + mstore(clientDataJson, encodedLength) + + // Copy the client data fields from calldata to their reserved space in memory. + calldatacopy(add(clientDataJson, 113), clientDataFields.offset, clientDataFields.length) + + // Store the base-64 URL character lookup table into the scratch and free memory pointer + // space in memory [^1]. The table is split into two 32-byte parts and stored in memory + // from address 0x1f to 0x5e. Note that the offset is chosen in such a way that the + // least significant byte of `mload(x)` is the base-64 ASCII character for the 6-bit + // value `x`. We will write the free memory pointer at address `0x40` before leaving the + // assembly block accounting for the allocation of `clientDataJson`. + // + // - [^1](https://docs.soliditylang.org/en/stable/internals/layout_in_memory.html). + mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef") + mstore(0x3f, "ghijklmnopqrstuvwxyz0123456789-_") + + // Initialize a pointer for writing the base-64 encoded challenge. + let ptr := add(clientDataJson, 68) + + // Base-64 encode the challenge to its reserved space in memory. + // + // To minimize stack and jump operations, we partially unroll the loop. With full 6 + // iterations of the loop, we need to encode seven 6-bit groups and one 4-bit group. In + // total, it encodes 6 iterations * 7 groups * 6 bits = 252 bits. The remaining 4-bit + // group is encoded after the loop. `i` is initialized to 250, which is the number of + // bits by which we need to shift the data to get the first 6-bit group, and then we + // subtract 6 to get the next 6-bit group. + // + // We want to exit when all full 6 bits groups are encoded. After 6 iterations, `i` will + // be -2 and the **signed** comparison with 0 will break the loop. + for { + let i := 250 + } sgt(i, 0) { + // Advance the pointer by the number of bytes written (7 bytes in this case). + ptr := add(ptr, 7) + // Move `i` by 42 = 6 bits * 7 (groups processed in each iteration). + i := sub(i, 42) + } { + // Encode 6-bit groups into characters by looking them up in the character table. + // 0x3f is a mask to get the last 6 bits so that we can index directly to the + // base-64 lookup table. + mstore8(ptr, mload(and(shr(i, challenge), 0x3f))) + mstore8(add(ptr, 1), mload(and(shr(sub(i, 6), challenge), 0x3f))) + mstore8(add(ptr, 2), mload(and(shr(sub(i, 12), challenge), 0x3f))) + mstore8(add(ptr, 3), mload(and(shr(sub(i, 18), challenge), 0x3f))) + mstore8(add(ptr, 4), mload(and(shr(sub(i, 24), challenge), 0x3f))) + mstore8(add(ptr, 5), mload(and(shr(sub(i, 30), challenge), 0x3f))) + mstore8(add(ptr, 6), mload(and(shr(sub(i, 36), challenge), 0x3f))) + } + + // Encode the final 4-bit group, where 0x0f is a mask to get the last 4 bits. + mstore8(ptr, mload(shl(2, and(challenge, 0x0f)))) + + // Update the free memory pointer to point to the end of the encoded string. + // Store the length of the encoded string at the beginning of `result`. + mstore(0x40, and(add(clientDataJson, add(encodedLength, 0x3f)), not(0x1f))) + } + } + + /** + * @notice Encodes the message that is signed in a WebAuthn assertion. + * @dev The signing message is defined to be the concatenation of the authenticator data bytes + * with the 32-byte SHA-256 digest of the client data JSON. The hashing algorithm used on the + * signing message itself depends on the public key algorithm that was selected on WebAuthn + * credential creation. + * @param challenge The WebAuthn challenge used for the credential assertion. + * @param authenticatorData Authenticator data. + * @param clientDataFields Client data fields. + * @return message Signing message bytes. + */ + function encodeSigningMessage( + bytes32 challenge, + bytes calldata authenticatorData, + string calldata clientDataFields + ) internal view returns (bytes memory message) { + string memory clientDataJson = encodeClientDataJson(challenge, clientDataFields); + bytes32 clientDataHash = _sha256(bytes(clientDataJson)); + + // solhint-disable-next-line no-inline-assembly + assembly ("memory-safe") { + // The length of the signing message, this is the length of the authenticator data plus + // the 32-byte hash of the client data JSON. + let messageLength := add(authenticatorData.length, 32) + + // Allocate the encoded signing `message` at the start of the free memory. Note that we + // pad the allocation to 32-byte boundary as Solidity typically does. + message := mload(0x40) + mstore(0x40, and(add(message, add(messageLength, 0x3f)), not(0x1f))) + mstore(message, messageLength) + + // The actual message data is written to 32 bytes past the start of the allocation, as + // the first 32 bytes contains the length of the byte array. + let messagePtr := add(message, 32) + + // Copy the authenticator from calldata to the start of the `message` buffer that was + // allocated. Note that we start copying 32 bytes after the start of the allocation to + // account for the length. + calldatacopy(messagePtr, authenticatorData.offset, authenticatorData.length) + + // Finally, write the client data JSON hash to the end of the `message`. + mstore(add(messagePtr, authenticatorData.length), clientDataHash) + } + } + + /** + * @notice Checks that the required authenticator data flags are set. + * @param authenticatorData The authenticator data. + * @param authenticatorFlags The authenticator flags to check for. + * @return success Whether the authenticator data flags are set. + */ + function checkAuthenticatorFlags( + bytes calldata authenticatorData, + AuthenticatorFlags authenticatorFlags + ) internal pure returns (bool success) { + success = authenticatorData[32] & AuthenticatorFlags.unwrap(authenticatorFlags) == AuthenticatorFlags.unwrap(authenticatorFlags); + } + + /** + * @notice Verifies a WebAuthn signature. + * @param challenge The WebAuthn challenge used in the credential assertion. + * @param signature The encoded WebAuthn signature bytes. + * @param authenticatorFlags The authenticator data flags that must be set. + * @param x The x-coordinate of the credential's public key. + * @param y The y-coordinate of the credential's public key. + * @param verifiers The P-256 verifier configuration to use. + * @return success Whether the signature is valid. + */ + function verifySignature( + bytes32 challenge, + bytes calldata signature, + AuthenticatorFlags authenticatorFlags, + uint256 x, + uint256 y, + P256.Verifiers verifiers + ) internal view returns (bool success) { + Signature calldata signatureStruct; + (success, signatureStruct) = castSignature(signature); + if (success) { + success = verifySignature(challenge, signatureStruct, authenticatorFlags, x, y, verifiers); + } + } + + /** + * @notice Verifies a WebAuthn signature. + * @param challenge The WebAuthn challenge used in the credential assertion. + * @param signature The WebAuthn signature data. + * @param authenticatorFlags The authenticator data flags that must be set. + * @param x The x-coordinate of the credential's public key. + * @param y The y-coordinate of the credential's public key. + * @param verifiers The P-256 verifier configuration to use. + * @return success Whether the signature is valid. + */ + function verifySignature( + bytes32 challenge, + Signature calldata signature, + AuthenticatorFlags authenticatorFlags, + uint256 x, + uint256 y, + P256.Verifiers verifiers + ) internal view returns (bool success) { + // The order of operations here is slightly counter-intuitive (in particular, you do not + // need to encode the signing message if the expected authenticator flags are missing). + // However, ordering things this way helps the Solidity compiler generate meaningfully more + // optimal code for the "happy path" when Yul optimizations are turned on. + bytes memory message = encodeSigningMessage(challenge, signature.authenticatorData, signature.clientDataFields); + if (checkAuthenticatorFlags(signature.authenticatorData, authenticatorFlags)) { + success = verifiers.verifySignatureAllowMalleability(_sha256(message), signature.r, signature.s, x, y); + } + } + + /** + * @notice Compute the SHA-256 hash of the input bytes. + * @dev The Solidity compiler sometimes generates a memory copy loop for the call to the SHA-256 + * precompile, even if the input is already in memory. Force this not to happen by manually + * implementing the call to the SHA-256 precompile. + * @param input The input bytes to hash. + * @return digest The SHA-256 digest of the input bytes. + */ + function _sha256(bytes memory input) public view returns (bytes32 digest) { + // solhint-disable-next-line no-inline-assembly + assembly ("memory-safe") { + // The SHA-256 precompile is at address 0x0002. Note that we don't check the whether or + // not the precompile reverted or if the return data size is 32 bytes, which is a + // reasonable assumption for the precompile, as it is specified to always return the + // SHA-256 of its input bytes. + pop(staticcall(gas(), 0x0002, add(input, 0x20), mload(input), 0, 32)) + digest := mload(0) + } + } +} diff --git a/certora/specs/ERC1967/erc1967.spec b/certora/specs/ERC1967/erc1967.spec deleted file mode 100644 index 8c96499cc..000000000 --- a/certora/specs/ERC1967/erc1967.spec +++ /dev/null @@ -1,7 +0,0 @@ -methods { - // avoids linking messages upon upgradeToAndCall - function _._upgradeToAndCall(address,bytes memory,bool) internal => NONDET; - function _._upgradeToAndCallUUPS(address,bytes memory,bool) internal => NONDET; - // view function - function _.proxiableUUID() external => NONDET; // expect bytes32 -} \ No newline at end of file diff --git a/certora/specs/ERC20/WETHcvl.spec b/certora/specs/ERC20/WETHcvl.spec deleted file mode 100644 index 8b1d8b70e..000000000 --- a/certora/specs/ERC20/WETHcvl.spec +++ /dev/null @@ -1,35 +0,0 @@ -using DummyWeth as weth; // we are limited by the fact that we cannot do transfers from CVL -using Utilities as utils; - -methods { - // Utilities - function Utilities.justRevert() external envfree; - - // WETH - function _.deposit() external with (env e) => wethDeposit(calledContract, e.msg.sender, e.msg.value) expect void; - function _.withdraw(uint256 amount) external with (env e) => wethWithdraw(calledContract, e.msg.sender, amount) expect void; -} - -function wethDeposit(address target, address caller, uint256 value) { - // should be reverting if target != weth. Instead, we will use a contract to revert - if (target != weth) { - utils.justRevert(); // check this works xxx - } else { - // money will be transferred because of the payability of deposit - env e2; - require e2.msg.sender == caller; - require e2.msg.value == value; - weth.deposit(e2); - } -} - -function wethWithdraw(address target, address caller, uint256 amount) { - // should be reverting if target != weth. Instead, we will use a contract to revert - if (target != weth) { - utils.justRevert(); // check this works xxx - } else { - env e2; - require e2.msg.sender == caller; - weth.withdraw(e2, amount); - } -} \ No newline at end of file diff --git a/certora/specs/ERC20/erc20cvl.spec b/certora/specs/ERC20/erc20cvl.spec deleted file mode 100644 index cacf0ce3c..000000000 --- a/certora/specs/ERC20/erc20cvl.spec +++ /dev/null @@ -1,56 +0,0 @@ -methods { - // ERC20 standard - function _.name() external => NONDET; // can we use PER_CALLEE_CONSTANT? - function _.symbol() external => NONDET; // can we use PER_CALLEE_CONSTANT? - function _.decimals() external => PER_CALLEE_CONSTANT; - function _.totalSupply() external => totalSupplyByToken[calledContract] expect uint256; - function _.balanceOf(address a) external => balanceByToken[calledContract][a] expect uint256; - function _.allowance(address a, address b) external => allowanceByToken[calledContract][a][b] expect uint256; - function _.approve(address a, uint256 x) external with (env e) => approveCVL(calledContract, e.msg.sender, a, x) expect bool; - function _.transfer(address a, uint256 x) external with (env e) => transferCVL(calledContract, e.msg.sender, a, x) expect bool; - function _.transferFrom(address a, address b, uint256 x) external with (env e) => transferFromCVL(calledContract, e.msg.sender, a, b, x) expect bool; - -} - - -/// CVL simple implementations of IERC20: -/// token => totalSupply -ghost mapping(address => uint256) totalSupplyByToken; -/// token => account => balance -ghost mapping(address => mapping(address => uint256)) balanceByToken; -/// token => owner => spender => allowance -ghost mapping(address => mapping(address => mapping(address => uint256))) allowanceByToken; - -// function tokenBalanceOf(address token, address account) returns uint256 { -// return balanceByToken[token][account]; -// } - -function approveCVL(address token, address approver, address spender, uint256 amount) returns bool { - // should be randomly reverting xxx - bool nondetSuccess; - if (!nondetSuccess) return false; - - allowanceByToken[token][approver][spender] = amount; - return true; -} - -function transferFromCVL(address token, address spender, address from, address to, uint256 amount) returns bool { - // should be randomly reverting xxx - bool nondetSuccess; - if (!nondetSuccess) return false; - - if (allowanceByToken[token][from][spender] < amount) return false; - allowanceByToken[token][from][spender] = assert_uint256(allowanceByToken[token][from][spender] - amount); - return transferCVL(token, from, to, amount); -} - -function transferCVL(address token, address from, address to, uint256 amount) returns bool { - // should be randomly reverting xxx - bool nondetSuccess; - if (!nondetSuccess) return false; - - if(balanceByToken[token][from] < amount) return false; - balanceByToken[token][from] = assert_uint256(balanceByToken[token][from] - amount); - balanceByToken[token][to] = require_uint256(balanceByToken[token][to] + amount); // We neglect overflows. - return true; -} \ No newline at end of file diff --git a/certora/specs/ERC20/erc20dispatched.spec b/certora/specs/ERC20/erc20dispatched.spec deleted file mode 100644 index c40394bbd..000000000 --- a/certora/specs/ERC20/erc20dispatched.spec +++ /dev/null @@ -1,16 +0,0 @@ -methods { - // ERC20 standard - function _.name() external => DISPATCHER(true); - function _.symbol() external => DISPATCHER(true); - function _.decimals() external => DISPATCHER(true); - function _.totalSupply() external => DISPATCHER(true); - function _.balanceOf(address) external => DISPATCHER(true); - function _.allowance(address,address) external => DISPATCHER(true); - function _.approve(address,uint256) external => DISPATCHER(true); - function _.transfer(address,uint256) external => DISPATCHER(true); - function _.transferFrom(address,address,uint256) external => DISPATCHER(true); - - // WETH - function _.deposit() external => DISPATCHER(true); - function _.withdraw(uint256) external => DISPATCHER(true); -} diff --git a/certora/specs/ERC721/erc721.spec b/certora/specs/ERC721/erc721.spec deleted file mode 100644 index 474b69f3a..000000000 --- a/certora/specs/ERC721/erc721.spec +++ /dev/null @@ -1,9 +0,0 @@ -methods { - // likely unsound, but assumes no callback - function _.onERC721Received( - address operator, - address from, - uint256 tokenId, - bytes data - ) external => NONDET; /* expects bytes4 */ -} \ No newline at end of file diff --git a/certora/specs/PriceAggregators/chainlink.spec b/certora/specs/PriceAggregators/chainlink.spec deleted file mode 100644 index 889e39e6f..000000000 --- a/certora/specs/PriceAggregators/chainlink.spec +++ /dev/null @@ -1,4 +0,0 @@ -methods { - function _.getRoundData(uint80) external => NONDET; - function _.latestRoundData() external => NONDET; -} \ No newline at end of file diff --git a/certora/specs/PriceAggregators/tellor.spec b/certora/specs/PriceAggregators/tellor.spec deleted file mode 100644 index 03f90c779..000000000 --- a/certora/specs/PriceAggregators/tellor.spec +++ /dev/null @@ -1,3 +0,0 @@ -methods { - function _.getTellorCurrentValue(uint256) external => NONDET; -} \ No newline at end of file diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index 3ae19f23a..0041687a0 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -182,14 +182,14 @@ rule createAndVerifyEQtoIsValidSignatureForSigner() └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ -rule isValidSignatureForSignerConsistency() -{ +rule isValidSignatureForSignerConsistency(method f) filtered { + f -> f.selector != sig:WebAuthnHarness.encodeClientDataJson(bytes32,string).selector +} { env e; env e1; env e2; require e1.msg.value == 0 && e2.msg.value == 0; - - method f; + calldataarg args; uint x; diff --git a/certora/specs/SafeWebAuthnSignerSingleton.spec b/certora/specs/SafeWebAuthnSignerSingleton.spec index 753804415..43f941d80 100644 --- a/certora/specs/SafeWebAuthnSignerSingleton.spec +++ b/certora/specs/SafeWebAuthnSignerSingleton.spec @@ -112,11 +112,12 @@ rule verifyIsValidSignatureAreEqual(env e){ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ -rule verifyIsValidSignatureWillContinueToSucceed(){ +rule verifyIsValidSignatureWillContinueToSucceed(method f) filtered { + f -> f.selector != sig:WebAuthnHarness.encodeClientDataJson(bytes32,string).selector +} { env e; require e.msg.value == 0; - method f; calldataarg args; bytes32 message; diff --git a/certora/specs/setup/builtin_assertions.spec b/certora/specs/setup/builtin_assertions.spec deleted file mode 100644 index 477d9680b..000000000 --- a/certora/specs/setup/builtin_assertions.spec +++ /dev/null @@ -1,12 +0,0 @@ -import "../problems.spec"; -import "../unresolved.spec"; -import "../optimizations.spec"; - -rule check_builtin_assertions(method f) - filtered { f -> f.contract == currentContract } -{ - env e; - calldataarg arg; - f(e, arg); - assert true; -} diff --git a/certora/specs/setup/sanity.spec b/certora/specs/setup/sanity.spec deleted file mode 100644 index f5b8619a7..000000000 --- a/certora/specs/setup/sanity.spec +++ /dev/null @@ -1,5 +0,0 @@ -import "../problems.spec"; -import "../unresolved.spec"; -import "../optimizations.spec"; - -use builtin rule sanity filtered { f -> f.contract == currentContract } \ No newline at end of file diff --git a/certora/specs/setup/sanity_with_all_default_summaries.spec b/certora/specs/setup/sanity_with_all_default_summaries.spec deleted file mode 100644 index 96b994078..000000000 --- a/certora/specs/setup/sanity_with_all_default_summaries.spec +++ /dev/null @@ -1,14 +0,0 @@ -import "../ERC20/erc20cvl.spec"; -import "../ERC20/WETHcvl.spec"; -import "../ERC721/erc721.spec"; -import "../ERC1967/erc1967.spec"; -import "../PriceAggregators/chainlink.spec"; -import "../PriceAggregators/tellor.spec"; - -import "../problems.spec"; -import "../unresolved.spec"; -import "../optimizations.spec"; - -import "../generic.spec"; // pick additional rules from here - -use builtin rule sanity filtered { f -> f.contract == currentContract } \ No newline at end of file diff --git a/certora/specs/setup/sanity_with_erc20cvl.spec b/certora/specs/setup/sanity_with_erc20cvl.spec deleted file mode 100644 index 1a932499c..000000000 --- a/certora/specs/setup/sanity_with_erc20cvl.spec +++ /dev/null @@ -1,8 +0,0 @@ -import "../ERC20/erc20cvl.spec"; -import "../ERC20/WETHcvl.spec"; - -import "../problems.spec"; -import "../unresolved.spec"; -import "../optimizations.spec"; - -use builtin rule sanity filtered { f -> f.contract == currentContract } \ No newline at end of file diff --git a/certora/specs/setup/sanity_with_erc20dispatched.spec b/certora/specs/setup/sanity_with_erc20dispatched.spec deleted file mode 100644 index 75340d9af..000000000 --- a/certora/specs/setup/sanity_with_erc20dispatched.spec +++ /dev/null @@ -1,7 +0,0 @@ -import "../ERC20/erc20dispatched.spec"; - -import "../problems.spec"; -import "../unresolved.spec"; -import "../optimizations.spec"; - -use builtin rule sanity filtered { f -> f.contract == currentContract } \ No newline at end of file diff --git a/certora/specs/spec_utils/AllowanceModule.spec b/certora/specs/spec_utils/AllowanceModule.spec deleted file mode 100644 index d0c84dd8c..000000000 --- a/certora/specs/spec_utils/AllowanceModule.spec +++ /dev/null @@ -1,32 +0,0 @@ -import "ERC20/erc20cvl.spec"; -import "ERC20/WETHcvl.spec"; -import "ERC721/erc721.spec"; -import "ERC1967/erc1967.spec"; -import "PriceAggregators/chainlink.spec"; -import "PriceAggregators/tellor.spec"; - -import "problems.spec"; -import "unresolved.spec"; -import "optimizations.spec"; - -import "generic.spec"; // pick additional rules from here - -methods { - function _.execTransactionFromModule( - address to, - uint256 value, - bytes data, - Enum.Operation operation - ) external => HAVOC_ECF; -} - -use builtin rule sanity filtered { f -> f.contract == currentContract } - -use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } -use builtin rule msgValueInLoopRule; -use builtin rule viewReentrancy; -use rule privilegedOperation filtered { f -> f.contract == currentContract } -use rule timeoutChecker filtered { f -> f.contract == currentContract } -use rule simpleFrontRunning filtered { f -> f.contract == currentContract } -use rule noRevert filtered { f -> f.contract == currentContract } -use rule alwaysRevert filtered { f -> f.contract == currentContract } \ No newline at end of file diff --git a/certora/specs/spec_utils/Safe4337Module.spec b/certora/specs/spec_utils/Safe4337Module.spec deleted file mode 100644 index 0e41df273..000000000 --- a/certora/specs/spec_utils/Safe4337Module.spec +++ /dev/null @@ -1,33 +0,0 @@ -import "ERC20/erc20cvl.spec"; -import "ERC20/WETHcvl.spec"; -import "ERC721/erc721.spec"; -import "ERC1967/erc1967.spec"; -import "PriceAggregators/chainlink.spec"; -import "PriceAggregators/tellor.spec"; - -import "problems.spec"; -import "unresolved.spec"; -import "optimizations.spec"; - -import "generic.spec"; // pick additional rules from here - -methods { - function _.isValidSignature(bytes data, bytes signatureData) external => DISPATCHER(true); - function _.execTransactionFromModuleReturnData(address to, uint256 value, bytes data, uint8 operation) external => HAVOC_ECF; - function _.execTransactionFromModule(address to, uint256 value, bytes data, uint8 operation) external => HAVOC_ECF; - function _.checkSignatures(bytes32 dataHash, bytes data, bytes signatures) external => DISPATCHER(true); - function _.domainSeparator() external => DISPATCHER(true); - function _.getModulesPaginated(address start, uint256 pageSize) external => HAVOC_ECF; -} - - -use builtin rule sanity filtered { f -> f.contract == currentContract } - -use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } -use builtin rule msgValueInLoopRule; -use builtin rule viewReentrancy; -use rule privilegedOperation filtered { f -> f.contract == currentContract } -use rule timeoutChecker filtered { f -> f.contract == currentContract } -use rule simpleFrontRunning filtered { f -> f.contract == currentContract } -use rule noRevert filtered { f -> f.contract == currentContract } -use rule alwaysRevert filtered { f -> f.contract == currentContract } \ No newline at end of file diff --git a/certora/specs/spec_utils/SafeModuleSetup.spec b/certora/specs/spec_utils/SafeModuleSetup.spec deleted file mode 100644 index a2925b0ec..000000000 --- a/certora/specs/spec_utils/SafeModuleSetup.spec +++ /dev/null @@ -1,27 +0,0 @@ -import "ERC20/erc20cvl.spec"; -import "ERC20/WETHcvl.spec"; -import "ERC721/erc721.spec"; -import "ERC1967/erc1967.spec"; -import "PriceAggregators/chainlink.spec"; -import "PriceAggregators/tellor.spec"; - -import "problems.spec"; -import "unresolved.spec"; -import "optimizations.spec"; - -import "generic.spec"; // pick additional rules from here - -methods { - function _.enableModule(address module) external => DISPATCHER(true); -} - -use builtin rule sanity filtered { f -> f.contract == currentContract } - -use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } -use builtin rule msgValueInLoopRule; -use builtin rule viewReentrancy; -use rule privilegedOperation filtered { f -> f.contract == currentContract } -use rule timeoutChecker filtered { f -> f.contract == currentContract } -use rule simpleFrontRunning filtered { f -> f.contract == currentContract } -use rule noRevert filtered { f -> f.contract == currentContract } -use rule alwaysRevert filtered { f -> f.contract == currentContract } \ No newline at end of file diff --git a/certora/specs/spec_utils/SafeSignerLaunchpad.spec b/certora/specs/spec_utils/SafeSignerLaunchpad.spec deleted file mode 100644 index 9510f0338..000000000 --- a/certora/specs/spec_utils/SafeSignerLaunchpad.spec +++ /dev/null @@ -1,38 +0,0 @@ -import "ERC20/erc20cvl.spec"; -import "ERC20/WETHcvl.spec"; -import "ERC721/erc721.spec"; -import "ERC1967/erc1967.spec"; -import "PriceAggregators/chainlink.spec"; -import "PriceAggregators/tellor.spec"; - -import "problems.spec"; -import "unresolved.spec"; -import "optimizations.spec"; - -import "generic.spec"; // pick additional rules from here - -methods { - function _.createSigner(uint256 x, uint256 y, P256.Verifiers verifiers) external => HAVOC_ECF; - function _.isValidSignatureForSigner(bytes32 message, bytes signature, uint256 x, uint256 y, P256.Verifiers verifiers) external => HAVOC_ECF; - function _.setup( - address[] _owners, - uint256 _threshold, - address to, - bytes data, - address fallbackHandler, - address paymentToken, - uint256 payment, - address paymentReceiver - ) external => HAVOC_ECF; -} - -use builtin rule sanity filtered { f -> f.contract == currentContract } - -use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } -use builtin rule msgValueInLoopRule; -use builtin rule viewReentrancy; -use rule privilegedOperation filtered { f -> f.contract == currentContract } -use rule timeoutChecker filtered { f -> f.contract == currentContract } -use rule simpleFrontRunning filtered { f -> f.contract == currentContract } -use rule noRevert filtered { f -> f.contract == currentContract } -use rule alwaysRevert filtered { f -> f.contract == currentContract } \ No newline at end of file diff --git a/certora/specs/spec_utils/generic.spec b/certora/specs/spec_utils/generic.spec deleted file mode 100644 index b6234ffd3..000000000 --- a/certora/specs/spec_utils/generic.spec +++ /dev/null @@ -1,88 +0,0 @@ -/* -This rule find which functions are privileged. -A function is privileged if there is only one address that can call it. - -The rules finds this by finding which functions can be called by two different users. -*/ -rule privilegedOperation(method f, address privileged) { - env e1; - calldataarg arg; - require e1.msg.sender == privileged; - - storage initialStorage = lastStorage; - f@withrevert(e1, arg); // privileged succeeds executing candidate privileged operation. - bool firstSucceeded = !lastReverted; - - env e2; - calldataarg arg2; - require e2.msg.sender != privileged; - f@withrevert(e2, arg2) at initialStorage; // unprivileged - bool secondSucceeded = !lastReverted; - - assert !(firstSucceeded && secondSucceeded); -} - -rule timeoutChecker(method f) { - storage before = lastStorage; - env e; calldataarg arg; - f(e,arg); - assert before == lastStorage; -} - -/* -This rule find which functions that can be called, may fail due to someone else calling a function right before. - -This is n expensive rule - might fail on the demo site on big contracts -*/ -rule simpleFrontRunning(method f, address privileged) filtered { f-> !f.isView } { - env e1; - calldataarg arg; - require e1.msg.sender == privileged; - storage initialStorage = lastStorage; - f(e1, arg); - bool firstSucceeded = !lastReverted; - env e2; - calldataarg arg2; - require e2.msg.sender != e1.msg.sender; - f(e2, arg2) at initialStorage; - f@withrevert(e1, arg); - bool succeeded = !lastReverted; - assert succeeded; -} - -rule noRevert(method f) { - env e; - calldataarg arg; - require e.msg.value == 0; - f@withrevert(e, arg); - assert !lastReverted; -} - - -rule alwaysRevert(method f) { - env e; - calldataarg arg; - f@withrevert(e, arg); - assert lastReverted; -} - -use builtin rule sanity; -use builtin rule hasDelegateCalls; -use builtin rule msgValueInLoopRule; -use builtin rule viewReentrancy; - -/** - -// Integrate rules from generic.spec in importing specs like this: - -use builtin rule sanity filtered { f -> f.contract == currentContract } -use builtin rule hasDelegateCalls filtered { f -> f.contract == currentContract } -use builtin rule msgValueInLoopRule; -use builtin rule viewReentrancy; -use rule privilegedOperation filtered { f -> f.contract == currentContract } -use rule timeoutChecker filtered { f -> f.contract == currentContract } -use rule simpleFrontRunning filtered { f -> f.contract == currentContract } -use rule noRevert filtered { f -> f.contract == currentContract } -use rule alwaysRevert filtered { f -> f.contract == currentContract } - - **/ \ No newline at end of file diff --git a/certora/specs/spec_utils/optimizations.spec b/certora/specs/spec_utils/optimizations.spec deleted file mode 100644 index e92485dc9..000000000 --- a/certora/specs/spec_utils/optimizations.spec +++ /dev/null @@ -1,5 +0,0 @@ -// optimizing summaries -methods { - -} - diff --git a/certora/specs/spec_utils/problems.spec b/certora/specs/spec_utils/problems.spec deleted file mode 100644 index 3ebe9e371..000000000 --- a/certora/specs/spec_utils/problems.spec +++ /dev/null @@ -1 +0,0 @@ -// workarounds for crashes \ No newline at end of file diff --git a/certora/specs/spec_utils/unresolved.spec b/certora/specs/spec_utils/unresolved.spec deleted file mode 100644 index 6bae1d482..000000000 --- a/certora/specs/spec_utils/unresolved.spec +++ /dev/null @@ -1,4 +0,0 @@ -// summaries for unresolved calls -methods { - -} \ No newline at end of file From 91fce2cf6cd0b061ec401eaafd837558d4f242b1 Mon Sep 17 00:00:00 2001 From: yoavelmalem Date: Tue, 27 Aug 2024 18:26:00 +0300 Subject: [PATCH 099/103] Add munge --- certora/confs/VerifyEQtoIsValidSignatureForSigner.conf | 2 +- certora/munged/SafeWebAuthnSignerSingleton.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/certora/confs/VerifyEQtoIsValidSignatureForSigner.conf b/certora/confs/VerifyEQtoIsValidSignatureForSigner.conf index 6b3832095..3c3c38a29 100644 --- a/certora/confs/VerifyEQtoIsValidSignatureForSigner.conf +++ b/certora/confs/VerifyEQtoIsValidSignatureForSigner.conf @@ -3,7 +3,7 @@ "dynamic_bound": "1", "files": [ "certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol", - "certora/munged/SafeWebAuthnSignerSingleton.sol", + "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", "certora/munged/SafeWebAuthnSignerProxy.sol", "certora/munged/WebAuthn.sol", "certora/harnesses/WebAuthnHarness.sol" diff --git a/certora/munged/SafeWebAuthnSignerSingleton.sol b/certora/munged/SafeWebAuthnSignerSingleton.sol index a16238b03..4922d0a4a 100644 --- a/certora/munged/SafeWebAuthnSignerSingleton.sol +++ b/certora/munged/SafeWebAuthnSignerSingleton.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.0; import {SignatureValidator} from "../../modules/passkey/contracts/base/SignatureValidator.sol"; -import {P256, WebAuthn} from "./WebAuthn.sol"; +import {P256, WebAuthn} from "../../modules/passkey/contracts/libraries/WebAuthn.sol"; /** * @title Safe WebAuthn Signer Singleton From 78e74a12009f5e405e1017f8c98e988efad97642 Mon Sep 17 00:00:00 2001 From: yoavelmalem Date: Tue, 27 Aug 2024 18:43:17 +0300 Subject: [PATCH 100/103] Add munge --- certora/confs/SafeWebAuthnSignerFactory.conf | 4 +- .../VerifyEQtoIsValidSignatureForSigner.conf | 4 +- certora/harnesses/WebAuthnHarness.sol | 2 +- .../harnesses/WebAuthnHarnessWithMunge.sol | 136 ++++++++++ .../SafeWebAuthnSignerFactoryWithMunge.spec | 248 ++++++++++++++++++ 5 files changed, 389 insertions(+), 5 deletions(-) create mode 100644 certora/harnesses/WebAuthnHarnessWithMunge.sol create mode 100644 certora/specs/SafeWebAuthnSignerFactoryWithMunge.spec diff --git a/certora/confs/SafeWebAuthnSignerFactory.conf b/certora/confs/SafeWebAuthnSignerFactory.conf index e8a7fd674..33490caf3 100644 --- a/certora/confs/SafeWebAuthnSignerFactory.conf +++ b/certora/confs/SafeWebAuthnSignerFactory.conf @@ -2,7 +2,7 @@ "assert_autofinder_success": true, "files": [ "certora/harnesses/FactoryHarnessForSignerConsistency.sol", - "certora/harnesses/WebAuthnHarness.sol", + "certora/harnesses/WebAuthnHarnessWithMunge.sol", "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol" ], @@ -26,5 +26,5 @@ "process": "emv", "rule_sanity": "basic", "solc": "solc8.23", - "verify": "FactoryHarnessForSignerConsistency:certora/specs/SafeWebAuthnSignerFactory.spec" + "verify": "FactoryHarnessForSignerConsistency:certora/specs/SafeWebAuthnSignerFactoryWithMunge.spec" } \ No newline at end of file diff --git a/certora/confs/VerifyEQtoIsValidSignatureForSigner.conf b/certora/confs/VerifyEQtoIsValidSignatureForSigner.conf index 3c3c38a29..d0700ebb6 100644 --- a/certora/confs/VerifyEQtoIsValidSignatureForSigner.conf +++ b/certora/confs/VerifyEQtoIsValidSignatureForSigner.conf @@ -6,7 +6,7 @@ "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", "certora/munged/SafeWebAuthnSignerProxy.sol", "certora/munged/WebAuthn.sol", - "certora/harnesses/WebAuthnHarness.sol" + "certora/harnesses/WebAuthnHarnessWithMunge.sol" ], "hashing_length_bound": "906", "link": [ @@ -29,5 +29,5 @@ "rule_sanity": "basic", "smt_timeout": "600", "solc": "solc8.23", - "verify": "SafeWebAuthnSignerFactoryHarness:certora/specs/SafeWebAuthnSignerFactory.spec" + "verify": "SafeWebAuthnSignerFactoryHarness:certora/specs/SafeWebAuthnSignerFactoryWithMunge.spec" } \ No newline at end of file diff --git a/certora/harnesses/WebAuthnHarness.sol b/certora/harnesses/WebAuthnHarness.sol index 68eda2c56..da7509a54 100644 --- a/certora/harnesses/WebAuthnHarness.sol +++ b/certora/harnesses/WebAuthnHarness.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.0; -import {P256, WebAuthn} from "../munged/WebAuthn.sol"; +import {P256, WebAuthn} from "../../modules/passkey/contracts/libraries/WebAuthn.sol"; contract WebAuthnHarness { diff --git a/certora/harnesses/WebAuthnHarnessWithMunge.sol b/certora/harnesses/WebAuthnHarnessWithMunge.sol new file mode 100644 index 000000000..e2439f794 --- /dev/null +++ b/certora/harnesses/WebAuthnHarnessWithMunge.sol @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.0; + +import {P256, WebAuthn} from "../munged/WebAuthn.sol"; + +contract WebAuthnHarnessWithMunge { + + mapping (bytes32 => mapping (bytes32 => string)) symbolicClientDataJson; + + function summaryEncodeDataJson(bytes32 challenge, string calldata clientDataFields) public returns (string memory){ + bytes32 hashClientDataFields = keccak256(abi.encodePacked(clientDataFields)); + string memory stringResult = symbolicClientDataJson[challenge][hashClientDataFields]; + bytes32 hashResult = keccak256(abi.encodePacked(stringResult)); + + require (checkInjective(challenge, hashClientDataFields, hashResult)); + + return stringResult; + } + + function checkInjective(bytes32 challenge, bytes32 clientDataFields, bytes32 result) internal view returns (bool){ + return true; + } + + function compareSignatures(WebAuthn.Signature memory sig1, WebAuthn.Signature memory sig2) public pure returns (bool) { + if (sig1.r != sig2.r || sig1.s != sig2.s) { + return false; + } + + if (keccak256(abi.encodePacked(sig1.clientDataFields)) != keccak256(abi.encodePacked(sig2.clientDataFields))) { + return false; + } + + if (keccak256(sig1.authenticatorData) != keccak256(sig2.authenticatorData)) { + return false; + } + + return true; + } + + function encodeSignature(WebAuthn.Signature calldata sig) external pure returns (bytes memory signature){ + signature = abi.encode(sig.authenticatorData, sig.clientDataFields, sig.r, sig.s); + } + + function castSignature(bytes calldata signature) external pure returns (bool isValid, WebAuthn.Signature calldata data){ + return WebAuthn.castSignature(signature); + } + + function castSignatureSuccess(bytes32 unused, bytes calldata signature) external pure returns (bool) { + (bool isValid, ) = WebAuthn.castSignature(signature); + return isValid; + } + + function castSignature_notreturns(bytes calldata signature) external pure { + WebAuthn.castSignature(signature); + } + + function compareStrings(string memory str1, string memory str2) public view returns (bool) { + bytes memory str1Bytes = bytes(str1); + bytes memory str2Bytes = bytes(str2); + return getSha256(str1Bytes) == getSha256(str2Bytes); + } + + function encodeClientDataJson( + bytes32 challenge, + string calldata clientDataFields + ) public pure returns (string memory clientDataJson){ + return WebAuthn.encodeClientDataJson(challenge, clientDataFields); + } + + function encodeSigningMessage( + bytes32 challenge, + bytes calldata authenticatorData, + string calldata clientDataFields + ) public view returns (bytes memory message) { + return WebAuthn.encodeSigningMessage(challenge, authenticatorData, clientDataFields); + } + + function checkAuthenticatorFlags( + bytes calldata authenticatorData, + WebAuthn.AuthenticatorFlags authenticatorFlags + ) public pure returns (bool success){ + return WebAuthn.checkAuthenticatorFlags(authenticatorData, authenticatorFlags); + } + + function prepareSignature(bytes calldata authenticatorData, + string calldata clientDataFields, + uint256 r, + uint256 s) public pure returns(bytes memory signature, WebAuthn.Signature memory structSignature){ + signature = abi.encode(authenticatorData, clientDataFields, r, s); + structSignature = WebAuthn.Signature(authenticatorData, clientDataFields, r, s); + } + + function verifySignature( + bytes32 challenge, + bytes calldata signature, + WebAuthn.AuthenticatorFlags authenticatorFlags, + uint256 x, + uint256 y, + P256.Verifiers verifiers + ) public view returns (bool success) { + return WebAuthn.verifySignature(challenge, signature, authenticatorFlags, x, y, verifiers); + } + + function verifySignature( + bytes32 challenge, + WebAuthn.Signature calldata signature, + WebAuthn.AuthenticatorFlags authenticatorFlags, + uint256 x, + uint256 y, + P256.Verifiers verifiers + ) public view returns (bool success) { + return WebAuthn.verifySignature(challenge, signature, authenticatorFlags, x, y, verifiers); + } + + function getSha256(bytes memory input) public view returns (bytes32 digest) { + return WebAuthn._sha256(input); + } + + mapping (bytes32 => mapping (bytes32 => mapping (bytes32 => bytes))) symbolicMessageSummary; + + function GETencodeSigningMessageSummary(bytes32 challenge, bytes calldata authenticatorData, string calldata clientDataFields) public returns (bytes memory){ + bytes32 hashed_authenticatorData = keccak256(authenticatorData); + bytes32 hashed_clientDataFields = keccak256(abi.encodePacked(clientDataFields)); + + bytes memory bytes_mapping = symbolicMessageSummary[challenge][hashed_authenticatorData][hashed_clientDataFields]; + + require (checkInjective(challenge, hashed_authenticatorData, hashed_clientDataFields, keccak256(bytes_mapping))); + + return bytes_mapping; + } + + function checkInjective(bytes32 challenge, bytes32 authenticatorData, bytes32 clientDataFields, bytes32 result) internal view returns (bool){ + return true; + } + +} diff --git a/certora/specs/SafeWebAuthnSignerFactoryWithMunge.spec b/certora/specs/SafeWebAuthnSignerFactoryWithMunge.spec new file mode 100644 index 000000000..8522d88e0 --- /dev/null +++ b/certora/specs/SafeWebAuthnSignerFactoryWithMunge.spec @@ -0,0 +1,248 @@ +using SafeWebAuthnSignerProxy as proxy; +using SafeWebAuthnSignerSingleton as singleton; +using WebAuthnHarnessWithMunge as WebAuthnHarness; + + +methods{ + function getSigner(uint256 x, uint256 y, P256.Verifiers v) internal returns (address) => getSignerGhost(x, y, v); + function createSigner(uint256, uint256, P256.Verifiers) external returns (address); + function hasNoCode(address) external returns (bool) envfree; + + function P256.verifySignatureAllowMalleability(P256.Verifiers a, bytes32 b, uint256 c, uint256 d, uint256 e, uint256 f) internal returns (bool) => + verifySignatureAllowMalleabilityGhost(a, b, c, d, e, f); + + function WebAuthn.encodeSigningMessage(bytes32 challenge, bytes calldata authenticatorData, string calldata clientDataFields) internal returns (bytes memory) => + GETencodeSigningMessageCVL(challenge, authenticatorData, clientDataFields); + + function WebAuthnHarness.checkInjective(bytes32 challenge, bytes32 authenticatorData, bytes32 clientDataFields, bytes32 result) internal returns (bool) => + checkInjectiveSummary(challenge, authenticatorData, clientDataFields, result); + function _.isValidSignature(bytes32,bytes) external => DISPATCHER(optimistic=true, use_fallback=true); + + function _._ external => DISPATCH [ + proxy._, + singleton._ + ] default NONDET; +} + +ghost mapping(bytes32 => mapping(bytes32 => mapping(bytes32 => bytes32))) componentToEncodeHash; +ghost mapping(bytes32 => bytes32) revChallenge; +ghost mapping(bytes32 => bytes32) revAuthenticator; +ghost mapping(bytes32 => bytes32) revClientData; + +function GETencodeSigningMessageCVL(bytes32 challenge, bytes authenticatorData, string clientDataFields) returns bytes { + bytes32 authHash = keccak256(authenticatorData); + bytes32 clientHash = keccak256(clientDataFields); + bytes32 toRetHash = componentToEncodeHash[challenge][authHash][clientHash]; + require(revChallenge[toRetHash] == challenge); + require(revAuthenticator[toRetHash] == authHash); + require(revClientData[toRetHash] == clientHash); + bytes toRet; + require keccak256(toRet) == toRetHash; + return toRet; +} + +ghost checkInjectiveSummary(bytes32, bytes32, bytes32, bytes32) returns bool { + axiom forall bytes32 x1. forall bytes32 y1. forall bytes32 z1. forall bytes32 x2. forall bytes32 y2. forall bytes32 z2. forall bytes32 result. + checkInjectiveSummary(x1, y1, z1, result) && checkInjectiveSummary(x2, y2, z2, result) => x1 == x2; +} + +ghost verifySignatureAllowMalleabilityGhost(P256.Verifiers, bytes32, uint256, uint256, uint256, uint256) returns bool { + axiom forall P256.Verifiers a. forall bytes32 message1. forall bytes32 message2. forall uint256 c. forall uint256 d. forall uint256 e. forall uint256 f. + verifySignatureAllowMalleabilityGhost(a, message1, c, d, e, f) && + verifySignatureAllowMalleabilityGhost(a, message2, c, d, e, f) => message1 == message2; +} + +// Summary is correct only if the unique signer rule is proved spec GetSigner +ghost getSignerGhost(uint256, uint256, P256.Verifiers) returns address { + axiom forall uint256 x1. forall uint256 y1. forall P256.Verifiers v1. + forall uint256 x2. forall uint256 y2. forall P256.Verifiers v2. + (getSignerGhost(x1, y1, v1) == getSignerGhost(x2, y2, v2)) <=> (x1 == x2 && y1 == y2 && v1 == v2); +} + +definition MAGIC_VALUE() returns bytes4 = to_bytes4(0x1626ba7e); + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Singleton implementation never change (Proved) │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ +rule singletonNeverChanges() +{ + env e; + method f; + calldataarg args; + address currentSingleton = currentContract.SINGLETON; + + f(e, args); + + assert currentSingleton == currentContract.SINGLETON; +} + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ createSigner and getSigner always returns the same address (Proved under assumption) │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ + +rule createAndGetSignerEquivalence(){ + env e; + + uint256 createX; + uint256 createY; + P256.Verifiers createVerifier; + + address signer1 = createSigner(e, createX, createY, createVerifier); + + uint256 getX; + uint256 getY; + P256.Verifiers getVerifier; + + address signer2 = getSigner(e, getX, getY, getVerifier); + + assert signer1 == signer2 <=> (createX == getX && createY == getY && createVerifier == getVerifier); +} + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Correctness of Signer Creation. (Cant called twice and override) (Bug CERT-6252) │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ + +ghost mathint numOfCreation; +ghost mapping(address => uint) address_map; +ghost address signerAddress; + +hook CREATE2(uint value, uint offset, uint length, bytes32 salt) address v{ + require(v == signerAddress); + numOfCreation = numOfCreation + 1; +} + +rule SignerCreationCantOverride() +{ + env e; + require numOfCreation == 0; + + uint x; + uint y; + P256.Verifiers verifier; + + address a = getSigner(e, x, y, verifier); + require address_map[a] == 0; + + createSigner(e, x, y, verifier); + createSigner@withrevert(e, x, y, verifier); + + assert numOfCreation < 2; +} + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Has no code integrity (Proved) │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ +rule hasNoCodeIntegrity() +{ + address a; + assert (a == proxy) => !hasNoCode(a); +} + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ isValidSignatureForSigner equiv to first deploying the signer with the factory, and then | +| verifying the signature with it directly (CERT-6221) │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ +rule createAndVerifyEQtoIsValidSignatureForSigner() +{ + env e; + uint x; + uint y; + P256.Verifiers verifier; + bytes signature; + bytes32 message; + + signerAddress = getSigner(e, x, y, verifier); + require(numOfCreation == 0); + require(hasNoCode(e, signerAddress)); + require(WebAuthnHarness.castSignatureSuccess(e, message, signature)); + + + storage s = lastStorage; + + bytes4 magic1 = isValidSignatureForSigner(e, message, signature, x, y, verifier); + + bytes4 magic2 = createAndVerify(e, message, signature, x, y, verifier) at s; + + assert magic1 == magic2 && numOfCreation == 1; +} + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ isValidSignatureForSigner Consistency (Proved) │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ + +rule isValidSignatureForSignerConsistency(method f) filtered { + f -> f.selector != sig:WebAuthnHarness.encodeClientDataJson(bytes32,string).selector +} { + env e; + env e1; + env e2; + require e1.msg.value == 0 && e2.msg.value == 0; + + calldataarg args; + + uint x; + uint y; + P256.Verifiers verifier; + + bytes signature; + bytes32 message; + + bytes4 magic1 = isValidSignatureForSigner@withrevert(e1, message, signature, x, y, verifier); + bool firstRevert = lastReverted; + + f(e, args); + + bytes4 magic2 = isValidSignatureForSigner@withrevert(e2, message, signature, x, y, verifier); + bool secondRevert = lastReverted; + + assert firstRevert == secondRevert; + assert (!firstRevert && !secondRevert) => (magic1 == MAGIC_VALUE()) <=> (magic2 == MAGIC_VALUE()); +} + + +/* +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ isValidSignatureForSigner Integrity (Violated) │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +*/ + +rule isValidSignatureForSignerIntegrity() +{ + env e; + + uint x; + uint y; + P256.Verifiers verifier; + bytes signature; + bytes32 message; + + bytes4 magic1 = isValidSignatureForSigner(e, message, signature, x, y, verifier); + + satisfy magic1 == MAGIC_VALUE(); +} + + +rule getSignerRevertingConditions { + env e; + uint256 x; + uint256 y; + P256.Verifiers verifiers; + + bool triedTransferringEth = e.msg.value != 0; + + getSigner@withrevert(e, x, y, verifiers); + + assert lastReverted <=> triedTransferringEth; +} From 7de765d9a734f8aaac01314d5132bfa3829cdea6 Mon Sep 17 00:00:00 2001 From: yoavelmalem Date: Wed, 28 Aug 2024 16:01:54 +0300 Subject: [PATCH 101/103] Add munge --- certora/confs/SafeWebAuthnSignerSingleton.conf | 3 +++ certora/munged/FactoryForSignerConsistency.sol | 3 +-- certora/munged/WebAuthn.sol | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/certora/confs/SafeWebAuthnSignerSingleton.conf b/certora/confs/SafeWebAuthnSignerSingleton.conf index 2c869e120..fba5044d0 100644 --- a/certora/confs/SafeWebAuthnSignerSingleton.conf +++ b/certora/confs/SafeWebAuthnSignerSingleton.conf @@ -7,6 +7,9 @@ "modules/passkey/contracts/libraries/P256.sol", "certora/harnesses/WebAuthnHarness.sol" ], + "link": [ + "SafeWebAuthnSignerFactory:SINGLETON=SafeWebAuthnSignerSingleton" + ], "loop_iter": "6", "optimistic_hashing": true, "optimistic_loop": true, diff --git a/certora/munged/FactoryForSignerConsistency.sol b/certora/munged/FactoryForSignerConsistency.sol index 83cfb8744..bc85d2cfe 100644 --- a/certora/munged/FactoryForSignerConsistency.sol +++ b/certora/munged/FactoryForSignerConsistency.sol @@ -62,7 +62,7 @@ contract SafeWebAuthnSignerFactory is ISafeSignerFactory { /** * @inheritdoc ISafeSignerFactory */ - function isValidSignatureForSigner( + function isValidSignatureForSigner( bytes32 message, bytes calldata signature, uint256 x, @@ -81,7 +81,6 @@ contract SafeWebAuthnSignerFactory is ISafeSignerFactory { assembly { // staticcall to the singleton contract with return size given as 32 bytes. The // singleton contract is known and immutable so it is safe to specify return size. - // MUNGED!! if staticcall(gas(), singleton, add(data, 0x20), mload(data), 0, 32) { magicValue := mload(0) } diff --git a/certora/munged/WebAuthn.sol b/certora/munged/WebAuthn.sol index cd09fb579..d88d0b548 100644 --- a/certora/munged/WebAuthn.sol +++ b/certora/munged/WebAuthn.sol @@ -80,6 +80,7 @@ library WebAuthn { function castSignature(bytes calldata signature) internal pure returns (bool isValid, Signature calldata data) { uint256 authenticatorDataLength; uint256 clientDataFieldsLength; + // MUNGED Initiating isValid for future usage isValid = true; // solhint-disable-next-line no-inline-assembly From 4616e1d1c1f33a7d48863af2e05ed8061c0f2d04 Mon Sep 17 00:00:00 2001 From: hristo Date: Wed, 11 Sep 2024 17:13:00 +0300 Subject: [PATCH 102/103] restore private to public munge of modules/passkey/contracts/libraries/WebAuthn.sol --- modules/passkey/contracts/libraries/WebAuthn.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/passkey/contracts/libraries/WebAuthn.sol b/modules/passkey/contracts/libraries/WebAuthn.sol index 6234dcdb1..a4b2d0044 100644 --- a/modules/passkey/contracts/libraries/WebAuthn.sol +++ b/modules/passkey/contracts/libraries/WebAuthn.sol @@ -336,7 +336,7 @@ library WebAuthn { * @param input The input bytes to hash. * @return digest The SHA-256 digest of the input bytes. */ - function _sha256(bytes memory input) public view returns (bytes32 digest) { + function _sha256(bytes memory input) private view returns (bytes32 digest) { // solhint-disable-next-line no-inline-assembly assembly ("memory-safe") { // The SHA-256 precompile is at address 0x0002. Note that we don't check the whether or From a1eab8a791deb09e595e60f3115f4b9d12020224 Mon Sep 17 00:00:00 2001 From: Hristo Grigorov Date: Thu, 12 Sep 2024 22:35:09 +0300 Subject: [PATCH 103/103] WebAuthnHarness to WebAuthnHarnessWithMunge fix --- certora/confs/ProxySimulator.conf | 3 +-- certora/confs/SafeWebAuthnSignerSingleton.conf | 3 +-- certora/confs/SignerCreationCantOverride.conf | 3 +-- .../confs/SingletonIsValidSignatureRevertingConditions.conf | 3 +-- certora/confs/ValidSignatureForSignerIntegrity.conf | 2 +- certora/confs/WebAuthn.conf | 4 ++-- certora/specs/ProxySimulator.spec | 2 +- certora/specs/SafeWebAuthnSignerFactory.spec | 2 +- certora/specs/SafeWebAuthnSignerSingleton.spec | 2 +- certora/specs/WebAuthn.spec | 4 ++-- 10 files changed, 12 insertions(+), 16 deletions(-) diff --git a/certora/confs/ProxySimulator.conf b/certora/confs/ProxySimulator.conf index f8edf10ca..c70bb3af2 100644 --- a/certora/confs/ProxySimulator.conf +++ b/certora/confs/ProxySimulator.conf @@ -5,8 +5,7 @@ "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol", "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", "modules/passkey/contracts/libraries/P256.sol", - "modules/passkey/contracts/libraries/WebAuthn.sol", - "certora/harnesses/WebAuthnHarness.sol" + "certora/harnesses/WebAuthnHarnessWithMunge.sol" ], "hashing_length_bound": "2048", "link": [ diff --git a/certora/confs/SafeWebAuthnSignerSingleton.conf b/certora/confs/SafeWebAuthnSignerSingleton.conf index fba5044d0..892ba4159 100644 --- a/certora/confs/SafeWebAuthnSignerSingleton.conf +++ b/certora/confs/SafeWebAuthnSignerSingleton.conf @@ -3,9 +3,8 @@ "files": [ "modules/passkey/contracts/SafeWebAuthnSignerFactory.sol", "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", - "modules/passkey/contracts/libraries/WebAuthn.sol", "modules/passkey/contracts/libraries/P256.sol", - "certora/harnesses/WebAuthnHarness.sol" + "certora/harnesses/WebAuthnHarnessWithMunge.sol" ], "link": [ "SafeWebAuthnSignerFactory:SINGLETON=SafeWebAuthnSignerSingleton" diff --git a/certora/confs/SignerCreationCantOverride.conf b/certora/confs/SignerCreationCantOverride.conf index 1478024f2..5ca4866b4 100644 --- a/certora/confs/SignerCreationCantOverride.conf +++ b/certora/confs/SignerCreationCantOverride.conf @@ -7,9 +7,8 @@ "certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol", "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol", - "modules/passkey/contracts/libraries/WebAuthn.sol", "modules/passkey/contracts/libraries/P256.sol", - "certora/harnesses/WebAuthnHarness.sol" + "certora/harnesses/WebAuthnHarnessWithMunge.sol" ], "hashing_length_bound": "912", "link": [ diff --git a/certora/confs/SingletonIsValidSignatureRevertingConditions.conf b/certora/confs/SingletonIsValidSignatureRevertingConditions.conf index 956527949..967f03bf2 100644 --- a/certora/confs/SingletonIsValidSignatureRevertingConditions.conf +++ b/certora/confs/SingletonIsValidSignatureRevertingConditions.conf @@ -3,9 +3,8 @@ "files": [ "modules/passkey/contracts/SafeWebAuthnSignerFactory.sol", "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", - "modules/passkey/contracts/libraries/WebAuthn.sol", "modules/passkey/contracts/libraries/P256.sol", - "certora/harnesses/WebAuthnHarness.sol" + "certora/harnesses/WebAuthnHarnessWithMunge.sol" ], "loop_iter": "6", "optimistic_hashing": true, diff --git a/certora/confs/ValidSignatureForSignerIntegrity.conf b/certora/confs/ValidSignatureForSignerIntegrity.conf index 57d2069f8..a1be3ce2c 100644 --- a/certora/confs/ValidSignatureForSignerIntegrity.conf +++ b/certora/confs/ValidSignatureForSignerIntegrity.conf @@ -2,7 +2,7 @@ "assert_autofinder_success": true, "files": [ "certora/harnesses/SafeWebAuthnSignerFactoryHarness.sol", - "certora/harnesses/WebAuthnHarness.sol", + "certora/harnesses/WebAuthnHarnessWithMunge.sol", "modules/passkey/contracts/SafeWebAuthnSignerSingleton.sol", "modules/passkey/contracts/SafeWebAuthnSignerProxy.sol" ], diff --git a/certora/confs/WebAuthn.conf b/certora/confs/WebAuthn.conf index 7a7faf3eb..1000b685f 100644 --- a/certora/confs/WebAuthn.conf +++ b/certora/confs/WebAuthn.conf @@ -1,7 +1,7 @@ { "assert_autofinder_success": true, "files": [ - "certora/harnesses/WebAuthnHarness.sol" + "certora/harnesses/WebAuthnHarnessWithMunge.sol" ], "loop_iter": "6", "optimistic_hashing": true, @@ -13,5 +13,5 @@ "process": "emv", "rule_sanity": "basic", "solc": "solc8.23", - "verify": "WebAuthnHarness:certora/specs/WebAuthn.spec" + "verify": "WebAuthnHarnessWithMunge:certora/specs/WebAuthn.spec" } \ No newline at end of file diff --git a/certora/specs/ProxySimulator.spec b/certora/specs/ProxySimulator.spec index 88d656364..e04d33f15 100644 --- a/certora/specs/ProxySimulator.spec +++ b/certora/specs/ProxySimulator.spec @@ -1,5 +1,5 @@ using SafeWebAuthnSignerProxy as SafeWebAuthnSignerProxy; -using WebAuthnHarness as WebAuthnHarness; +using WebAuthnHarnessWithMunge as WebAuthnHarness; methods { function P256.verifySignatureAllowMalleability(P256.Verifiers a, bytes32 b, uint256 c, uint256 d, uint256 e, uint256 f) internal returns (bool) => diff --git a/certora/specs/SafeWebAuthnSignerFactory.spec b/certora/specs/SafeWebAuthnSignerFactory.spec index 0041687a0..8522d88e0 100644 --- a/certora/specs/SafeWebAuthnSignerFactory.spec +++ b/certora/specs/SafeWebAuthnSignerFactory.spec @@ -1,6 +1,6 @@ using SafeWebAuthnSignerProxy as proxy; using SafeWebAuthnSignerSingleton as singleton; -using WebAuthnHarness as WebAuthnHarness; +using WebAuthnHarnessWithMunge as WebAuthnHarness; methods{ diff --git a/certora/specs/SafeWebAuthnSignerSingleton.spec b/certora/specs/SafeWebAuthnSignerSingleton.spec index 43f941d80..faa8e2e03 100644 --- a/certora/specs/SafeWebAuthnSignerSingleton.spec +++ b/certora/specs/SafeWebAuthnSignerSingleton.spec @@ -1,4 +1,4 @@ -using WebAuthnHarness as WebAuthnHarness; +using WebAuthnHarnessWithMunge as WebAuthnHarness; methods { function P256.verifySignatureAllowMalleability(P256.Verifiers a, bytes32 b, uint256 c, uint256 d, uint256 e, uint256 f) internal returns (bool) => diff --git a/certora/specs/WebAuthn.spec b/certora/specs/WebAuthn.spec index d45864dc3..ca1f5f5aa 100644 --- a/certora/specs/WebAuthn.spec +++ b/certora/specs/WebAuthn.spec @@ -198,7 +198,7 @@ rule castSignatureDeterministicDecoding(){ │ CastSignature Length Check Validity (Proved) | └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ */ - +/* note: the rule requires specific features still not available on a stable certora-cli rule castSignatureLengthCheckValidity(){ env e; @@ -213,7 +213,7 @@ rule castSignatureLengthCheckValidity(){ assert isValid <=> length_is_correct; } - +*/ /* ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ castSignature Reverting Conditions |