← home

Writing

Where custody actually fails

20 September 2026 · Prasanta Sahoo

The headline exchange losses are rarely a broken curve or a cracked HSM. They are process and integration failures around the keys — and those are the parts a cryptography audit doesn't look at.

“Is the custody secure?” usually gets answered with the name of a vendor: an HSM model, an MPC library, a qualified custodian. Those answers are about the cryptography, and the cryptography is almost never what fails.

What fails is everything around it: who can authorise a withdrawal, what the signing service will sign, whether the policy engine can be bypassed, how the hot wallet is refilled, what happens when a key is rotated, and whether a compromised operator can move funds alone. None of that is in the crypto. All of it has moved real money.

Here are the failure classes I look for when reviewing a custody stack. None require breaking a cipher.

1. The signing service is a confused deputy

A signer that signs whatever transaction is presented to it, with authorisation checked somewhere upstream, is only as strong as that upstream check — and the signer itself becomes the target. Compromise the service that calls the signer, and the keys never had to leak.

CheckAuthorisation and transaction policy are enforced at the signer, against the transaction it is actually about to sign — not trusted from the caller. The signer validates destination, amount and asset itself.

2. Quorum that isn't

An m-of-n signing policy is only meaningful if the m shares are held by distinct people on distinct systems. The common failures: a threshold set too low for the value at risk; two “independent” shares sitting on the same host or held by the same person; or an admin path that can lower the threshold without itself requiring quorum.

CheckShares are genuinely separated by person and system, the threshold matches the value, and changing the threshold is itself a quorum action.

3. The withdrawal policy engine can be bypassed

Whitelists, velocity limits, per-address caps and manual-review triggers are the real security control on withdrawals. The bug is almost always a second path that skips them: an internal transfer endpoint, an admin override, an emergency-drain function, a migration tool. The front door is guarded; the side door isn't.

CheckEvery path that can move funds — including internal, admin, emergency and migration paths — passes the same policy engine. Enumerate them; there is always more than one.

4. Hot-wallet float larger than it needs to be

The hot wallet is the part that gets drained. If the float is sized for convenience rather than for actual outflow, the blast radius of any hot-side compromise is correspondingly larger. Related: refill automation that tops the hot wallet up from cold without a human in the loop turns a hot compromise into a cold compromise.

CheckFloat is sized to real withdrawal demand with a sweep policy; cold-to-hot refills require authorisation and can't be driven by a compromised hot-side process.

5. Four-eyes that one pair of eyes can defeat

Dual control is worth exactly as much as the independence of the two controllers. It fails when one operator holds both credentials, when the approval UI can be driven by API with a single token, when the “second approver” is a service account, or when the initiator can also approve under a different role.

CheckThe two approvals require two distinct humans with distinct credentials and devices, and the initiator is structurally barred from approving.

6. MPC implementation details

MPC removes the single private key, which is good, and introduces a protocol whose implementation is now the attack surface. The recurring issues are not in the maths but in the engineering: nonce reuse or biased randomness in signing, missing checks that let a single malicious participant bias key generation, weak authentication between parties, and recovery flows that reconstruct a full key in one place — reintroducing exactly the single point the scheme was meant to remove.

CheckSigning nonces are unique and well-sourced, key generation is robust to a malicious participant, parties authenticate each other, and no recovery path ever materialises a full key in one location.

7. Key rotation that leaves the old key live

Rotation is only protective if the old key is actually decommissioned. The common gap: new key deployed, old key never revoked or still holding funds — so the pre-rotation compromise everyone assumed was closed is still open. The mirror image: rotation that invalidates recovery material nobody re-tested.

CheckRotation revokes and drains the old key, and recovery material is re-verified against the new key after every rotation.

8. Custodian integration trust

Using Fireblocks, BitGo or Coinbase Custody moves the key risk to a vendor but creates a new one: the integration code. Webhooks that aren't authenticated, so a forged callback confirms a withdrawal that never happened. Callbacks trusted without verifying signature or replay. API keys with more scope than the workflow needs. The custodian is solid; the glue around it is where the bug is.

CheckEvery callback from the custodian is signature-verified and replay-protected, and the API credentials are scoped to the minimum the workflow requires.

9. Backup and recovery material exposure

The seed phrase, the key shards, the recovery kit — the security of the whole system collapses to the security of its worst-protected backup. Shards in a shared password manager, a recovery seed photographed “temporarily,” a cloud-KMS-wrapped key whose KMS access isn't itself quorum-controlled. The primary path can be immaculate and the backup path can hand over everything.

CheckRecovery material is protected to the same standard as the live keys, geographically and organisationally split, and its access is itself audited and quorum-gated.

10. Deposit-address handling

Less catastrophic, more common. Address reuse that harms user privacy and enables correlation; change-address handling bugs that misattribute or lose funds; derivation-path errors that generate addresses the wallet can't later spend; and address-poisoning defences that aren't there.

CheckFresh addresses per deposit, correct change handling, verified derivation, and address-poisoning awareness in the withdrawal UI.

How you actually review this

Custody review is a process-and-integration exercise, not a cryptography exercise. Three things carry it.

Enumerate every path that can move funds. Not the withdrawal endpoint — every path. Internal transfers, admin tools, emergency functions, migration scripts, the refill automation. The bug is almost always the path someone forgot was a path.

Trace authorisation to where the signature happens. Follow a withdrawal from request to signed transaction and ask, at the moment of signing, what the signer knows and what it trusts. Most real failures are a trust assumption that holds until one upstream component is compromised.

Attack the backup and recovery paths as hard as the live ones. They are usually weaker and always sufficient.

None of this needs a cryptographer. It needs someone who will read the whole custody workflow as an attacker reads it — looking for the one path that skips the control.

If you run custody — self-managed, MPC, or integrated with a third-party custodian — and no independent review has traced every fund-movement path end to end, that is the review worth doing. research@deebug.io