-
Notifications
You must be signed in to change notification settings - Fork 24
Add SignerZKEmail and ZKEmailUtils #96
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
62 commits
Select commit
Hold shift + click to select a range
89cecfc
Add ZKEmailSigner
ernestognw bea0548
Remove yarn.lock
ernestognw b968068
Update ZKEmailSigner.sol
Amxx b99b9f8
Add initializer
ernestognw f5a2643
Merge branch 'master' into feature/zk-email
ernestognw 9577e06
Run `forge upate lib/email-tx-builder` to bring recent changes
ernestognw 813b226
Add ZKEmailUtils and update ZKEmailSigner
ernestognw ef5fe72
Add BN256 curve reference
ernestognw d9cba45
Add .prettierignore to ignore lib linting
ernestognw ccc43fa
Exclude zk-email from pragma consistency checks
ernestognw 24ed077
Update contracts/utils/cryptography/ZKEmailSigner.sol
Amxx 82850cb
Update contracts/utils/cryptography/ZKEmailSigner.sol
Amxx b6a3fdf
Update ZKEmailSigner.sol
Amxx 3dd1fb9
Run `forge update`
ernestognw 6bc3140
Document ZKEmailSigner and ZKEmailUtils
ernestognw 27dcc0d
Rename to SignerZKEmail for consistency
ernestognw 35b0bd6
Document ZKEmailSigner and ZKEmailUtils
ernestognw 900b668
Update README to include SignerZKEmail and ZKEmailUtils documentation
ernestognw e8fa099
Simplify return type for ZKEmailUtils.isValidZKEmail
Amxx 4b6426c
Setup tests
ernestognw 581123d
Rename commandTemplate to templateId
ernestognw 11b197f
Address https://github.com/OpenZeppelin/openzeppelin-community-contra…
Amxx 6d5f974
Fix compilation
ernestognw 3e9d0e3
Refactor ZKEmailUtils
ernestognw 72a4cfc
Adjustment
ernestognw 9742daa
Refactor ZKEmailUtils
ernestognw 34aa505
Note _matchCase
ernestognw fa8b58b
Remove _commandMatch
ernestognw 471e4de
refactor
Amxx aa7cdaa
Readd refactored _commandMatch
ernestognw 190f0b3
Remove extra line
ernestognw 202b59f
Add missing logic to remove prefix using Bytes library
ernestognw 8f0a057
Fix compilation
ernestognw ed5e027
Pragma increase for mcopy support
ernestognw 4ec7569
Apply pragma to SignerZKEmail too
ernestognw 8a202bd
Fix pragma in mocks
ernestognw d3e0ddf
Add @JohnGuilding and @zkfriendly to CODEOWNERS
ernestognw 6d4f9f6
Try alternative syntax
ernestognw 683f3fc
Revert CODEOWNERS
ernestognw c3275d2
Address review suggestions
ernestognw 1d428d4
Add ZKEmailUtils tests
ernestognw 521fbdd
Improve coverage
ernestognw 7842fa6
Moar coverage
ernestognw 6bb0c00
Remove verifyEmail from signer
ernestognw 8a5a283
Merge branch 'master' into feature/zk-email
ernestognw c039530
Remove unnecessary imports
ernestognw cd6f66f
Reorder ZKEmailUtils checks
ernestognw de69659
Moar tests
ernestognw e70f6d1
Simplify ZKEmailUtils.t.sol
ernestognw a6dcddd
Update syntax in _commandMatch to test codecov
ernestognw de157d8
Improve fuzz tests
ernestognw c9672d9
Improve fuzz tests
ernestognw 9ee4ccd
Remove DKIM mock
ernestognw f26d26b
Add Hardhat tests
ernestognw 799cf6c
Fix codespell
ernestognw f27f1a1
Add AccountZKEmail.test.js
ernestognw e1d6297
Fix codespell
ernestognw c924e97
Merge branch 'master' into feature/zk-email
ernestognw e7f44f2
nit
ernestognw 9fed941
Reorder README.adoc
ernestognw 0138787
forge update
ernestognw 9f1171e
Merge branch 'master' into feature/zk-email
ernestognw File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
lib |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.24; | ||
|
||
import {Account} from "../../account/Account.sol"; | ||
import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol"; | ||
import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; | ||
import {ERC7739, EIP712} from "../../utils/cryptography/ERC7739.sol"; | ||
import {ERC7821} from "../../account/extensions/ERC7821.sol"; | ||
import {SignerZKEmail} from "../../utils/cryptography/SignerZKEmail.sol"; | ||
import {IDKIMRegistry} from "@zk-email/contracts/DKIMRegistry.sol"; | ||
import {IVerifier} from "@zk-email/email-tx-builder/interfaces/IVerifier.sol"; | ||
|
||
contract AccountZKEmailMock is Account, SignerZKEmail, ERC7739, ERC7821, ERC721Holder, ERC1155Holder { | ||
constructor( | ||
bytes32 accountSalt_, | ||
IDKIMRegistry registry_, | ||
IVerifier verifier_, | ||
uint256 templateId_ | ||
) EIP712("AccountZKEmailMock", "1") { | ||
_setAccountSalt(accountSalt_); | ||
_setDKIMRegistry(registry_); | ||
_setVerifier(verifier_); | ||
_setTemplateId(templateId_); | ||
} | ||
|
||
/// @inheritdoc ERC7821 | ||
function _erc7821AuthorizedExecutor( | ||
address caller, | ||
bytes32 mode, | ||
bytes calldata executionData | ||
) internal view virtual override returns (bool) { | ||
return caller == address(entryPoint()) || super._erc7821AuthorizedExecutor(caller, mode, executionData); | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
// contracts/MyAccountZKEmail.sol | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.24; | ||
|
||
import {Account} from "../../../account/Account.sol"; | ||
import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; | ||
import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol"; | ||
import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; | ||
import {ERC7739} from "../../../utils/cryptography/ERC7739.sol"; | ||
import {ERC7821} from "../../../account/extensions/ERC7821.sol"; | ||
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; | ||
import {SignerZKEmail} from "../../../utils/cryptography/SignerZKEmail.sol"; | ||
import {IDKIMRegistry} from "@zk-email/contracts/DKIMRegistry.sol"; | ||
import {IVerifier} from "@zk-email/email-tx-builder/interfaces/IVerifier.sol"; | ||
|
||
contract MyAccountZKEmail is Account, SignerZKEmail, ERC7739, ERC7821, ERC721Holder, ERC1155Holder, Initializable { | ||
constructor() EIP712("MyAccountZKEmail", "1") {} | ||
|
||
function initialize( | ||
bytes32 accountSalt_, | ||
IDKIMRegistry registry_, | ||
IVerifier verifier_, | ||
uint256 templateId_ | ||
) public initializer { | ||
_setAccountSalt(accountSalt_); | ||
_setDKIMRegistry(registry_); | ||
_setVerifier(verifier_); | ||
_setTemplateId(templateId_); | ||
} | ||
|
||
/// @dev Allows the entry point as an authorized executor. | ||
function _erc7821AuthorizedExecutor( | ||
address caller, | ||
bytes32 mode, | ||
bytes calldata executionData | ||
) internal view virtual override returns (bool) { | ||
return caller == address(entryPoint()) || super._erc7821AuthorizedExecutor(caller, mode, executionData); | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
contracts/mocks/utils/cryptography/ZKEmailVerifierMock.sol
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.20; | ||
|
||
import {IVerifier, EmailProof} from "@zk-email/email-tx-builder/interfaces/IVerifier.sol"; | ||
|
||
contract ZKEmailVerifierMock is IVerifier { | ||
function commandBytes() external pure returns (uint256) { | ||
// Same as in https://github.com/zkemail/email-tx-builder/blob/1452943807a5fdc732e1113c34792c76cf7dd031/packages/contracts/src/utils/Verifier.sol#L15 | ||
return 605; | ||
} | ||
|
||
function verifyEmailProof(EmailProof memory proof) external pure returns (bool) { | ||
return proof.proof.length > 0 && bytes1(proof.proof[0]) == 0x01; // boolean true | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
// 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 {AbstractSigner} from "./AbstractSigner.sol"; | ||
import {ZKEmailUtils} from "./ZKEmailUtils.sol"; | ||
|
||
/** | ||
* @dev Implementation of {AbstractSigner} using https://docs.zk.email[ZKEmail] signatures. | ||
* | ||
* ZKEmail enables secure authentication and authorization through email messages, leveraging | ||
* DKIM signatures from a {DKIMRegistry} and zero-knowledge proofs enabled by a {verifier} | ||
* contract that ensures email authenticity without revealing sensitive information. The DKIM | ||
* registry is trusted to correctly update DKIM keys, but users can override this behaviour and | ||
* set their own keys. This contract implements the core functionality for validating email-based | ||
* signatures in smart contracts. | ||
* | ||
* Developers must set the following components during contract initialization: | ||
* | ||
* * {accountSalt} - A unique identifier derived from the user's email address and account code. | ||
* * {DKIMRegistry} - An instance of the DKIM registry contract for domain verification. | ||
* * {verifier} - An instance of the Verifier contract for zero-knowledge proof validation. | ||
* * {templateId} - The template ID of the sign hash command, defining the expected format. | ||
* | ||
* Example of usage: | ||
* | ||
* ```solidity | ||
* contract MyAccountZKEmail is Account, SignerZKEmail, Initializable { | ||
* constructor(bytes32 accountSalt, IDKIMRegistry registry, IVerifier verifier, uint256 templateId) { | ||
* // Will revert if the signer is already initialized | ||
* _setAccountSalt(accountSalt); | ||
* _setDKIMRegistry(registry); | ||
* _setVerifier(verifier); | ||
* _setTemplateId(templateId); | ||
* } | ||
* } | ||
* ``` | ||
* | ||
* IMPORTANT: Avoiding to call {_setAccountSalt}, {_setDKIMRegistry}, {_setVerifier} and {_setTemplateId} | ||
* either during construction (if used standalone) or during initialization (if used as a clone) may | ||
* leave the signer either front-runnable or unusable. | ||
*/ | ||
abstract contract SignerZKEmail is AbstractSigner { | ||
using ZKEmailUtils for EmailAuthMsg; | ||
|
||
bytes32 private _accountSalt; | ||
IDKIMRegistry private _registry; | ||
IVerifier private _verifier; | ||
uint256 private _templateId; | ||
|
||
/// @dev Proof verification error. | ||
error InvalidEmailProof(ZKEmailUtils.EmailProofError err); | ||
|
||
/** | ||
* @dev Unique identifier for owner of this contract defined as a hash of an email address and an account code. | ||
* | ||
* An account code is a random integer in a finite scalar field of https://neuromancer.sk/std/bn/bn254[BN254] curve. | ||
* It is a private randomness to derive a CREATE2 salt of the user's Ethereum address | ||
* from the email address, i.e., userEtherAddr := CREATE2(hash(userEmailAddr, accountCode)). | ||
* | ||
* The account salt is used for: | ||
* | ||
* * Privacy: Enables email address privacy on-chain so long as the randomly generated account code is not revealed | ||
* to an adversary. | ||
* * Security: Provides a unique identifier that cannot be easily guessed or brute-forced, as it's derived | ||
* from both the email address and a random account code. | ||
* * Deterministic Address Generation: Enables the creation of deterministic addresses based on email addresses, | ||
* allowing users to recover their accounts using only their email. | ||
*/ | ||
function accountSalt() public view virtual returns (bytes32) { | ||
return _accountSalt; | ||
} | ||
|
||
/// @dev An instance of the DKIM registry contract. | ||
/// See https://docs.zk.email/architecture/dkim-verification[DKIM Verification]. | ||
// solhint-disable-next-line func-name-mixedcase | ||
function DKIMRegistry() public view virtual returns (IDKIMRegistry) { | ||
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]. | ||
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 Set the {accountSalt}. | ||
function _setAccountSalt(bytes32 accountSalt_) internal virtual { | ||
_accountSalt = accountSalt_; | ||
} | ||
|
||
/// @dev Set the {DKIMRegistry} contract address. | ||
function _setDKIMRegistry(IDKIMRegistry registry_) internal virtual { | ||
_registry = registry_; | ||
} | ||
|
||
/// @dev Set the {verifier} contract address. | ||
function _setVerifier(IVerifier verifier_) internal virtual { | ||
_verifier = verifier_; | ||
} | ||
|
||
/// @dev Set the command's {templateId}. | ||
function _setTemplateId(uint256 templateId_) internal virtual { | ||
_templateId = templateId_; | ||
} | ||
|
||
/** | ||
* @dev See {AbstractSigner-_rawSignatureValidation}. Validates a raw signature by: | ||
* | ||
* 1. Decoding the email authentication message from the signature | ||
* 2. Verifying the hash matches the command parameters | ||
* 3. Checking the template ID matches | ||
* 4. Validating the account salt | ||
* 5. Verifying the email proof | ||
*/ | ||
function _rawSignatureValidation( | ||
bytes32 hash, | ||
bytes calldata signature | ||
) internal view virtual override returns (bool) { | ||
// Check if the signature is long enough to contain the EmailAuthMsg | ||
// The minimum length is 512 bytes (initial part + pointer offsets) | ||
// - `templateId` is a uint256 (32 bytes). | ||
// - `commandParams` is a dynamic array of bytes32 (32 bytes offset). | ||
// - `skippedCommandPrefixSize` is a uint256 (32 bytes). | ||
// - `proof` is a struct with the following fields (32 bytes offset): | ||
// - `domainName` is a dynamic string (32 bytes offset). | ||
// - `publicKeyHash` is a bytes32 (32 bytes). | ||
// - `timestamp` is a uint256 (32 bytes). | ||
// - `maskedCommand` is a dynamic string (32 bytes offset). | ||
// - `emailNullifier` is a bytes32 (32 bytes). | ||
// - `accountSalt` is a bytes32 (32 bytes). | ||
// - `isCodeExist` is a boolean, so its length is 1 byte padded to 32 bytes. | ||
// - `proof` is a dynamic bytes (32 bytes offset). | ||
// There are 128 bytes for the EmailAuthMsg type and 256 bytes for the proof. | ||
// Considering all dynamic elements are empty (i.e. `commandParams` = [], `domainName` = "", `maskedCommand` = "", `proof` = []), | ||
// then we have 128 bytes for the EmailAuthMsg type, 256 bytes for the proof and 4 * 32 for the length of the dynamic elements. | ||
// So the minimum length is 128 + 256 + 4 * 32 = 512 bytes. | ||
if (signature.length < 512) return false; | ||
EmailAuthMsg memory emailAuthMsg = abi.decode(signature, (EmailAuthMsg)); | ||
return (abi.decode(emailAuthMsg.commandParams[0], (bytes32)) == hash && | ||
emailAuthMsg.templateId == templateId() && | ||
emailAuthMsg.proof.accountSalt == accountSalt() && | ||
emailAuthMsg.isValidZKEmail(DKIMRegistry(), verifier()) == ZKEmailUtils.EmailProofError.NoError); | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.