This EIP proposes a standard interface for registering and validating DomainKeys Identified Mail (DKIM) public key hashes on the Ethereum blockchain. The interface allows domain owners to register their DKIM public key hashes and enables third parties to verify the validity of these hashes.
The registry operates by storing hashes of both domain names and DKIM public keys, creating a mapping that enables on-chain verification of DKIM signatures. Domain owners register their DKIM public key hashes by extracting the public key from their DNS TXT records (as specified in RFC 6376), computing the hash, and submitting it to the registry. DKIM clients can then query the registry to verify that a given public key hash is authorized for a specific domain, enabling trustless email ownership verification for applications such as account abstraction and social recovery mechanisms.
With the growing adoption of Account Abstraction ERC-4337 and the emergence of ZK Email Technology, there is a need for a standardized way to verify email ownership on-chain. This EIP provides a crucial building block for these technologies by enabling the verification of DKIM signatures through on-chain registries.
This standard enables several important use cases:
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.
pragma solidity ^0.8.25;
/// @title ERC-XXX DKIM Registry Interface
/// @dev See https://eips.ethereum.org/EIPS/eip-xxx
/// Note: the ERC-165 identifier for this interface is 0xdee3d600.
interface IDKIMRegistry {
event KeyHashRegistered(bytes32 domainHash, bytes32 keyHash);
event KeyHashRevoked(bytes32 domainHash);
function isKeyHashValid(
bytes32 domainHash,
bytes32 keyHash
) external view returns (bool);
}
The domainHash
parameter MUST be the hash of the lowercase domain name or subdomain name. For example:
domainHash = keccak256(bytes("example.com"))
domainHash = keccak256(bytes("mail.example.com"))
The registry MUST treat each domain and subdomain as a distinct entity. This means that:
The keyHash
parameter MUST be a cryptographic hash of the DKIM public key. The public key should be in the standard DKIM format as specified in RFC 6376.
Implementations MAY choose any cryptographically secure hash function for computing the key hash. Common choices include keccak256, Poseidon (for zk-friendly applications) or other hash functions.
The DKIM public key MUST follow the format specified in RFC 6376. Here are the key requirements:
Given the following DKIM DNS record from RFC 6376:
$ORIGIN _domainkey.example.org.
brisbane IN TXT ("v=DKIM1; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQ"
"KBgQDwIRP/UC3SBsEmGqZ9ZJW3/DkMoGeLnQg1fWn7/zYt"
"IxN2SnFCjxOCKG9v3b4jYfcTNh5ijSsq631uBItLa7od+v"
"/RtdC2UzJ1lWT947qR+Rcac2gbto/NMqJ0fzfVjH4OuKhi"
"tdY9tf6mcwGjaNBcWToIMmPSPDdQPNUYckcQ2QIDAQAB")
```
The process to register this key would be:
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDwIRP/UC3SBsEmGqZ9ZJW3/DkMoGeLnQg1fWn7/zYtIxN2SnFCjxOCKG9v3b4jYfcTNh5ijSsq631uBItLa7od+v/RtdC2UzJ1lWT947qR+Rcac2gbto/NMqJ0fzfVjH4OuKhitdY9tf6mcwGjaNBcWToIMmPSPDdQPNUYckcQ2QIDAQAB
solidity
bytes32 keyHash = keccak256(bytes("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDwIRP/UC3SBsEmGqZ9ZJW3/DkMoGeLnQg1fWn7/zYtIxN2SnFCjxOCKG9v3b4jYfcTNh5ijSsq631uBItLa7od+v/RtdC2UzJ1lWT947qR+Rcac2gbto/NMqJ0fzfVjH4OuKhitdY9tf6mcwGjaNBcWToIMmPSPDdQPNUYckcQ2QIDAQAB"));
solidity
bytes32 domainHash = keccak256(bytes("example.org"));
solidity
registry.setKeyHash(domainHash, keyHash);
This event MUST be emitted when a new DKIM public key hash is registered for a domain.
event KeyHashRegistered(bytes32 domainHash, bytes32 keyHash)
This event MUST be emitted when a DKIM public key hash is revoked for a domain.
event KeyHashRevoked(bytes32 domainHash)
This function MUST return true
if the provided key hash is valid for the given domain hash, and false
otherwise.
function isKeyHashValid(
bytes32 domainHash,
bytes32 keyHash
) external view returns (bool)
The interface is designed to be simple and focused on the core functionality of DKIM public key hash registration and validation. The use of keccak256 hashing for both domain names and public keys ensures consistent and secure handling of the data.
The events allow for efficient tracking of key registrations and revocations, which is important for maintaining the integrity of the registry.
This EIP introduces a new interface and does not affect existing contracts or standards.
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/Ownable.sol";
import "./interfaces/IDKIMRegistry.sol";
contract DKIMRegistry is IDKIMRegistry, Ownable {
constructor(address _owner) Ownable(_owner) { }
// Mapping from hashed domain name to DKIM public key hash to enabled
mapping(bytes32 => mapping(bytes32 => bool)) private _keyHashes;
/**
* @notice Checks if a DKIM key hash is valid for a given domain
* @param domainHash The hash of the domain name
* @param keyHash The hash of the DKIM public key
* @return bool True if the key hash is valid for the domain, false otherwise
*/
function isKeyHashValid(
bytes32 domainHash,
bytes32 keyHash
) public view returns (bool) {
return _keyHashes[domainHash][keyHash];
}
/**
* @notice Sets a DKIM key hash for a domain
* @param domainHash The hash of the domain name
* @param keyHash The hash of the DKIM public key to register
* @dev Only callable by the contract owner
* @dev Cannot set zero hash as a valid key hash
*/
function setKeyHash(
bytes32 domainHash,
bytes32 keyHash
) public onlyOwner {
require(keyHash != bytes32(0), "cannot set zero hash");
_keyHashes[domainHash][keyHash] = true;
emit KeyHashRegistered(domainHash, keyHash);
}
/**
* @notice Sets multiple DKIM key hashes for a domain in a single transaction
* @param domainHash The hash of the domain name
* @param keyHashes Array of DKIM public key hashes to register
* @dev Only callable by the contract owner
* @dev Array must not be empty
* @dev Each key hash must not be zero
*/
function setKeyHashes(
bytes32 domainHash,
bytes32[] memory keyHashes
) public onlyOwner {
require(keyHashes.length > 0, "empty array");
for (uint256 i = 0; i < keyHashes.length; i++) {
setKeyHash(domainHash, keyHashes[i]);
}
}
/**
* @notice Revokes a DKIM key hash for a domain
* @param domainHash The hash of the domain name
* @param keyHash The hash of the DKIM public key to revoke
* @dev Only callable by the contract owner
* @dev Sets the key hash mapping to false, effectively revoking it
*/
function revokeKeyHash(bytes32 domainHash, bytes32 keyHash) public onlyOwner {
delete _keyHashes[domainHash][keyHash];
emit KeyHashRevoked(domainHash);
}
}
domainHash
and keyHash
algorithms carefully. Upgrading the hash function must be thoughtfully planned.Copyright and related rights waived via CC0.