Cryptography
This document is better viewed at https://docs.openzeppelin.com/community-contracts/utils/cryptography |
A collection of contracts and libraries that implement various signature validation schemes and cryptographic primitives. These utilities enable secure authentication, multisignature operations, and advanced cryptographic operations in smart contracts.
-
ZKEmailUtils
: Library for ZKEmail signature validation utilities, enabling email-based authentication through zero-knowledge proofs. -
WebAuthn
: Library for verifying WebAuthn Authentication Assertions. -
SignerZKEmail
: Implementation of an AbstractSigner that enables email-based authentication through zero-knowledge proofs. -
SignerWebAuthn
: Implementation of SignerP256 that supports WebAuthn authentication assertions. -
ERC7913ZKEmailVerifier
,ERC7913WebAuthnVerifier
: Ready to use ERC-7913 signature verifiers for ZKEmail and WebAuthn.
Utils
ZKEmailUtils
import "@openzeppelin/community-contracts/utils/cryptography/ZKEmailUtils.sol";
Library for ZKEmail signature validation utilities.
ZKEmail is a protocol that enables email-based authentication and authorization for smart contracts using zero-knowledge proofs. It allows users to prove ownership of an email address without revealing the email content or private keys.
The validation process involves several key components:
-
A DKIMRegistry (DomainKeys Identified Mail) verification mechanism to ensure the email was sent from a valid domain. Defined by an
IDKIMRegistry
interface. -
A command template validation mechanism to ensure the email command matches the expected format and parameters.
-
A zero-knowledge proof verification mechanism to ensure the email was actually sent and received without revealing its contents. Defined by an
IVerifier
interface.
-
isValidZKEmail(emailAuthMsg, dkimregistry, verifier)
-
isValidZKEmail(emailAuthMsg, dkimregistry, verifier, template)
-
isValidZKEmail(emailAuthMsg, dkimregistry, verifier, template, stringCase)
isValidZKEmail(struct EmailAuthMsg emailAuthMsg, contract IDKIMRegistry dkimregistry, contract IVerifier verifier) → enum ZKEmailUtils.EmailProofError
internal
Variant of isValidZKEmail
that validates the ["signHash", "{uint}"]
command template.
isValidZKEmail(struct EmailAuthMsg emailAuthMsg, contract IDKIMRegistry dkimregistry, contract IVerifier verifier, string[] template) → enum ZKEmailUtils.EmailProofError
internal
Validates a ZKEmail authentication message.
This function takes an email authentication message, a DKIM registry contract, and a verifier contract
as inputs. It performs several validation checks and returns a tuple containing a boolean success flag
and an EmailProofError
if validation failed. Returns {EmailProofError.NoError} if all validations pass,
or false with a specific EmailProofError
indicating which validation check failed.
Attempts to validate the command for all possible string Case values.
|
isValidZKEmail(struct EmailAuthMsg emailAuthMsg, contract IDKIMRegistry dkimregistry, contract IVerifier verifier, string[] template, enum ZKEmailUtils.Case stringCase) → enum ZKEmailUtils.EmailProofError
internal
Variant of isValidZKEmail
that validates a template with a specific string Case
.
Useful for templates with Ethereum address matchers (i.e. {ethAddr}
), which are case-sensitive (e.g., ["someCommand", "{address}"]
).
WebAuthn
import "@openzeppelin/community-contracts/utils/cryptography/WebAuthn.sol";
Library for verifying WebAuthn Authentication Assertions.
WebAuthn enables strong authentication for smart contracts using P256 as an alternative to traditional secp256k1 ECDSA signatures. This library verifies signatures generated during WebAuthn authentication ceremonies as specified in the WebAuthn Level 2 standard.
For blockchain use cases, the following WebAuthn validations are intentionally omitted:
-
Origin validation: Origin verification in
clientDataJSON
is omitted as blockchain contexts rely on authenticator and dapp frontend enforcement. Standard authenticators implement proper origin validation. -
RP ID hash validation: Verification of
rpIdHash
in authenticatorData against expected RP ID hash is omitted. This is typically handled by platform-level security measures. Including an expiry timestamp in signed data is recommended for enhanced security. -
Signature counter: Verification of signature counter increments is omitted. While useful for detecting credential cloning, on-chain operations typically include nonce protection, making this check redundant.
-
Extension outputs: Extension output value verification is omitted as these are not essential for core authentication security in blockchain applications.
-
Attestation: Attestation object verification is omitted as this implementation focuses on authentication (
webauthn.get
) rather than registration ceremonies.
Inspired by:
-
verify(challenge, auth, qx, qy)
-
verify(challenge, auth, qx, qy, requireUV)
-
tryDecodeAuth(input)
-
bytes1 constant AUTH_DATA_FLAGS_UP
-
bytes1 constant AUTH_DATA_FLAGS_UV
-
bytes1 constant AUTH_DATA_FLAGS_BE
-
bytes1 constant AUTH_DATA_FLAGS_BS
verify(bytes challenge, struct WebAuthn.WebAuthnAuth auth, bytes32 qx, bytes32 qy) → bool
internal
Performs standard verification of a WebAuthn Authentication Assertion.
verify(bytes challenge, struct WebAuthn.WebAuthnAuth auth, bytes32 qx, bytes32 qy, bool requireUV) → bool
internal
Performs verification of a WebAuthn Authentication Assertion. This variants allow the caller to select whether of not to require the UV flag (step 17).
Verifies:
-
Type is "webauthn.get" (see {_validateExpectedTypeHash})
-
Challenge matches the expected value (see {_validateChallenge})
-
Cryptographic signature is valid for the given public key
-
confirming physical user presence during authentication
-
(if
requireUV
is true) confirming stronger user authentication (biometrics/PIN) -
Backup Eligibility (
BE
) and Backup State (BS) bits relationship is valid
tryDecodeAuth(bytes input) → bool success, struct WebAuthn.WebAuthnAuth auth
internal
Verifies that calldata bytes (input
) represents a valid WebAuthnAuth
object. If encoding is valid,
returns true and the calldata view at the object. Otherwise, returns false and an invalid calldata object.
The returned auth object should not be accessed if success is false. Trying to access the data may
cause revert/panic.
|
bytes1 AUTH_DATA_FLAGS_UP
internal constant
Bit 0 of the authenticator data flags: "User Present" bit.
bytes1 AUTH_DATA_FLAGS_UV
internal constant
Bit 2 of the authenticator data flags: "User Verified" bit.
Abstract Signers
SignerZKEmail
import "@openzeppelin/community-contracts/utils/cryptography/signers/SignerZKEmail.sol";
Implementation of {AbstractSigner} using 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:
contract MyAccountZKEmail is Account, SignerZKEmail, Initializable {
function initialize(
bytes32 accountSalt,
IDKIMRegistry registry,
IVerifier verifier,
uint256 templateId
) public initializer {
// Will revert if the signer is already initialized
_setAccountSalt(accountSalt);
_setDKIMRegistry(registry);
_setVerifier(verifier);
_setTemplateId(templateId);
}
}
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.
|
-
accountSalt()
-
DKIMRegistry()
-
verifier()
-
templateId()
-
_setAccountSalt(accountSalt_)
-
_setDKIMRegistry(registry_)
-
_setVerifier(verifier_)
-
_setTemplateId(templateId_)
-
_rawSignatureValidation(hash, signature)
-
InvalidEmailProof(err)
accountSalt() → bytes32
public
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 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.
DKIMRegistry() → contract IDKIMRegistry
public
An instance of the DKIM registry contract. See DKIM Verification.
verifier() → contract IVerifier
public
An instance of the Verifier contract. See ZK Proofs.
_setAccountSalt(bytes32 accountSalt_)
internal
Set the accountSalt
.
_setDKIMRegistry(contract IDKIMRegistry registry_)
internal
Set the DKIMRegistry
contract address.
_setVerifier(contract IVerifier verifier_)
internal
Set the verifier
contract address.
_setTemplateId(uint256 templateId_)
internal
Set the command’s templateId
.
_rawSignatureValidation(bytes32 hash, bytes signature) → bool
internal
See {AbstractSigner-_rawSignatureValidation}. Validates a raw signature by:
-
Decoding the email authentication message from the signature
-
Verifying the hash matches the command parameters
-
Checking the template ID matches
-
Validating the account salt
-
Verifying the email proof
SignerWebAuthn
import "@openzeppelin/community-contracts/utils/cryptography/signers/SignerWebAuthn.sol";
Implementation of {SignerP256} that supports WebAuthn authentication assertions.
This contract enables signature validation using WebAuthn authentication assertions, leveraging the P256 public key stored in the contract. It allows for both WebAuthn and raw P256 signature validation, providing compatibility with both signature types.
The signature is expected to be an abi-encoded WebAuthn.WebAuthnAuth
struct.
Example usage:
contract MyAccountWebAuthn is Account, SignerWebAuthn, Initializable {
function initialize(bytes32 qx, bytes32 qy) public initializer {
_setSigner(qx, qy);
}
}
Failing to call {_setSigner} either during construction (if used standalone) or during initialization (if used as a clone) may leave the signer either front-runnable or unusable. |
-
_rawSignatureValidation(hash, signature)
-
_setSigner(qx, qy)
-
signer()
-
SignerP256InvalidPublicKey(qx, qy)
_rawSignatureValidation(bytes32 hash, bytes signature) → bool
internal
Validates a raw signature using the WebAuthn authentication assertion.
In case the signature can’t be validated, it falls back to the
{SignerP256-_rawSignatureValidation} method for raw P256 signature validation by passing
the raw r
and s
values from the signature.
Verifiers
ERC7913ZKEmailVerifier
import "@openzeppelin/community-contracts/utils/cryptography/verifiers/ERC7913ZKEmailVerifier.sol";
ERC-7913 signature verifier that supports ZKEmail accounts.
This contract verifies signatures produced through ZKEmail’s zero-knowledge proofs which allows users to authenticate using their email addresses.
The key decoding logic is customizable: users may override the _decodeKey
function
to enforce restrictions or validation on the decoded values (e.g., requiring a specific
verifier, templateId, or registry). To remain compliant with ERC-7913’s statelessness,
it is recommended to enforce such restrictions using immutable variables only.
Example of overriding _decodeKey to enforce a specific verifier, registry, (or templateId):
function _decodeKey(bytes calldata key) internal view override returns (
IDKIMRegistry registry,
bytes32 accountSalt,
IVerifier verifier,
uint256 templateId
) {
(registry, accountSalt, verifier, templateId) = super._decodeKey(key);
require(verifier == _verifier, "Invalid verifier");
require(registry == _registry, "Invalid registry");
return (registry, accountSalt, verifier, templateId);
}
-
verify(key, hash, signature)
-
_decodeKey(key)
verify(bytes key, bytes32 hash, bytes signature) → bytes4
public
Verifies a zero-knowledge proof of an email signature validated by a DKIMRegistry
contract.
The key format is ABI-encoded (IDKIMRegistry, bytes32, IVerifier, uint256) where:
-
IDKIMRegistry: The registry contract that validates DKIM public key hashes
-
bytes32: The account salt that uniquely identifies the user’s email address
-
IVerifier: The verifier contract instance for ZK proof verification.
-
uint256: The template ID for the command
See _decodeKey
for the key encoding format.
The signature is an ABI-encoded {ZKEmailUtils-EmailAuthMsg} struct containing the command parameters, template ID, and proof details.
Signature encoding:
bytes memory signature = abi.encode(EmailAuthMsg({
templateId: 1,
commandParams: [hash],
proof: {
domainName: "example.com", // The domain name of the email sender
publicKeyHash: bytes32(0x...), // Hash of the DKIM public key used to sign the email
timestamp: block.timestamp, // When the email was sent
maskedCommand: "Sign hash", // The command being executed, with sensitive data masked
emailNullifier: bytes32(0x...), // Unique identifier for the email to prevent replay attacks
accountSalt: bytes32(0x...), // Unique identifier derived from email and account code
isCodeExist: true, // Whether the account code exists in the proof
proof: bytes(0x...) // The zero-knowledge proof verifying the email's authenticity
}
}));
ERC7913WebAuthnVerifier
import "@openzeppelin/community-contracts/utils/cryptography/verifiers/ERC7913WebAuthnVerifier.sol";
ERC-7913 signature verifier that supports WebAuthn authentication assertions.
This verifier enables the validation of WebAuthn signatures using P256 public keys.
The key is expected to be a 64-byte concatenation of the P256 public key coordinates (qx || qy).
The signature is expected to be an abi-encoded WebAuthn.WebAuthnAuth
struct.
Uses {WebAuthn-verifyMinimal} for signature verification, which performs the essential WebAuthn checks: type validation, challenge matching, and cryptographic signature verification.
Wallets that may require default P256 validation may install a P256 verifier separately. |
-
verify(key, hash, signature)
verify(bytes key, bytes32 hash, bytes signature) → bytes4
public
Verifies signature
as a valid signature of hash
by key
.
MUST return the bytes4 magic value IERC7913SignatureVerifier.verify.selector if the signature is valid. SHOULD return 0xffffffff or revert if the signature is not valid. SHOULD return 0xffffffff or revert if the key is empty