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.

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.

Functions
  • 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:

Functions
  • verify(challenge, auth, qx, qy)

  • verify(challenge, auth, qx, qy, requireUV)

  • tryDecodeAuth(input)

Internal Variables
  • 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:

  1. Type is "webauthn.get" (see {_validateExpectedTypeHash})

  2. Challenge matches the expected value (see {_validateChallenge})

  3. Cryptographic signature is valid for the given public key

  4. confirming physical user presence during authentication

  5. (if requireUV is true) confirming stronger user authentication (biometrics/PIN)

  6. 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.

bytes1 AUTH_DATA_FLAGS_BE internal constant

Bit 3 of the authenticator data flags: "Backup Eligibility" bit.

bytes1 AUTH_DATA_FLAGS_BS internal constant

Bit 4 of the authenticator data flags: "Backup State" 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.
Functions
  • accountSalt()

  • DKIMRegistry()

  • verifier()

  • templateId()

  • _setAccountSalt(accountSalt_)

  • _setDKIMRegistry(registry_)

  • _setVerifier(verifier_)

  • _setTemplateId(templateId_)

  • _rawSignatureValidation(hash, signature)

Errors
  • 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.

templateId() → uint256 public

The command template of the sign hash command.

_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:

  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

InvalidEmailProof(enum ZKEmailUtils.EmailProofError err) error

Proof verification error.

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.
Functions
  • _rawSignatureValidation(hash, signature)

SignerP256
  • _setSigner(qx, qy)

  • signer()

Errors
SignerP256
  • 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);
  }
Functions
  • 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
    }
}));

_decodeKey(bytes key) → contract IDKIMRegistry registry, bytes32 accountSalt, contract IVerifier verifier, uint256 templateId internal

Decodes the key into its components.

bytes memory key = abi.encode(registry, accountSalt, verifier, templateId);

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.
Functions
  • 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