signature-malleabilitylisted
Install: claude install-skill iktok90-design/ai-smart-contract-auditor
# Signature malleability detection
## When this applies
Trigger on any of:
- Direct `ecrecover(hash, v, r, s)` calls (not via OpenZeppelin `ECDSA.recover`)
- Manual `(r, s, v)` decoding from a `bytes` signature with assembly
- Permit / meta-tx / order-book / claim flows that verify a signed digest
- Signatures used as nonces or dedup keys (e.g. `usedSig[sig] = true`)
- EIP-712 typed-data verification, or its absence where one is needed
## Detection patterns
### Missing low-s enforcement / malleable sig (HIGH if sig is a key)
```solidity
address signer = ecrecover(hash, v, r, s); // no s-range check
require(signer == expected);
usedSignature[keccak256(abi.encode(r,s,v))] = true; // ← dedup keyed on sig bytes
```
**Signal:** for any valid `(r,s,v)` the "flipped" signature `(r, n - s, v ^ 1)` recovers the *same* signer (the classic Bitcoin/Ethereum transaction-malleability class). If the signature itself is the replay key, an attacker submits the twin and bypasses dedup. Enforce `s <= secp256k1n/2` (EIP-2).
### Unchecked ecrecover returning address(0) (HIGH)
```solidity
address signer = ecrecover(hash, v, r, s);
require(signer == owner); // if owner could ever be address(0)... or no check at all
```
**Signal:** malformed inputs make `ecrecover` return `address(0)`. Any code path where the compared-against value can be `address(0)` (uninitialized mapping slot, default) authenticates an attacker with garbage. Always `require(signer != address(0))`.
### Unconstrained v (