Documentation Index
Fetch the complete documentation index at: https://docs.webacy.com/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The Digital Signature Module analyzes how contracts handle cryptographic signatures, detecting vulnerabilities that could allow signature replay attacks, forgery, or unauthorized access.
What Are Digital Signatures?
Digital signatures in blockchain provide:
- Authentication: Proof that a message came from a specific address
- Integrity: Assurance the message hasn’t been altered
- Non-repudiation: Signer cannot deny signing
// Basic signature verification
function verify(
bytes32 messageHash,
bytes memory signature,
address expectedSigner
) public pure returns (bool) {
return recoverSigner(messageHash, signature) == expectedSigner;
}
Common Vulnerabilities
Signature Replay Attack
Using the same signature multiple times.
// VULNERABLE: No replay protection
function claim(uint256 amount, bytes memory signature) public {
bytes32 hash = keccak256(abi.encodePacked(msg.sender, amount));
require(verify(hash, signature, signer), "Invalid signature");
_mint(msg.sender, amount); // Can be called multiple times!
}
Attack: User claims rewards repeatedly with the same signature.
Cross-Chain Replay
Signature valid on multiple chains.
// VULNERABLE: No chain ID in signature
function execute(address to, uint256 value, bytes memory signature) public {
bytes32 hash = keccak256(abi.encodePacked(to, value));
require(verify(hash, signature, owner), "Invalid signature");
payable(to).transfer(value);
}
Attack: Signature from mainnet replayed on testnet or L2.
Missing Nonce
No mechanism to invalidate old signatures.
// VULNERABLE: Signatures never expire
function withdraw(uint256 amount, bytes memory signature) public {
bytes32 hash = keccak256(abi.encodePacked(msg.sender, amount));
require(verify(hash, signature, admin), "Invalid");
// No nonce - signature is valid forever
_transfer(address(this), msg.sender, amount);
}
Signature Malleability
ECDSA signatures can be modified while remaining valid.
// VULNERABLE: No malleability check
function verifySignature(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
public pure returns (address)
{
return ecrecover(hash, v, r, s); // Malleable!
}
Safe Patterns
Nonce-Based Replay Protection
// SAFE: Nonce prevents replay
mapping(address => uint256) public nonces;
function claim(uint256 amount, uint256 nonce, bytes memory signature) public {
require(nonce == nonces[msg.sender], "Invalid nonce");
bytes32 hash = keccak256(abi.encodePacked(
msg.sender,
amount,
nonce,
block.chainid, // Chain-specific
address(this) // Contract-specific
));
require(verify(hash, signature, signer), "Invalid signature");
nonces[msg.sender]++; // Increment nonce
_mint(msg.sender, amount);
}
EIP-712 Typed Data
// SAFE: Structured, typed signing
bytes32 public constant CLAIM_TYPEHASH =
keccak256("Claim(address recipient,uint256 amount,uint256 nonce)");
function claim(uint256 amount, uint256 nonce, bytes memory signature) public {
bytes32 structHash = keccak256(abi.encode(
CLAIM_TYPEHASH,
msg.sender,
amount,
nonce
));
bytes32 hash = _hashTypedDataV4(structHash); // Includes domain separator
address signer = ECDSA.recover(hash, signature);
require(signer == authorizedSigner, "Invalid signature");
require(nonce == nonces[msg.sender]++, "Invalid nonce");
_mint(msg.sender, amount);
}
OpenZeppelin ECDSA
// SAFE: Using audited library
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
using ECDSA for bytes32;
function verify(bytes32 hash, bytes memory signature) public view returns (bool) {
// Handles malleability, returns address(0) on failure
return hash.toEthSignedMessageHash().recover(signature) == trustedSigner;
}
| Tag | Severity | Description |
|---|
signature_replay | High | No replay protection detected |
missing_chainid | Medium | Cross-chain replay possible |
missing_nonce | High | Signatures never invalidated |
signature_malleability | Medium | Raw ecrecover without checks |
weak_signature_scheme | Medium | Non-standard signature verification |
API Response Example
{
"issues": [
{
"tag": "signature_replay",
"severity": "high",
"description": "Signature can be reused - no nonce or invalidation",
"location": "claim(uint256,bytes)"
},
{
"tag": "missing_chainid",
"severity": "medium",
"description": "Signature hash does not include chain ID",
"location": "execute(address,uint256,bytes)"
}
]
}
Best Practices