# `Cartouche.Recover`
[🔗](https://github.com/ZenHive/onchain-stack/blob/cartouche-v0.9.1/packages/cartouche/lib/cartouche/recover.ex#L1)

EIP-191 (`personal_sign`) signature recovery primitives.

Given a message and a packed secp256k1 signature, recover the signer's
public key (`recover_public_key/2`) or Ethereum address (`recover_eth/2`).
`recover_personal_sign/2` is the MetaMask / WalletConnect path: it wraps the
message in the EIP-191 envelope before recovering. When the signature arrived
without a recovery bit (e.g. some HSM / KMS backends return only `(r, s)`),
`find_recid/3` brute-forces the two valid recids against an expected address.

Signatures may be supplied either as a `Curvy.Signature` struct (when the
recovery bit lives in `:recid`) or as packed `r <> s <> v` bytes. `v` is one
or more bytes (65 bytes total when it fits in a single byte; longer when
EIP-155 `v` exceeds 255). It is interpreted as recid `0`/`1` (raw form),
`27`/`28` (`personal_sign`), or `35 + 2 * chain_id + recid` (EIP-155).

Used internally by `Cartouche.Signer` for the recover-and-verify sanity check
after each sign call, and exposed as the public surface for consumers
verifying user-supplied signatures (e.g. `personal_sign` payloads from
MetaMask, WalletConnect, or any wallet implementing EIP-191).

# `find_recid`

```elixir
@spec find_recid(binary(), Curvy.Signature.t(), &lt;&lt;_::160&gt;&gt;) ::
  {:ok, 0..1} | {:error, String.t()}
```

Finds the given recid which recovers the given signature for the message to the given
Ethereum address. This is a very simple guess-check-revise since there are only four
possible values, and we only accept two of those.

## Examples

  iex> use Cartouche.Hex
  iex> priv_key = ~h[0x800509fa3e80882ad0be77c27505bdc91380f800d51ed80897d22f9fcc75f4bf]
  iex> {:ok, sig} = Cartouche.Signer.Curvy.sign("test", priv_key)
  iex> {:ok, recid} = Cartouche.Recover.find_recid("test", sig, ~h[0x63CC7C25E0CDB121ABB0FE477A6B9901889F99A7])
  iex> Cartouche.Recover.recover_eth("test", %{sig|recid: recid})
  ...> |> to_hex()
  "0x63cc7c25e0cdb121abb0fe477a6b9901889f99a7"

# `find_recid_from_digest`

```elixir
@spec find_recid_from_digest(&lt;&lt;_::256&gt;&gt;, Curvy.Signature.t(), &lt;&lt;_::160&gt;&gt;) ::
  {:ok, 0..1} | {:error, String.t()}
```

Finds the recid recovering `signature` (over an already-computed 32-byte
`digest`) to `address`, performing **no** hashing of its own.

Digest-native counterpart to `find_recid/3` — this is the entrypoint the signer
uses so the recid is searched against the *same digest the backend signed*,
which is what lets one backend set cover plain Eth tx, EIP-712, and Hyperliquid
without re-hashing assumptions. Same guess-check over the four candidate recids,
accepting only `0`/`1`.

# `normalize_low_s`

```elixir
@spec normalize_low_s(Curvy.Signature.t()) :: Curvy.Signature.t()
```

Canonicalizes a secp256k1 signature to its low-`s` form (EIP-2).

If `s > n/2`, replaces it with `n - s` (and leaves `recid` as `nil`, since the
recovery bit is re-derived by `find_recid_from_digest/3` afterward). Local
Curvy already emits low-`s`, so this is a no-op there; it exists so DER-decoded
KMS/HSM backends (which may return high-`s`) inherit the invariant without each
re-solving the malleability fix. Idempotent.

# `prefix_eth`

```elixir
@spec prefix_eth(binary()) :: binary()
```

Wraps a message in the EIP-191 `personal_sign` envelope.

The returned binary concatenates four parts:

  * `0x19` — the EIP-191 version byte
  * `"Ethereum Signed Message:\n"` — the literal namespace prefix (newline-terminated)
  * the byte length of `msg`, formatted as decimal ASCII
  * `msg` itself

The doctest output below shows `\x19` and `\n` as escape sequences — those
are the literal `0x19` byte and `0x0A` newline byte in the returned string.

See [EIP-191](https://eips.ethereum.org/EIPS/eip-191).

## Examples

  iex> Cartouche.Recover.prefix_eth("hello")
  "Ethereum Signed Message:\n5hello"

# `recover_eth`

```elixir
@spec recover_eth(binary(), Curvy.Signature.t() | binary()) :: &lt;&lt;_::160&gt;&gt;
```

Recovers a signer's Ethereum address from a signed message. The message will
be digested by keccak first. Note: the rec_id can be embeded in the signature
or passed separately.

## Examples

  iex> use Cartouche.Hex
  iex> priv_key = ~h[0x800509fa3e80882ad0be77c27505bdc91380f800d51ed80897d22f9fcc75f4bf]
  iex> {:ok, sig} = Cartouche.Signer.Curvy.sign("test", priv_key)
  iex> {:ok, recid} = Cartouche.Recover.find_recid("test", sig, ~h[0x63CC7C25E0CDB121ABB0FE477A6B9901889F99A7])
  iex> Cartouche.Recover.recover_eth("test", %{sig|recid: recid})
  ...> |> to_hex()
  "0x63cc7c25e0cdb121abb0fe477a6b9901889f99a7"

# `recover_eth_from_digest`

```elixir
@spec recover_eth_from_digest(&lt;&lt;_::256&gt;&gt;, Curvy.Signature.t() | binary()) ::
  &lt;&lt;_::160&gt;&gt;
```

Recovers a signer's Ethereum address from a signature over an already-computed
32-byte digest, performing **no** hashing of its own.

Digest-native counterpart to `recover_eth/2`; see
`recover_public_key_from_digest/2`.

# `recover_personal_sign`

```elixir
@spec recover_personal_sign(binary(), Curvy.Signature.t() | binary()) :: &lt;&lt;_::160&gt;&gt;
```

Recovers the signer of an EIP-191 `personal_sign` payload (MetaMask,
WalletConnect, and any wallet that hashes the `Ethereum Signed Message:\n`
envelope).

Applies `prefix_eth/1` then `recover_eth/2`. Use this rather than calling
`recover_eth/2` on the raw message — wallets sign the prefixed envelope, not
the bare UTF-8 bytes.

## Examples

    iex> use Cartouche.Hex
    iex> priv_key = ~h[0x800509fa3e80882ad0be77c27505bdc91380f800d51ed80897d22f9fcc75f4bf]
    iex> prefixed = Cartouche.Recover.prefix_eth("hello")
    iex> {:ok, sig} = Cartouche.Signer.Curvy.sign(prefixed, priv_key)
    iex> {:ok, recid} = Cartouche.Recover.find_recid(prefixed, sig, ~h[0x63CC7C25E0CDB121ABB0FE477A6B9901889F99A7])
    iex> packed = <<sig.r::256, sig.s::256, 27 + recid>>
    iex> Cartouche.Recover.recover_personal_sign("hello", packed) |> to_hex()
    "0x63cc7c25e0cdb121abb0fe477a6b9901889f99a7"

# `recover_public_key`

```elixir
@spec recover_public_key(binary(), Curvy.Signature.t() | binary()) :: binary()
```

Recovers a signer's public key from a signed message. The message will be
digested by keccak first. Note: the rec_id can be embeded in the signature
or passed separately.

## Examples

  iex> use Cartouche.Hex
  iex> # Decoded Signature
  iex> priv_key = ~h[0x800509fa3e80882ad0be77c27505bdc91380f800d51ed80897d22f9fcc75f4bf]
  iex> {:ok, sig} = Cartouche.Signer.Curvy.sign("test", priv_key)
  iex> {:ok, recid} = Cartouche.Recover.find_recid("test", sig, ~h[0x63CC7C25E0CDB121ABB0FE477A6B9901889F99A7])
  iex> Cartouche.Recover.recover_public_key("test", %{sig|recid: recid}) |> to_hex()
  "0x0480076bfb96955526052b2676dfca87e0b7869ce85e00c5dbce29e76b8429d6dbf0f33b1a0095b2a9a4d9ea2a9746b122995a5b5874ee3161138c9d19f072b2d9"

  iex> use Cartouche.Hex
  iex> # Binary Signature
  iex> priv_key = ~h[0x800509fa3e80882ad0be77c27505bdc91380f800d51ed80897d22f9fcc75f4bf]
  iex> {:ok, sig} = Cartouche.Signer.Curvy.sign("test", priv_key)
  iex> {:ok, recid} = Cartouche.Recover.find_recid("test", sig, ~h[0x63CC7C25E0CDB121ABB0FE477A6B9901889F99A7])
  iex> signature = <<sig.r::256, sig.s::256, recid>>
  iex> Cartouche.Recover.recover_public_key("test", signature) |> to_hex()
  "0x0480076bfb96955526052b2676dfca87e0b7869ce85e00c5dbce29e76b8429d6dbf0f33b1a0095b2a9a4d9ea2a9746b122995a5b5874ee3161138c9d19f072b2d9"

  iex> use Cartouche.Hex
  iex> # EIP-155 Signature
  iex> priv_key = ~h[0x800509fa3e80882ad0be77c27505bdc91380f800d51ed80897d22f9fcc75f4bf]
  iex> {:ok, sig} = Cartouche.Signer.Curvy.sign("test", priv_key)
  iex> {:ok, recid} = Cartouche.Recover.find_recid("test", sig, ~h[0x63CC7C25E0CDB121ABB0FE477A6B9901889F99A7])
  iex> recovery_bit = 35 + 5 * 2 + recid
  iex> signature = <<sig.r::256, sig.s::256, recovery_bit::8>>
  iex> Cartouche.Recover.recover_public_key("test", signature) |> to_hex()
  "0x0480076bfb96955526052b2676dfca87e0b7869ce85e00c5dbce29e76b8429d6dbf0f33b1a0095b2a9a4d9ea2a9746b122995a5b5874ee3161138c9d19f072b2d9"

# `recover_public_key_from_digest`

```elixir
@spec recover_public_key_from_digest(&lt;&lt;_::256&gt;&gt;, Curvy.Signature.t() | binary()) ::
  binary()
```

Recovers a signer's public key from a signature over an already-computed
32-byte digest, performing **no** hashing of its own.

This is the digest-native counterpart to `recover_public_key/2`: use it when
the bytes that were signed are a pre-computed hash (an Ethereum tx keccak, an
EIP-712 / Hyperliquid typed-data hash, …) rather than a raw message to be
keccak'd. The recid may be embedded in the signature or set on the struct.

High-s signatures are normalized before recovery, flipping `s` and the
recovery bit together. Equivalent low-s and complement-s signatures recover
the same public key, for both packed bytes and `Curvy.Signature` structs.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
