diff --git a/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol b/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol deleted file mode 100644 index 1576b2d3..00000000 --- a/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol +++ /dev/null @@ -1,112 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.24; - -import {IDKIMRegistry} from "@zk-email/contracts/DKIMRegistry.sol"; -import {IVerifier} from "@zk-email/email-tx-builder/interfaces/IVerifier.sol"; -import {EmailAuthMsg} from "@zk-email/email-tx-builder/interfaces/IEmailTypes.sol"; -import {IERC7913SignatureVerifier} from "../../interfaces/IERC7913.sol"; -import {ZKEmailUtils} from "./ZKEmailUtils.sol"; - -/** - * @dev ERC-7913 signature verifier that supports ZKEmail accounts. - * - * This contract verifies signatures produced through ZKEmail's zero-knowledge - * proofs which allows users to authenticate using their email addresses. - * - * The key decoding logic is customizable: users may override the {_decodeKey} function - * to enforce restrictions or validation on the decoded values (e.g., requiring a specific - * verifier, templateId, or registry). To remain compliant with ERC-7913's statelessness, - * it is recommended to enforce such restrictions using immutable variables only. - * - * Example of overriding _decodeKey to enforce a specific verifier, registry, (or templateId): - * - * ```solidity - * function _decodeKey(bytes calldata key) internal view override returns ( - * IDKIMRegistry registry, - * bytes32 accountSalt, - * IVerifier verifier, - * uint256 templateId - * ) { - * (registry, accountSalt, verifier, templateId) = super._decodeKey(key); - * require(verifier == _verifier, "Invalid verifier"); - * require(registry == _registry, "Invalid registry"); - * return (registry, accountSalt, verifier, templateId); - * } - * ``` - */ -abstract contract ERC7913SignatureVerifierZKEmail is IERC7913SignatureVerifier { - using ZKEmailUtils for EmailAuthMsg; - - /** - * @dev Verifies a zero-knowledge proof of an email signature validated by a {DKIMRegistry} contract. - * - * The key format is ABI-encoded (IDKIMRegistry, bytes32, IVerifier, uint256) where: - * - * * IDKIMRegistry: The registry contract that validates DKIM public key hashes - * * bytes32: The account salt that uniquely identifies the user's email address - * * IVerifier: The verifier contract instance for ZK proof verification. - * * uint256: The template ID for the command - * - * See {_decodeKey} for the key encoding format. - * - * The signature is an ABI-encoded {ZKEmailUtils-EmailAuthMsg} struct containing - * the command parameters, template ID, and proof details. - * - * Signature encoding: - * - * ```solidity - * bytes memory signature = abi.encode(EmailAuthMsg({ - * templateId: 1, - * commandParams: [hash], - * proof: { - * domainName: "example.com", // The domain name of the email sender - * publicKeyHash: bytes32(0x...), // Hash of the DKIM public key used to sign the email - * timestamp: block.timestamp, // When the email was sent - * maskedCommand: "Sign hash", // The command being executed, with sensitive data masked - * emailNullifier: bytes32(0x...), // Unique identifier for the email to prevent replay attacks - * accountSalt: bytes32(0x...), // Unique identifier derived from email and account code - * isCodeExist: true, // Whether the account code exists in the proof - * proof: bytes(0x...) // The zero-knowledge proof verifying the email's authenticity - * } - * })); - * ``` - */ - function verify( - bytes calldata key, - bytes32 hash, - bytes calldata signature - ) public view virtual override returns (bytes4) { - (IDKIMRegistry registry_, bytes32 accountSalt_, IVerifier verifier_, uint256 templateId_) = abi.decode( - key, - (IDKIMRegistry, bytes32, IVerifier, uint256) - ); - EmailAuthMsg memory emailAuthMsg = abi.decode(signature, (EmailAuthMsg)); - - return - (abi.decode(emailAuthMsg.commandParams[0], (bytes32)) == hash && - emailAuthMsg.templateId == templateId_ && - emailAuthMsg.proof.accountSalt == accountSalt_ && - emailAuthMsg.isValidZKEmail(registry_, verifier_) == ZKEmailUtils.EmailProofError.NoError) - ? IERC7913SignatureVerifier.verify.selector - : bytes4(0xffffffff); - } - - /** - * @dev Decodes the key into its components. - * - * ```solidity - * bytes memory key = abi.encode(registry, accountSalt, verifier, templateId); - * ``` - */ - function _decodeKey( - bytes calldata key - ) - internal - view - virtual - returns (IDKIMRegistry registry, bytes32 accountSalt, IVerifier verifier, uint256 templateId) - { - return abi.decode(key, (IDKIMRegistry, bytes32, IVerifier, uint256)); - } -} diff --git a/contracts/utils/cryptography/SignerZKEmail.sol b/contracts/utils/cryptography/SignerZKEmail.sol index feb4348f..62d3814a 100644 --- a/contracts/utils/cryptography/SignerZKEmail.sol +++ b/contracts/utils/cryptography/SignerZKEmail.sol @@ -81,10 +81,8 @@ abstract contract SignerZKEmail is AbstractSigner { return _registry; } - /** - * @dev An instance of the Verifier contract. - * See https://docs.zk.email/architecture/zk-proofs#how-zk-email-uses-zero-knowledge-proofs[ZK Proofs]. - */ + /// @dev An instance of the Verifier contract. + /// See https://docs.zk.email/architecture/zk-proofs#how-zk-email-uses-zero-knowledge-proofs[ZK Proofs]. function verifier() public view virtual returns (IVerifier) { return _verifier; } diff --git a/test/account/AccountERC7913.test.js b/test/account/AccountERC7913.test.js index 6f245abc..72004254 100644 --- a/test/account/AccountERC7913.test.js +++ b/test/account/AccountERC7913.test.js @@ -3,7 +3,7 @@ const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); const { getDomain } = require('@openzeppelin/contracts/test/helpers/eip712'); const { ERC4337Helper } = require('../helpers/erc4337'); -const { NonNativeSigner, P256SigningKey, RSASHA256SigningKey, ZKEmailSigningKey } = require('../helpers/signers'); +const { NonNativeSigner, P256SigningKey, RSASHA256SigningKey } = require('../helpers/signers'); const { PackedUserOperation } = require('../helpers/eip712-types'); const { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } = require('./Account.behavior'); @@ -15,36 +15,15 @@ const signerECDSA = ethers.Wallet.createRandom(); const signerP256 = new NonNativeSigner(P256SigningKey.random()); const signerRSA = new NonNativeSigner(RSASHA256SigningKey.random()); -// Constants for ZKEmail -const accountSalt = '0x046582bce36cdd0a8953b9d40b8f20d58302bacf3bcecffeb6741c98a52725e2'; // keccak256("test@example.com") -const selector = '12345'; -const domainName = 'gmail.com'; -const publicKeyHash = '0x0ea9c777dc7110e5a9e89b13f0cfc540e3845ba120b2b6dc24024d61488d4788'; -const emailNullifier = '0x00a83fce3d4b1c9ef0f600644c1ecc6c8115b57b1596e0e3295e2c5105fbfd8a'; -const templateId = ethers.solidityPackedKeccak256(['string', 'uint256'], ['TEST', 0n]); - // Minimal fixture common to the different signer verifiers async function fixture() { // EOAs and environment - const [admin, beneficiary, other] = await ethers.getSigners(); + const [beneficiary, other] = await ethers.getSigners(); const target = await ethers.deployContract('CallReceiverMockExtended'); - // DKIM Registry for ZKEmail - const dkim = await ethers.deployContract('ECDSAOwnedDKIMRegistry'); - await dkim.initialize(admin, admin); - await dkim - .SET_PREFIX() - .then(prefix => dkim.computeSignedMsg(prefix, domainName, publicKeyHash)) - .then(message => admin.signMessage(message)) - .then(signature => dkim.setDKIMPublicKeyHash(selector, domainName, publicKeyHash, signature)); - - // ZKEmail Verifier - const zkEmailVerifier = await ethers.deployContract('ZKEmailVerifierMock'); - // ERC-7913 verifiers const verifierP256 = await ethers.deployContract('ERC7913SignatureVerifierP256'); const verifierRSA = await ethers.deployContract('ERC7913SignatureVerifierRSA'); - const verifierZKEmail = await ethers.deployContract('$ERC7913SignatureVerifierZKEmail'); // ERC-4337 env const helper = new ERC4337Helper(); @@ -64,20 +43,7 @@ async function fixture() { .then(signature => Object.assign(userOp, { signature })); }; - return { - helper, - verifierP256, - verifierRSA, - verifierZKEmail, - dkim, - zkEmailVerifier, - domain, - target, - beneficiary, - other, - makeMock, - signUserOp, - }; + return { helper, verifierP256, verifierRSA, domain, target, beneficiary, other, makeMock, signUserOp }; } describe('AccountERC7913', function () { @@ -137,36 +103,4 @@ describe('AccountERC7913', function () { shouldBehaveLikeERC1271({ erc7739: true }); shouldBehaveLikeERC7821(); }); - - // Using ZKEmail with an ERC-7913 verifier - describe('ZKEmail', function () { - beforeEach(async function () { - // Create ZKEmail signer - this.signer = new NonNativeSigner( - new ZKEmailSigningKey(domainName, publicKeyHash, emailNullifier, accountSalt, templateId), - ); - - // Create account with ZKEmail verifier - this.mock = await this.makeMock( - ethers.concat([ - this.verifierZKEmail.target, - ethers.AbiCoder.defaultAbiCoder().encode( - ['address', 'bytes32', 'address', 'uint256'], - [this.dkim.target, accountSalt, this.zkEmailVerifier.target, templateId], - ), - ]), - ); - - // Override the signUserOp function to use the ZKEmail signer - this.signUserOp = async userOp => { - const hash = await userOp.hash(); - return Object.assign(userOp, { signature: this.signer.signingKey.sign(hash).serialized }); - }; - }); - - shouldBehaveLikeAccountCore(); - shouldBehaveLikeAccountHolder(); - shouldBehaveLikeERC1271({ erc7739: true }); - shouldBehaveLikeERC7821(); - }); });