Cryptography and Signature Pitfalls

Smart contracts use cryptography heavily — for authentication, authorization, off-chain message verification, replay protection, ownership transfer, gasless interactions, account abstraction, and bridging. The good news is that the EVM exposes only a small set of cryptographic primitives, all well-studied. The bad news is that misusing those primitives is one of the most common and consequential bug classes in the industry. The damage from a signature bug is rarely partial: typically every signed message in the system is forgeable or replayable.

This chapter covers what an auditor needs to recognize:

  • ecrecover and signature malleability. EIP-2's s-value restriction; why v matters; how malleability turns into double-spend in some contract designs.
  • EIP-191 and EIP-712. Personal sign vs. typed-data sign; domain separation; what a "good" signed message looks like.
  • Replay protection. Nonces, deadlines, chain IDs, contract addresses; the multi-axis nature of replay risk.
  • Permit and Permit2. Off-chain token approvals; the common bugs around them; the Uniswap Permit2 ecosystem.
  • BLS, Schnorr, and the EVM precompiles. What's available on mainnet today; common misuses; emerging patterns.
  • Account abstraction signatures (ERC-4337). Validation rules, signature aggregation, paymaster signatures.

Each section assumes basic familiarity with elliptic-curve cryptography but does not require deep knowledge — the goal is to make the failure modes visible without turning the chapter into a textbook.

What an Auditor Needs to Verify About Any Signature

For any contract that verifies an off-chain signature, the audit task reduces to confirming the following hold:

  1. Domain separation. The signed message includes enough context that the same signature cannot be applied somewhere else (different chain, different contract, different function, different version).
  2. Replay protection. The signed message includes a nonce or unique identifier that, once consumed, cannot be re-used.
  3. Expiration. The signed message includes a deadline that the contract enforces.
  4. Malleability resistance. The signature scheme used does not admit multiple valid signatures for the same message (or, if it does, the contract treats them as a single use).
  5. Recovery soundness. ecrecover(...) == expectedSigner is checked, and the contract rejects address(0).
  6. Hashing structure. The hash being signed is unambiguous: no parameter can be re-interpreted as another, no concatenation collisions are possible.
  7. Scheme appropriateness. The signing scheme matches the use case (ECDSA for EOA, EIP-1271 for contract wallets, account-abstraction validation for 4337).

A contract that gets all seven right is unlikely to have a signature bug. A contract that gets any of them wrong almost certainly does.

Scheme Landscape (As of 2026)

The signing schemes an auditor will encounter, by frequency:

SchemePrimary UseNotes
ECDSA (secp256k1)EOA signatures, the default in ecrecoverEIP-2 restricts s to low half; malleability still possible if not checked
EIP-191 personal_signLegacy "sign this message" UIsPrefer EIP-712 for new code
EIP-712 typed-dataModern standard for structured messagesUsed by Permit, Permit2, most DEX intents, gasless wallets
EIP-1271Contract-wallet signature verificationSmart-contract wallets (Safe, AA) sign by calling isValidSignature
ERC-6492Pre-deployed counterfactual wallet signaturesLets an undeployed wallet sign messages verifiably
secp256r1 / P-256Passkeys, biometric / WebAuthn devices, EIP-7212 precompile (post-Pectra)Increasingly relevant for account abstraction
BLS12-381Aggregated signatures, ZK rollup commitments, staking signatures, EIP-2537 precompileUsed in beacon chain consensus; appearing in app-layer designs
Schnorr / MuSigMulti-party signatures, emerging in DeFiNo native EVM precompile; usually verified by EVM-level math
EdDSA (ed25519)Cross-chain bridges from Solana, etc.; emerging EIP for precompileVerifier contracts on EVM today are expensive

Most production audits in 2026 focus on ECDSA, EIP-712, EIP-1271, Permit / Permit2, and (increasingly) ERC-4337-related validation. BLS and Schnorr remain niche but worth recognizing.

A General Audit Workflow for Signed Messages

For every signature-verifying function in scope:

  1. Trace the off-chain construction of the message: what does the signer's wallet display? What does it actually sign?
  2. Identify the on-chain hash construction: what bytes are hashed before being passed to ecrecover (or equivalent)?
  3. Check domain separation: is the chain ID included? The contract address? A typehash unique to this function?
  4. Check replay protection: nonce semantics, expiration, single-use vs. multi-use logic.
  5. Check malleability: is s validated? Is v validated? Does the contract use OpenZeppelin's ECDSA.recover (which handles these) or raw ecrecover (which does not)?
  6. Check signer authorization: is the recovered signer the correct signer for the action being authorized?
  7. Check the failure mode: what happens on invalid signature? Revert is preferred; returning false and continuing is suspicious.

The subsections that follow expand each of these into concrete patterns and findings.