DomainKeys Identified Mail (DKIM) Registry
Abstract
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.
Motivation
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:
- Account Abstraction: When combined with zkEmail, this registry enables email-based account abstraction. Users can prove ownership of their email address through DKIM signatures, allowing them to:
- Create and manage smart contract wallets
- Sign transactions using their email credentials
- Implement social recovery mechanisms
- Account Recovery: The registry facilitates secure account recovery mechanisms:
- Users can recover their wallet access by on-chain proving email ownership
- The process is trustless and secure due to the cryptographic nature of DKIM
Specification
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.
Interface
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);
}
Domain Hash
The domainHash
parameter MUST be the hash of the lowercase domain name or subdomain name. For example:
- For the domain “example.com” using keccak256:
domainHash = keccak256(bytes("example.com"))
- For the subdomain “mail.example.com” using keccak256:
domainHash = keccak256(bytes("mail.example.com"))
The registry MUST treat each domain and subdomain as a distinct entity. This means that:
- A key hash registered for “example.com” does not automatically apply to its subdomains
- Each subdomain can have its own independent DKIM key hash registration
- The full domain name (including subdomain if present) must be hashed as a single string
Key Hash
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.
DKIM Public Key Specification
The DKIM public key MUST follow the format specified in RFC 6376. Here are the key requirements:
- Key Format:
- The public key MUST be in the format specified in the DKIM DNS record (p=PUBLIC_KEY)
- The key MUST be base64 encoded
- The key MUST NOT include the PEM headers or any other formatting
- The key MUST be the raw public key data as specified in the DKIM DNS record
- Key Requirements:
- The key MUST be a valid RSA public key
- The key MUST be in the format as published in the domain’s DNS TXT record
- The key MUST be the exact value from the p= parameter in the DKIM DNS record
- The key MUST NOT include any whitespace or line breaks
- Key Registration Process:
- Obtain the DKIM public key from the domain’s DNS TXT record
- Extract the value from the p= parameter
- Calculate the hash of the raw public key using the implementation’s chosen hash function
- Register the hash in the DKIM registry
- Key Validation:
- The registry MUST verify that the provided key hash corresponds to a valid DKIM public key
- The key MUST be in the correct format as specified in RFC 6376
- The key MUST be the exact value as published in the domain’s DNS record
-
Example with keccak256:
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:
- Extract the public key value from the p= parameter:
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDwIRP/UC3SBsEmGqZ9ZJW3/DkMoGeLnQg1fWn7/zYtIxN2SnFCjxOCKG9v3b4jYfcTNh5ijSsq631uBItLa7od+v/RtdC2UzJ1lWT947qR+Rcac2gbto/NMqJ0fzfVjH4OuKhitdY9tf6mcwGjaNBcWToIMmPSPDdQPNUYckcQ2QIDAQAB
-
Calculate the keccak256 hash of the public key:
bytes32 keyHash = keccak256(bytes("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDwIRP/UC3SBsEmGqZ9ZJW3/DkMoGeLnQg1fWn7/zYtIxN2SnFCjxOCKG9v3b4jYfcTNh5ijSsq631uBItLa7od+v/RtdC2UzJ1lWT947qR+Rcac2gbto/NMqJ0fzfVjH4OuKhitdY9tf6mcwGjaNBcWToIMmPSPDdQPNUYckcQ2QIDAQAB"));
- Calculate the domain hash:
bytes32 domainHash = keccak256(bytes("example.org"));
- Register the key hash in the registry:
registry.setKeyHash(domainHash, keyHash);
Events
KeyHashRegistered
This event MUST be emitted when a new DKIM public key hash is registered for a domain.
event KeyHashRegistered(bytes32 domainHash, bytes32 keyHash)
KeyHashRevoked
This event MUST be emitted when a DKIM public key hash is revoked for a domain.
event KeyHashRevoked(bytes32 domainHash)
Functions
isKeyHashValid
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)
Rationale
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.
Backwards Compatibility
This EIP introduces a new interface and does not affect existing contracts or standards.
Reference Implementation
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);
}
}
Security Considerations
- Domain owners must ensure they have control over their private keys and domain names.
- The registry implementation should include proper access control mechanisms to prevent unauthorized registrations.
- The registry should implement a mechanism to handle key rotation and revocation.
- Implementations should consider rate limiting to prevent spam registrations.
- Registries should select the
domainHash
andkeyHash
algorithms carefully. Upgrading the hash function must be thoughtfully planned.
Copyright
Copyright and related rights waived via CC0.