# Signature Malleability

> Detect ECDSA signature malleability and ecrecover pitfalls — missing low-s (EIP-2) enforcement, unconstrained v, unchecked address(0) from ecrecover, replay across chainId/contract from a missing EIP-712 domain separator, EIP-2098 compact-signature confusion, and signature reuse. Activate whenever code calls ecrecover directly, parses (r,s,v) from bytes, or verifies signed messages without OpenZe…

- **Type:** Skill
- **Install:** `agentstack add skill-omermaksutii-rugproof-signature-malleability`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [omermaksutii](https://agentstack.voostack.com/s/omermaksutii)
- **Installs:** 0
- **Category:** [Security](https://agentstack.voostack.com/c/security)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [omermaksutii](https://github.com/omermaksutii)
- **Source:** https://github.com/omermaksutii/RugProof/tree/main/skills/signature-malleability
- **Website:** https://omermaksutii.github.io/RugProof

## Install

```sh
agentstack add skill-omermaksutii-rugproof-signature-malleability
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# 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 (MEDIUM)
Accepting arbitrary `v` (not 27/28) or trusting a caller-supplied `v` for EIP-2098 compact sigs, mixing the 64-byte and 65-byte formats. EIP-2098 packs `yParity` into the high bit of `s`; decoding it as a raw 65-byte sig corrupts recovery.

### Cross-domain / cross-chain replay (HIGH)
```solidity
bytes32 digest = keccak256(abi.encode(ORDER_TYPEHASH, maker, amount, nonce));
```
**Signal:** no EIP-712 domain separator binding `chainId` and `verifyingContract`. A signature is replayable on another chain (post-fork) or a sibling deployment. Build the digest with `_hashTypedDataV4` (`\x19\x01` + domainSeparator + structHash) and recompute the domain separator if `block.chainid` changes.

### Signature reuse / no nonce (HIGH)
Same digest accepted twice because there's no per-signer nonce or no marking of consumed digests — meta-tx and claim flows drain repeatedly.

## Severity rubric

| Pattern | Severity | Notes |
|---|---|---|
| Malleable sig used as replay/dedup key | **High** | Twin sig bypasses guard |
| ecrecover==address(0) authenticates attacker | **High** | Auth bypass |
| Missing EIP-712 domain → cross-chain replay | **High** | Signature reuse across deployments |
| No nonce → straight replay | **High** | Repeated execution |
| Unconstrained v / EIP-2098 confusion | **Medium** | Format-dependent |
| Low-s missing but sig never used as a key | **Low** | Defense-in-depth |

## Remediation patterns

1. **Use OpenZeppelin `ECDSA.recover`** — it enforces `s <= secp256k1n/2`, rejects bad `v`, and reverts on `address(0)` (it also handles EIP-2098).
2. **Bind digests with EIP-712** (`EIP712` base + `_hashTypedDataV4`), including `chainId` and `verifyingContract`; recompute the domain separator on chainid change.
3. **Use per-signer nonces** (incrementing) rather than signature-bytes dedup; consume by `(signer, nonce)` not `(r,s,v)`.
4. **Explicit `require(signer != address(0))`** even when using libraries, for any hand-rolled path.
5. Prefer **`SignatureChecker`** when signers may be ERC-1271 contract wallets.

## False-positive notes

- Code already routing through OpenZeppelin `ECDSA`/`SignatureChecker` with EIP-712 and nonces is safe — don't re-flag the underlying `ecrecover`.
- A sig verified against a *non-zero, always-set* signer where the digest is never reused is malleability-tolerant; note, don't escalate.

## Related

- [[signature-replay]] — nonce/chainId replay is the sibling concern
- [[erc1271-contract-signatures]] — smart-wallet signers need `isValidSignature`
- [[permit2-patterns]] — Permit2 / EIP-2612 digests and nonces

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [omermaksutii](https://github.com/omermaksutii)
- **Source:** [omermaksutii/RugProof](https://github.com/omermaksutii/RugProof)
- **License:** MIT
- **Homepage:** https://omermaksutii.github.io/RugProof

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-omermaksutii-rugproof-signature-malleability
- Seller: https://agentstack.voostack.com/s/omermaksutii
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
