Skip to content

Commit c5dd503

Browse files
committed
Merge branch 'feature/zk-email' into feature/zk-email-7913
2 parents c9864de + e8fa099 commit c5dd503

File tree

9 files changed

+266
-113
lines changed

9 files changed

+266
-113
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// contracts/MyAccountZKEmail.sol
2+
// SPDX-License-Identifier: MIT
3+
4+
pragma solidity ^0.8.20;
5+
6+
import {Account} from "../../../account/Account.sol";
7+
import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
8+
import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
9+
import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
10+
import {ERC7739} from "../../../utils/cryptography/ERC7739.sol";
11+
import {ERC7821} from "../../../account/extensions/ERC7821.sol";
12+
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
13+
import {SignerZKEmail} from "../../../utils/cryptography/SignerZKEmail.sol";
14+
import {IDKIMRegistry} from "@zk-email/contracts/DKIMRegistry.sol";
15+
import {IVerifier} from "@zk-email/email-tx-builder/interfaces/IVerifier.sol";
16+
17+
contract MyAccountZKEmail is Account, SignerZKEmail, ERC7739, ERC7821, ERC721Holder, ERC1155Holder, Initializable {
18+
constructor() EIP712("MyAccountZKEmail", "1") {}
19+
20+
function initialize(
21+
bytes32 accountSalt_,
22+
IDKIMRegistry registry_,
23+
IVerifier verifier_,
24+
uint256 templateId_
25+
) public initializer {
26+
_setAccountSalt(accountSalt_);
27+
_setDKIMRegistry(registry_);
28+
_setVerifier(verifier_);
29+
_setCommandTemplate(templateId_);
30+
}
31+
32+
/// @dev Allows the entry point as an authorized executor.
33+
function _erc7821AuthorizedExecutor(
34+
address caller,
35+
bytes32 mode,
36+
bytes calldata executionData
37+
) internal view virtual override returns (bool) {
38+
return caller == address(entryPoint()) || super._erc7821AuthorizedExecutor(caller, mode, executionData);
39+
}
40+
}

contracts/utils/README.adoc

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@ Miscellaneous contracts and libraries containing utility functions you can use t
99
* {ERC7739}: An abstract contract to validate signatures following the rehashing scheme from `ERC7739Utils`.
1010
* {ERC7739Utils}: Utilities library that implements a defensive rehashing mechanism to prevent replayability of smart contract signatures based on ERC-7739.
1111
* {ERC7913Utils}: utilities library that implements ERC-7913 signature verification with fallback to ERC-1271 and ECDSA.
12-
* {SignerECDSA}, {SignerERC7913}, {SignerP256}, {SignerRSA}: Implementations of an {AbstractSigner} with specific signature validation algorithms.
1312
* {ERC7913SignatureVerifierP256}, {ERC7913SignatureVerifierRSA}: Ready to use ERC-7913 signature verifiers for P256 and RSA keys
13+
* {ERC7913SignatureVerifierZKEmail}: Ready to use ERC-7913 signature verifiers for P256 and RSA keys
14+
* {SignerECDSA}, {SignerERC7913}, {SignerP256}, {SignerRSA}: Implementations of an {AbstractSigner} with specific signature validation algorithms.
15+
* {SignerZKEmail}: Implementation of an {AbstractSigner} that enables email-based authentication through zero-knowledge proofs.
1416
* {Masks}: Library to handle `bytes32` masks.
17+
* {ZKEmailUtils}: Library for ZKEmail signature validation utilities, enabling email-based authentication through zero-knowledge proofs.
1518

1619
== Cryptography
1720

@@ -31,6 +34,10 @@ Miscellaneous contracts and libraries containing utility functions you can use t
3134

3235
{{SignerRSA}}
3336

37+
{{SignerZKEmail}}
38+
39+
{{ZKEmailUtils}}
40+
3441
=== ERC-7913
3542

3643
{{ERC7913Utils}}
@@ -39,6 +46,8 @@ Miscellaneous contracts and libraries containing utility functions you can use t
3946

4047
{{ERC7913SignatureVerifierRSA}}
4148

49+
{{ERC7913SignatureVerifierZKEmail}}
50+
4251
== Libraries
4352

4453
{{Masks}}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.20;
4+
5+
import {IDKIMRegistry} from "@zk-email/contracts/DKIMRegistry.sol";
6+
import {IVerifier, EmailProof} from "@zk-email/email-tx-builder/interfaces/IVerifier.sol";
7+
import {EmailAuthMsg} from "@zk-email/email-tx-builder/interfaces/IEmailTypes.sol";
8+
import {AbstractSigner} from "./AbstractSigner.sol";
9+
import {ZKEmailUtils} from "./ZKEmailUtils.sol";
10+
11+
/**
12+
* @dev Implementation of {AbstractSigner} using https://docs.zk.email[ZKEmail] signatures.
13+
*
14+
* ZKEmail enables secure authentication and authorization through email messages, leveraging
15+
* DKIM signatures from a trusted {DKIMRegistry} and zero-knowledge proofs enabled by a {verifier}
16+
* contract that ensures email authenticity without revealing sensitive information. This contract
17+
* implements the core functionality for validating email-based signatures in smart contracts.
18+
*
19+
* Developers must set the following components during contract initialization:
20+
*
21+
* * {accountSalt} - A unique identifier derived from the user's email address and account code.
22+
* * {DKIMRegistry} - An instance of the DKIM registry contract for domain verification.
23+
* * {verifier} - An instance of the Verifier contract for zero-knowledge proof validation.
24+
* * {commandTemplate} - The template ID of the sign hash command, defining the expected format.
25+
*
26+
* Example of usage:
27+
*
28+
* ```solidity
29+
* contract MyAccountZKEmail is Account, SignerZKEmail, Initializable {
30+
* constructor(bytes32 accountSalt, IDKIMRegistry registry, IVerifier verifier, uint256 templateId) {
31+
* // Will revert if the signer is already initialized
32+
* _setAccountSalt(accountSalt);
33+
* _setDKIMRegistry(registry);
34+
* _setVerifier(verifier);
35+
* _setCommandTemplate(templateId);
36+
* }
37+
* }
38+
* ```
39+
*
40+
* IMPORTANT: Avoiding to call {_setAccountSalt}, {_setDKIMRegistry}, {_setVerifier} and {_setCommandTemplate}
41+
* either during construction (if used standalone) or during initialization (if used as a clone) may
42+
* leave the signer either front-runnable or unusable.
43+
*/
44+
abstract contract SignerZKEmail is AbstractSigner {
45+
using ZKEmailUtils for EmailAuthMsg;
46+
47+
bytes32 private _accountSalt;
48+
IDKIMRegistry private _registry;
49+
IVerifier private _verifier;
50+
uint256 private _commandTemplate;
51+
52+
/// @dev Proof verification error.
53+
error InvalidEmailProof(ZKEmailUtils.EmailProofError err);
54+
55+
/**
56+
* @dev Unique identifier for owner of this contract defined as a hash of an email address and an account code.
57+
*
58+
* An account code is a random integer in a finite scalar field of https://neuromancer.sk/std/bn/bn254[BN254] curve.
59+
* It is a private randomness to derive a CREATE2 salt of the user's Ethereum address
60+
* from the email address, i.e., userEtherAddr := CREATE2(hash(userEmailAddr, accountCode)).
61+
*
62+
* The account salt is used for:
63+
*
64+
* * User Identification: Links the email address to a specific Ethereum address securely and deterministically.
65+
* * Security: Provides a unique identifier that cannot be easily guessed or brute-forced, as it's derived
66+
* from both the email address and a random account code.
67+
* * Deterministic Address Generation: Enables the creation of deterministic addresses based on email addresses,
68+
* allowing users to recover their accounts using only their email.
69+
*/
70+
function accountSalt() public view virtual returns (bytes32) {
71+
return _accountSalt;
72+
}
73+
74+
/// @dev An instance of the DKIM registry contract.
75+
/// See https://docs.zk.email/architecture/dkim-verification[DKIM Verification].
76+
// solhint-disable-next-line func-name-mixedcase
77+
function DKIMRegistry() public view virtual returns (IDKIMRegistry) {
78+
return _registry;
79+
}
80+
81+
/// @dev An instance of the Verifier contract.
82+
/// See https://docs.zk.email/architecture/zk-proofs#how-zk-email-uses-zero-knowledge-proofs[ZK Proofs].
83+
function verifier() public view virtual returns (IVerifier) {
84+
return _verifier;
85+
}
86+
87+
/// @dev The templateId of the sign hash command.
88+
function commandTemplate() public view virtual returns (uint256) {
89+
return _commandTemplate;
90+
}
91+
92+
/// @dev Set the {accountSalt}.
93+
function _setAccountSalt(bytes32 accountSalt_) internal virtual {
94+
_accountSalt = accountSalt_;
95+
}
96+
97+
/// @dev Set the {DKIMRegistry} contract address.
98+
function _setDKIMRegistry(IDKIMRegistry registry_) internal virtual {
99+
_registry = registry_;
100+
}
101+
102+
/// @dev Set the {verifier} contract address.
103+
function _setVerifier(IVerifier verifier_) internal virtual {
104+
_verifier = verifier_;
105+
}
106+
107+
/// @dev Set the {commandTemplate} ID.
108+
function _setCommandTemplate(uint256 commandTemplate_) internal virtual {
109+
_commandTemplate = commandTemplate_;
110+
}
111+
112+
/**
113+
* @dev Authenticates the email sender and authorizes the message in the email command.
114+
*
115+
* NOTE: This function only verifies the authenticity of the email and command, without
116+
* handling replay protection. The calling contract should implement its own mechanisms
117+
* to prevent replay attacks, similar to how nonces are used with ECDSA signatures.
118+
*/
119+
function verifyEmail(EmailAuthMsg memory emailAuthMsg) public view virtual {
120+
ZKEmailUtils.EmailProofError err = emailAuthMsg.isValidZKEmail(DKIMRegistry(), verifier());
121+
if (err != ZKEmailUtils.EmailProofError.NoError) revert InvalidEmailProof(err);
122+
}
123+
124+
/**
125+
* @dev See {AbstractSigner-_rawSignatureValidation}. Validates a raw signature by:
126+
*
127+
* 1. Decoding the email authentication message from the signature
128+
* 2. Verifying the hash matches the command parameters
129+
* 3. Checking the template ID matches
130+
* 4. Validating the account salt
131+
* 5. Verifying the email proof
132+
*/
133+
function _rawSignatureValidation(
134+
bytes32 hash,
135+
bytes calldata signature
136+
) internal view virtual override returns (bool) {
137+
EmailAuthMsg memory emailAuthMsg = abi.decode(signature, (EmailAuthMsg));
138+
return (abi.decode(emailAuthMsg.commandParams[0], (bytes32)) == hash &&
139+
emailAuthMsg.templateId == commandTemplate() &&
140+
emailAuthMsg.proof.accountSalt == accountSalt() &&
141+
emailAuthMsg.isValidZKEmail(DKIMRegistry(), verifier()) == ZKEmailUtils.EmailProofError.NoError);
142+
}
143+
}

contracts/utils/cryptography/ZKEmailSigner.sol

Lines changed: 0 additions & 101 deletions
This file was deleted.

0 commit comments

Comments
 (0)