From 27d2e523ef154e1d57b61bc4d811731e6448a4b2 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Sat, 29 Mar 2025 21:26:54 +0100 Subject: [PATCH 1/9] documentation --- lib/email-tx-builder | 2 +- lib/forge-std | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/email-tx-builder b/lib/email-tx-builder index ee70403a..db9b1131 160000 --- a/lib/email-tx-builder +++ b/lib/email-tx-builder @@ -1 +1 @@ -Subproject commit ee70403ad43c0006edfd153fa9dda248bb0ef618 +Subproject commit db9b113165c822c716ef6e72a573d9504428b60d diff --git a/lib/forge-std b/lib/forge-std index 841c3a3e..33539934 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 841c3a3e8b7612f76599797b93da5a61899c2aa8 +Subproject commit 3353993420c6e488a2914ce02d88174e80ad80f8 From a0d9c1dcfff0070ba4b23a55d24ed800d21dbe02 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 3 Apr 2025 19:38:20 +0200 Subject: [PATCH 2/9] Add ERC7913SignatureVerifierZKEmail --- .../ERC7913SignatureVerifierZKEmail.sol | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol diff --git a/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol b/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol new file mode 100644 index 00000000..06f24818 --- /dev/null +++ b/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; + +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 support ZKEmail accounts. + */ +contract ERC7913SignatureVerifierZKEmail is IERC7913SignatureVerifier { + using ZKEmailUtils for EmailAuthMsg; + + IVerifier public immutable verifier; + uint256 public immutable commandTemplate; + + constructor(IVerifier _verifier, uint256 _commandTemplate) { + verifier = _verifier; + commandTemplate = _commandTemplate; + } + + /// @inheritdoc IERC7913SignatureVerifier + function verify(bytes calldata key, bytes32 hash, bytes calldata signature) public view virtual returns (bytes4) { + (IDKIMRegistry registry, bytes32 accountSalt) = abi.decode(key, (IDKIMRegistry, bytes32)); + EmailAuthMsg memory emailAuthMsg = abi.decode(signature, (EmailAuthMsg)); + if ( + bytes32(emailAuthMsg.commandParams[0]) == hash && + emailAuthMsg.templateId == commandTemplate && + emailAuthMsg.proof.accountSalt == accountSalt + ) { + (bool verified, ) = emailAuthMsg.isValidZKEmail(registry, verifier); + if (verified) { + return IERC7913SignatureVerifier.verify.selector; + } + } + return 0xffffffff; + } +} From b24564cae7d0ab3546dc894f77c58954e4c96496 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 3 Apr 2025 19:47:03 +0200 Subject: [PATCH 3/9] Update ERC7913SignatureVerifierZKEmail.sol --- .../utils/cryptography/ERC7913SignatureVerifierZKEmail.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol b/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol index 06f24818..7d7dd3c2 100644 --- a/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol +++ b/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol @@ -2,8 +2,6 @@ pragma solidity ^0.8.20; -import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; - 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"; From e2f86b96e4bd4a31a67181cafb0cc2b3a5b73d15 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 4 Apr 2025 15:56:25 +0200 Subject: [PATCH 4/9] Apply ZKEmailUtils changes to ERC7913SignatureVerifierZKEmail --- .../ERC7913SignatureVerifierZKEmail.sol | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol b/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol index 7d7dd3c2..ce8a5f90 100644 --- a/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol +++ b/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol @@ -26,16 +26,13 @@ contract ERC7913SignatureVerifierZKEmail is IERC7913SignatureVerifier { function verify(bytes calldata key, bytes32 hash, bytes calldata signature) public view virtual returns (bytes4) { (IDKIMRegistry registry, bytes32 accountSalt) = abi.decode(key, (IDKIMRegistry, bytes32)); EmailAuthMsg memory emailAuthMsg = abi.decode(signature, (EmailAuthMsg)); - if ( - bytes32(emailAuthMsg.commandParams[0]) == hash && - emailAuthMsg.templateId == commandTemplate && - emailAuthMsg.proof.accountSalt == accountSalt - ) { - (bool verified, ) = emailAuthMsg.isValidZKEmail(registry, verifier); - if (verified) { - return IERC7913SignatureVerifier.verify.selector; - } - } - return 0xffffffff; + + return + (abi.decode(emailAuthMsg.commandParams[0], (bytes32)) == hash && + emailAuthMsg.templateId == commandTemplate && + emailAuthMsg.proof.accountSalt == accountSalt && + emailAuthMsg.isValidZKEmail(registry, verifier) == ZKEmailUtils.EmailProofError.NoError) + ? IERC7913SignatureVerifier.verify.selector + : bytes4(0xffffffff); } } From 4a1071664788f911fd3bc7f9395b919119f54e91 Mon Sep 17 00:00:00 2001 From: ernestognw Date: Sat, 12 Apr 2025 14:53:56 -0600 Subject: [PATCH 5/9] Rebased --- lib/email-tx-builder | 2 +- lib/forge-std | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/email-tx-builder b/lib/email-tx-builder index db9b1131..ee70403a 160000 --- a/lib/email-tx-builder +++ b/lib/email-tx-builder @@ -1 +1 @@ -Subproject commit db9b113165c822c716ef6e72a573d9504428b60d +Subproject commit ee70403ad43c0006edfd153fa9dda248bb0ef618 diff --git a/lib/forge-std b/lib/forge-std index 33539934..841c3a3e 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 3353993420c6e488a2914ce02d88174e80ad80f8 +Subproject commit 841c3a3e8b7612f76599797b93da5a61899c2aa8 From 330bedfffb48aceecfab7a1c018cae5107122468 Mon Sep 17 00:00:00 2001 From: ernestognw Date: Tue, 22 Apr 2025 16:18:42 -0600 Subject: [PATCH 6/9] Review and testsd --- .../ERC7913SignatureVerifierZKEmail.sol | 68 +++++++++++++++--- test/account/AccountERC7913.test.js | 72 ++++++++++++++++++- 2 files changed, 127 insertions(+), 13 deletions(-) diff --git a/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol b/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol index ce8a5f90..6b7511fe 100644 --- a/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol +++ b/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol @@ -9,29 +9,77 @@ import {IERC7913SignatureVerifier} from "../../interfaces/IERC7913.sol"; import {ZKEmailUtils} from "./ZKEmailUtils.sol"; /** - * @dev ERC-7913 signature verifier that support ZKEmail accounts. + * @dev ERC-7913 signature verifier that supports ZKEmail accounts. + * + * This verifier validates signatures produced through ZKEmail's zero-knowledge + * proofs which allows users to authenticate using their email addresses. */ -contract ERC7913SignatureVerifierZKEmail is IERC7913SignatureVerifier { +abstract contract ERC7913SignatureVerifierZKEmail is IERC7913SignatureVerifier { using ZKEmailUtils for EmailAuthMsg; - IVerifier public immutable verifier; - uint256 public immutable commandTemplate; + IVerifier private immutable _verifier; + uint256 private immutable _templateId; - constructor(IVerifier _verifier, uint256 _commandTemplate) { - verifier = _verifier; - commandTemplate = _commandTemplate; + constructor(IVerifier verifier_, uint256 templateId_) { + _verifier = verifier_; + _templateId = templateId_; } - /// @inheritdoc IERC7913SignatureVerifier + /// @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; + } + + /// @dev The command template of the sign hash command. + function templateId() public view virtual returns (uint256) { + return _templateId; + } + + /** + * @dev Verifies a zero-knowledge proof of an email signature validated by a {DKIMRegistry} contract. + * + * The key format is ABI-encoded (IDKIMRegistry, bytes32) where: + * - IDKIMRegistry: The registry contract that validates DKIM public key hashes + * - bytes32: The account salt that uniquely identifies the user's email address + * + * The signature is an ABI-encoded {ZKEmailUtils-EmailAuthMsg} struct containing + * the command parameters, template ID, and proof details. + * + * Key encoding: + * + * ```solidity + * bytes memory key = abi.encode(registry, accountSalt); + * ``` + * + * 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 returns (bytes4) { (IDKIMRegistry registry, bytes32 accountSalt) = abi.decode(key, (IDKIMRegistry, bytes32)); EmailAuthMsg memory emailAuthMsg = abi.decode(signature, (EmailAuthMsg)); return (abi.decode(emailAuthMsg.commandParams[0], (bytes32)) == hash && - emailAuthMsg.templateId == commandTemplate && + emailAuthMsg.templateId == templateId() && emailAuthMsg.proof.accountSalt == accountSalt && - emailAuthMsg.isValidZKEmail(registry, verifier) == ZKEmailUtils.EmailProofError.NoError) + emailAuthMsg.isValidZKEmail(registry, verifier()) == ZKEmailUtils.EmailProofError.NoError) ? IERC7913SignatureVerifier.verify.selector : bytes4(0xffffffff); } diff --git a/test/account/AccountERC7913.test.js b/test/account/AccountERC7913.test.js index 72004254..8c7dc5dd 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 } = require('../helpers/signers'); +const { NonNativeSigner, P256SigningKey, RSASHA256SigningKey, ZKEmailSigningKey } = require('../helpers/signers'); const { PackedUserOperation } = require('../helpers/eip712-types'); const { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } = require('./Account.behavior'); @@ -15,15 +15,39 @@ 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 [beneficiary, other] = await ethers.getSigners(); + const [admin, 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', [ + zkEmailVerifier.target, + templateId, + ]); // ERC-4337 env const helper = new ERC4337Helper(); @@ -43,7 +67,20 @@ async function fixture() { .then(signature => Object.assign(userOp, { signature })); }; - return { helper, verifierP256, verifierRSA, domain, target, beneficiary, other, makeMock, signUserOp }; + return { + helper, + verifierP256, + verifierRSA, + verifierZKEmail, + dkim, + zkEmailVerifier, + domain, + target, + beneficiary, + other, + makeMock, + signUserOp, + }; } describe('AccountERC7913', function () { @@ -103,4 +140,33 @@ 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'], [this.dkim.target, accountSalt]), + ]), + ); + + // 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(); + }); }); From 359af07eaa7b1310d184789d7c856f0c7e857b69 Mon Sep 17 00:00:00 2001 From: ernestognw Date: Tue, 22 Apr 2025 16:20:49 -0600 Subject: [PATCH 7/9] Pragma up --- .../utils/cryptography/ERC7913SignatureVerifierZKEmail.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol b/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol index 6b7511fe..9b5ed483 100644 --- a/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol +++ b/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; +pragma solidity ^0.8.24; import {IDKIMRegistry} from "@zk-email/contracts/DKIMRegistry.sol"; import {IVerifier} from "@zk-email/email-tx-builder/interfaces/IVerifier.sol"; From 238b86d4e68ea21d699298ac93da2cfffd4813ea Mon Sep 17 00:00:00 2001 From: ernestognw Date: Tue, 22 Apr 2025 22:18:45 -0600 Subject: [PATCH 8/9] review --- .../cryptography/ERC7913SignatureVerifierZKEmail.sol | 8 +++++--- contracts/utils/cryptography/SignerZKEmail.sol | 6 ++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol b/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol index 9b5ed483..f4660d1e 100644 --- a/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol +++ b/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol @@ -11,7 +11,7 @@ import {ZKEmailUtils} from "./ZKEmailUtils.sol"; /** * @dev ERC-7913 signature verifier that supports ZKEmail accounts. * - * This verifier validates signatures produced through ZKEmail's zero-knowledge + * This contract verifies signatures produced through ZKEmail's zero-knowledge * proofs which allows users to authenticate using their email addresses. */ abstract contract ERC7913SignatureVerifierZKEmail is IERC7913SignatureVerifier { @@ -25,8 +25,10 @@ abstract contract ERC7913SignatureVerifierZKEmail is IERC7913SignatureVerifier { _templateId = templateId_; } - /// @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/contracts/utils/cryptography/SignerZKEmail.sol b/contracts/utils/cryptography/SignerZKEmail.sol index 62d3814a..feb4348f 100644 --- a/contracts/utils/cryptography/SignerZKEmail.sol +++ b/contracts/utils/cryptography/SignerZKEmail.sol @@ -81,8 +81,10 @@ 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; } From 214188dc803a5a4f87b3f3a947f6984b0f4cdb2d Mon Sep 17 00:00:00 2001 From: ernestognw Date: Tue, 6 May 2025 22:31:00 -0600 Subject: [PATCH 9/9] Add templateId and verifier as part of the key in ERC7913SignatureVerifierZKEmail --- .../ERC7913SignatureVerifierZKEmail.sol | 94 ++++++++++++------- test/account/AccountERC7913.test.js | 10 +- 2 files changed, 64 insertions(+), 40 deletions(-) diff --git a/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol b/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol index f4660d1e..1576b2d3 100644 --- a/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol +++ b/contracts/utils/cryptography/ERC7913SignatureVerifierZKEmail.sol @@ -13,46 +13,45 @@ import {ZKEmailUtils} from "./ZKEmailUtils.sol"; * * 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; - IVerifier private immutable _verifier; - uint256 private immutable _templateId; - - constructor(IVerifier verifier_, uint256 templateId_) { - _verifier = verifier_; - _templateId = templateId_; - } - - /** - * @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; - } - - /// @dev The command template of the sign hash command. - function templateId() public view virtual returns (uint256) { - return _templateId; - } - /** * @dev Verifies a zero-knowledge proof of an email signature validated by a {DKIMRegistry} contract. * - * The key format is ABI-encoded (IDKIMRegistry, bytes32) where: - * - IDKIMRegistry: The registry contract that validates DKIM public key hashes - * - bytes32: The account salt that uniquely identifies the user's email address + * The key format is ABI-encoded (IDKIMRegistry, bytes32, IVerifier, uint256) where: * - * The signature is an ABI-encoded {ZKEmailUtils-EmailAuthMsg} struct containing - * the command parameters, template ID, and proof details. + * * 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 * - * Key encoding: + * See {_decodeKey} for the key encoding format. * - * ```solidity - * bytes memory key = abi.encode(registry, accountSalt); - * ``` + * The signature is an ABI-encoded {ZKEmailUtils-EmailAuthMsg} struct containing + * the command parameters, template ID, and proof details. * * Signature encoding: * @@ -73,16 +72,41 @@ abstract contract ERC7913SignatureVerifierZKEmail is IERC7913SignatureVerifier { * })); * ``` */ - function verify(bytes calldata key, bytes32 hash, bytes calldata signature) public view virtual returns (bytes4) { - (IDKIMRegistry registry, bytes32 accountSalt) = abi.decode(key, (IDKIMRegistry, bytes32)); + 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) + 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/test/account/AccountERC7913.test.js b/test/account/AccountERC7913.test.js index 8c7dc5dd..6f245abc 100644 --- a/test/account/AccountERC7913.test.js +++ b/test/account/AccountERC7913.test.js @@ -44,10 +44,7 @@ async function fixture() { // ERC-7913 verifiers const verifierP256 = await ethers.deployContract('ERC7913SignatureVerifierP256'); const verifierRSA = await ethers.deployContract('ERC7913SignatureVerifierRSA'); - const verifierZKEmail = await ethers.deployContract('$ERC7913SignatureVerifierZKEmail', [ - zkEmailVerifier.target, - templateId, - ]); + const verifierZKEmail = await ethers.deployContract('$ERC7913SignatureVerifierZKEmail'); // ERC-4337 env const helper = new ERC4337Helper(); @@ -153,7 +150,10 @@ describe('AccountERC7913', function () { this.mock = await this.makeMock( ethers.concat([ this.verifierZKEmail.target, - ethers.AbiCoder.defaultAbiCoder().encode(['address', 'bytes32'], [this.dkim.target, accountSalt]), + ethers.AbiCoder.defaultAbiCoder().encode( + ['address', 'bytes32', 'address', 'uint256'], + [this.dkim.target, accountSalt, this.zkEmailVerifier.target, templateId], + ), ]), );