Skip to content

Commit

Permalink
Update of UserOverrideableDKIMRegistry (#233)
Browse files Browse the repository at this point in the history
* Update UserOverridableDKIMRegistry

* Add comments to UserOverrideableDKIMRegistry.sol and remove comment outted codes in UserOverrideableDKIMRegistry.t.sol

* Update solc version

* Add IDKIMRegistryExt

* Remove IDKIMRegistryExt

* Update yarn.lock

* Add one require to isDKIMPublicKeyHashValid in UserOverridableDKIMRegistry

* Update zk-regex version

* update versions

* Update helper version

---------

Co-authored-by: wshino <[email protected]>
  • Loading branch information
SoraSuegami and wshino authored Oct 25, 2024
1 parent 44b3509 commit 3b3c808
Show file tree
Hide file tree
Showing 9 changed files with 1,957 additions and 1,542 deletions.
6 changes: 3 additions & 3 deletions packages/circuits/package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"name": "@zk-email/circuits",
"version": "6.2.0",
"version": "6.3.0",
"license": "MIT",
"scripts": {
"publish": "yarn npm publish --access=public",
"test": "NODE_OPTIONS=--max_old_space_size=8192 jest --runInBand --detectOpenHandles --forceExit --verbose tests"
},
"dependencies": {
"@zk-email/zk-regex-circom": "^2.1.0",
"@zk-email/zk-regex-circom": "^2.3.0",
"circomlib": "^2.0.5"
},
"devDependencies": {
Expand Down Expand Up @@ -43,4 +43,4 @@
]
]
}
}
}
2 changes: 1 addition & 1 deletion packages/contracts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ For a detailed overview of its functionalities, please refer to the source file:
## UserOverrideableDKIMRegistry.sol

`UserOverrideableDKIMRegistry.sol` is a Solidity contract within the `@zk-email/contracts` package.
This functions similarly to [DKIMRegistry](./DKIMRegistry.sol), but it allows users to set their own public keys. Even if the main authorizer, who is the contract owner, has already approved a public key, the user's signature is still required for setting it. Additionally, the public key can be revoked by the signature of either the user or the main authorizer alone.
This functions similarly to [DKIMRegistry](./DKIMRegistry.sol), but it allows users to set their own public keys. Even if the main authorizer, who is the contract owner, has already approved a public key, the user's signature is still required for setting it until the predetermined delay time has passed. Additionally, the public key can be revoked by the signature of either the user or the main authorizer alone.

[UserOverrideableDKIMRegistry.sol](./UserOverrideableDKIMRegistry.sol)

Expand Down
131 changes: 107 additions & 24 deletions packages/contracts/UserOverrideableDKIMRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,50 +7,88 @@ import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

/**
A Registry that store the hash(dkim_public_key) for each domain
The hash is calculated by taking Poseidon of DKIM key split into 9 chunks of 242 bits each
https://zkrepl.dev/?gist=43ce7dce2466c63812f6efec5b13aa73 can be used to generate the public key hash.
The same code is used in EmailVerifier.sol
Input is DKIM pub key split into 17 chunks of 121 bits. You can use `helpers` package to fetch/split DKIM keys
A Registry that store the hash(dkim_public_key) for each domain and each user.
This functions similarly to [DKIMRegistry](./DKIMRegistry.sol), but it allows users to set their own public keys.
Even if the main authorizer, who is the contract owner, has already approved a public key, the user's signature is still required for setting it until the predetermined delay time (`setTimestampDelay`) has passed.
Additionally, the public key can be revoked by the signature of either the user or the main authorizer alone.
*/
contract UserOverrideableDKIMRegistry is IDKIMRegistry, Ownable {
contract UserOverrideableDKIMRegistry is
IDKIMRegistry,
OwnableUpgradeable,
UUPSUpgradeable
{
using Strings for *;
using ECDSA for *;

/// @notice Emitted when a DKIM public key hash is successfully set.
event DKIMPublicKeyHashRegistered(
string domainName,
bytes32 publicKeyHash,
address register
string indexed domainName,
bytes32 indexed publicKeyHash,
address indexed authorizer
);

/// @notice Emitted when a DKIM public key hash is successfully revoked.
event DKIMPublicKeyHashRevoked(
bytes32 indexed publicKeyHash,
address indexed authorizer
);
event DKIMPublicKeyHashRevoked(bytes32 publicKeyHash, address register);
event DKIMPublicKeyHashReactivated(bytes32 publicKeyHash, address register);

// Main authorizer address.
/// @notice Emitted when a DKIM public key hash is successfully reactivated.
event DKIMPublicKeyHashReactivated(
bytes32 indexed publicKeyHash,
address indexed authorizer
);

/// @notice Main authorizer address.
address public mainAuthorizer;

// Mapping from domain name to DKIM public key hash
/// @notice Time delay until a DKIM public key hash set by the main authorizer is enabled
uint public setTimestampDelay;

/// @notice DKIM public key hashes that are set
mapping(string => mapping(bytes32 => mapping(address => bool)))
public dkimPublicKeyHashes;

// DKIM public that are revoked (eg: in case of private key compromise)
/// @notice DKIM public key hashes that are revoked (eg: in case of private key compromise)
mapping(bytes32 => mapping(address => bool))
public revokedDKIMPublicKeyHashes;

// DKIM public that are reactivated (eg: in case that a malicious `mainAuthorizer` revokes a valid public key but a user reactivates it.)
/// @notice DKIM public key hashes that are reactivated (eg: in case that a malicious `mainAuthorizer` revokes a valid public key but a user reactivates it.)
mapping(bytes32 => mapping(address => bool))
public reactivatedDKIMPublicKeyHashes;

/// @notice The timestamp from which the set DKIM public key hash is enabled
mapping(bytes32 => uint) public enabledTimeOfDKIMPublicKeyHash;

string public constant SET_PREFIX = "SET:";
string public constant REVOKE_PREFIX = "REVOKE:";
string public constant REACTIVATE_PREFIX = "REACTIVATE";
string public constant REACTIVATE_PREFIX = "REACTIVATE:";

constructor(address _owner, address _mainAuthorizer) Ownable(_owner) {
constructor() {}

/// @notice Initializes the contract with a predefined signer and deploys a new DKIMRegistry.
/// @param _initialOwner The address of the initial owner of the contract.
/// @param _mainAuthorizer The address of the main authorizer.
/// @param _setTimestampDelay The time delay until a DKIM public key hash set by the main authorizer is enabled.
function initialize(
address _initialOwner,
address _mainAuthorizer,
uint _setTimestampDelay
) public initializer {
__Ownable_init(_initialOwner);
mainAuthorizer = _mainAuthorizer;
setTimestampDelay = _setTimestampDelay;
}

/// @notice Checks if a DKIM public key hash is valid for a given domain.
/// @param domainName The domain name for which the DKIM public key hash is being checked.
/// @param publicKeyHash The hash of the DKIM public key to be checked.
/// @return bool True if the DKIM public key hash is valid, false otherwise.
/// @dev This function returns true if the owner of the given `msg.sender` approves the public key hash before `enabledTimeOfDKIMPublicKeyHash` and neither `mainAuthorizer` nor the owner of `msg.sender` revokes the public key hash. However, after `enabledTimeOfDKIMPublicKeyHash`, only one of their approvals is required. In addition, if the public key hash is reactivated by the owner of `msg.sender`, the public key hash revoked only by `mainAuthorizer` is considered valid.
function isDKIMPublicKeyHashValid(
string memory domainName,
bytes32 publicKeyHash
Expand All @@ -60,6 +98,14 @@ contract UserOverrideableDKIMRegistry is IDKIMRegistry, Ownable {
isDKIMPublicKeyHashValid(domainName, publicKeyHash, ownerOfSender);
}

/// @notice Checks if a DKIM public key hash is valid for a given domain.
/// @param domainName The domain name for which the DKIM public key hash is being checked.
/// @param publicKeyHash The hash of the DKIM public key to be checked.
/// @param authorizer The address of the expected authorizer
/// @return bool True if the DKIM public key hash is valid, false otherwise.
/// @dev This function returns true if 1) at least the given `authorizer` approves the public key hash before `enabledTimeOfDKIMPublicKeyHash` and 2) neither `mainAuthorizer` nor `authorizer` revokes the public key hash. However, after `enabledTimeOfDKIMPublicKeyHash`, only one of their approvals is required. In addition, if the public key hash is reactivated by the `authorizer`, the public key hash revoked only by `mainAuthorizer` is considered valid.
/// @dev The domain name, public key hash, and authorizer address must not be zero.
/// @dev The authorizer address cannot be the mainAuthorizer.
function isDKIMPublicKeyHashValid(
string memory domainName,
bytes32 publicKeyHash,
Expand All @@ -68,6 +114,10 @@ contract UserOverrideableDKIMRegistry is IDKIMRegistry, Ownable {
require(bytes(domainName).length > 0, "domain name cannot be zero");
require(publicKeyHash != bytes32(0), "public key hash cannot be zero");
require(authorizer != address(0), "authorizer address cannot be zero");
require(
authorizer != mainAuthorizer,
"authorizer cannot be mainAuthorizer"
);
uint256 revokeThreshold = _computeRevokeThreshold(
publicKeyHash,
authorizer
Expand Down Expand Up @@ -107,6 +157,10 @@ contract UserOverrideableDKIMRegistry is IDKIMRegistry, Ownable {
require(bytes(domainName).length > 0, "domain name cannot be zero");
require(publicKeyHash != bytes32(0), "public key hash cannot be zero");
require(authorizer != address(0), "authorizer address cannot be zero");
require(
dkimPublicKeyHashes[domainName][publicKeyHash][authorizer] == false,
"public key hash is already set"
);
require(
revokedDKIMPublicKeyHashes[publicKeyHash][authorizer] == false,
"public key hash is already revoked"
Expand Down Expand Up @@ -136,6 +190,11 @@ contract UserOverrideableDKIMRegistry is IDKIMRegistry, Ownable {
}

dkimPublicKeyHashes[domainName][publicKeyHash][authorizer] = true;
if (authorizer == mainAuthorizer) {
enabledTimeOfDKIMPublicKeyHash[publicKeyHash] =
block.timestamp +
setTimestampDelay;
}

emit DKIMPublicKeyHashRegistered(domainName, publicKeyHash, authorizer);
}
Expand Down Expand Up @@ -232,6 +291,18 @@ contract UserOverrideableDKIMRegistry is IDKIMRegistry, Ownable {
emit DKIMPublicKeyHashRevoked(publicKeyHash, authorizer);
}

/**
* @notice Reactivates a DKIM public key hash.
* @dev This function allows an authorized user or a contract to reactivate a DKIM public key hash that was revoked by the main authorizer.
* @param domainName The domain name associated with the DKIM public key hash.
* @param publicKeyHash The hash of the DKIM public key to be reactivated.
* @param authorizer The address of the authorizer who can reactivate the DKIM public key hash.
* @param signature The signature proving the authorization to reactivate the DKIM public key hash.
* @custom:require The domain name, public key hash, and authorizer address must not be zero.
* @custom:require The public key hash must be revoked by the main authorizer.
* @custom:require The signature must be valid according to EIP-1271 if the authorizer is a contract, or ECDSA if the authorizer is an EOA.
* @custom:event DKIMPublicKeyHashReactivated Emitted when a DKIM public key hash is successfully reactivated.
*/
function reactivateDKIMPublicKeyHash(
string memory domainName,
bytes32 publicKeyHash,
Expand All @@ -241,14 +312,14 @@ contract UserOverrideableDKIMRegistry is IDKIMRegistry, Ownable {
require(bytes(domainName).length > 0, "domain name cannot be zero");
require(publicKeyHash != bytes32(0), "public key hash cannot be zero");
require(authorizer != address(0), "authorizer address cannot be zero");
require(
reactivatedDKIMPublicKeyHashes[publicKeyHash][authorizer] == false,
"public key hash is already reactivated"
);
require(
authorizer != mainAuthorizer,
"mainAuthorizer cannot reactivate the public key hash"
);
require(
reactivatedDKIMPublicKeyHashes[publicKeyHash][authorizer] == false,
"public key hash is already reactivated"
);
require(
_computeRevokeThreshold(publicKeyHash, authorizer) == 1,
"revoke threshold must be one"
Expand Down Expand Up @@ -301,7 +372,7 @@ contract UserOverrideableDKIMRegistry is IDKIMRegistry, Ownable {
return
string.concat(
prefix,
";domain=",
"domain=",
domainName,
";public_key_hash=",
uint256(publicKeyHash).toHexString(),
Expand All @@ -319,7 +390,13 @@ contract UserOverrideableDKIMRegistry is IDKIMRegistry, Ownable {
dkimPublicKeyHashes[domainName][publicKeyHash][mainAuthorizer] ==
true
) {
threshold += 1;
if (
block.timestamp < enabledTimeOfDKIMPublicKeyHash[publicKeyHash]
) {
threshold += 1;
} else {
threshold += 2;
}
}
if (
dkimPublicKeyHashes[domainName][publicKeyHash][authorizer] == true
Expand Down Expand Up @@ -355,4 +432,10 @@ contract UserOverrideableDKIMRegistry is IDKIMRegistry, Ownable {
) internal pure returns (bool) {
return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b));
}

/// @notice Upgrade the implementation of the proxy.
/// @param newImplementation Address of the new implementation.
function _authorizeUpgrade(
address newImplementation
) internal override onlyOwner {}
}
2 changes: 1 addition & 1 deletion packages/contracts/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ src = './'
out = 'out'
allow_paths = ['../../node_modules']
libs = ['../../node_modules']
solc_version = '0.8.23'
solc_version = '0.8.26'
5 changes: 3 additions & 2 deletions packages/contracts/package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
{
"name": "@zk-email/contracts",
"version": "6.2.0",
"version": "6.3.0",
"license": "MIT",
"scripts": {
"build": "forge build",
"publish": "yarn npm publish --access=public"
},
"dependencies": {
"@openzeppelin/contracts": "^5.0.0",
"@openzeppelin/contracts-upgradeable": "^5.0.0",
"dotenv": "^16.3.1"
},
"files": [
Expand All @@ -19,4 +20,4 @@
"devDependencies": {
"forge-std": "https://github.com/foundry-rs/forge-std"
}
}
}
1 change: 1 addition & 0 deletions packages/contracts/remappings.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@openzeppelin/contracts=../../node_modules/@openzeppelin/contracts
@openzeppelin=../../node_modules/@openzeppelin/contracts
@openzeppelin/contracts-upgradeable/=../../node_modules/@openzeppelin/contracts-upgradeable
forge-std=../../node_modules/forge-std

Loading

0 comments on commit 3b3c808

Please sign in to comment.