Skip to content
CodeBrewerz logoCodeBrewerz
Build it yourself

Let's Build a Secure Wallet: Keys, Signatures and Every Way It Leaks

A wallet holds no money. It holds a key — and every wallet that has ever been drained lost that key in one of about six ways. Here is the design that closes them.

Published 8 min readBy Piyush Jain
  • Security
  • Cryptography
  • Wallets
  • Key Management

A wallet does not hold money. It never did. The balance lives on a ledger that thousands of machines agree about, and what your wallet holds is a private key — the only thing in the world that can produce a signature that ledger will accept.

That reframing is the whole discipline. You are not building a vault. You are building a key manager whose one job is to make a signature happen under conditions you control, on hardware you have to assume is hostile.

Where does the key come from?

From randomness, and only from randomness.

Figure 1

What a seed phrase is actually worth

Entropy128 bits
Possibilities3.4 × 10^38
Time to search half

2048 words, 11 bits each, minus a 4-bit checksum. The standard wallet default, and already far past anything brute-forceable.

Where the key came from
Entropy in bits, against an attacker managing a trillion guesses per second — far beyond any real adversary, chosen to make the point that the gap between 128 bits and a human-chosen phrase is not a matter of degree. Nothing here generates or handles key material; it is arithmetic on the size of the search space.

The gap between the first two options and the last three is the entire security of the system, and it does not degrade gracefully. 128 bits of entropy is not “very hard to guess”; it is beyond the reach of any adversary that will ever exist. A human-chosen phrase is not “a bit weaker”; it is a weekend of compute.

So: use the platform’s cryptographic random source and nothing else. getrandom on Linux, SecRandomCopyBytes on Apple platforms, crypto.getRandomValues in a browser, crypto.randomBytes in Node. Never Math.random, never a timestamp, never a hash of something memorable, and never a scheme you invented on the grounds that it seemed clever. Every brainwallet scheme in history has ended with the addresses drained within seconds of being funded, by attackers who had pre-computed the hashes years earlier.

Then encode those bytes as a BIP-39 mnemonic. The word list is deliberate: 2048 words, each unambiguous in its first four letters, with a checksum built into the last word so a typo is caught rather than silently deriving a different, empty wallet.

How does one key become thousands?

Through BIP-32 hierarchical deterministic derivation, which is one of the genuinely elegant pieces of design in the space.

Figure 2

One seed, every key, and where the hardening matters

/////
Master node

Derived from the seed by HMAC-SHA512. Every key you will ever use comes from this one value, which is why the seed phrase is the only thing that ever really needs backing up — and the only thing whose disclosure loses everything at once.

What an xpub exposes

Every address below it, past and future, and therefore your entire balance and transaction history. Accounting software and watch-only wallets legitimately want this. Treat it as a privacy secret even though it is not a spending secret.

The failure that empties wallets

An extended public key plus any one non-hardened child private key lets an attacker recover the parent private key, and from it every sibling. That is not a bug, it is the arithmetic of BIP-32 — and it is exactly why the account level and everything above it is hardened.

A BIP-44 path, m/44'/60'/0'/0/0 — the default for an Ethereum account. The apostrophe means hardened derivation, where the child is derived from the parent private key rather than the public one. That single difference is what stops a leaked extended public key from becoming a leaked private key.

The master key comes from the seed. Every subsequent key is derived from its parent and an index, deterministically. That gives you a fresh address per transaction — which is the only privacy measure that actually works on a public ledger — with a single thing to back up, forever.

The detail that separates a correct implementation from a catastrophic one is hardened derivation, marked with the apostrophe in the path.

Non-hardened derivation has a specific, useful property: you can derive child public keys from a parent public key, without any private key. That is what makes watch-only wallets and accounting integrations possible.

It also has a specific, dangerous one: given a parent extended public key and any single non-hardened child private key, an attacker can algebraically recover the parent private key — and from that, every sibling. Hardened derivation breaks that relationship by deriving from the parent private key instead, at the cost of losing public derivation below that point.

This is why the standard path hardens everything down to the account level and stops: you get isolation between accounts, and you keep the ability to hand an accountant an xpub for one account without exposing anything else. Get this backwards and one leaked child key empties the whole tree.

How does a signature actually get made?

Figure 3

Where the key is, at every step

1
The application builds a transactionUntrusted host

Recipient, amount, nonce, gas, chain id, and whatever data the contract call needs. This runs on a machine you cannot fully trust, and that is fine, because nothing secret is involved yet.

2
It is serialised and hashedUntrusted host

The canonical encoding is hashed to a fixed-size digest. Deterministic serialisation matters here — two encodings of the same intent produce two different signatures, and replay protection depends on the chain id being inside the hash.

3
The digest crosses to the secure elementBoundary

Over USB, Bluetooth or an internal bus. What crosses is a hash and a derivation path. What never crosses, in either direction, is the private key.

4
The device shows you what it is about to signSecure element

Decoded on the device's own screen, from the bytes it actually received rather than from what the application claims they say. This is the only step that protects you from a compromised computer, and it only works if you read it.

5
It signs, inside the secure elementSecure element

ECDSA or EdDSA over the digest with the derived key. Deterministic nonces per RFC 6979 are not optional — a repeated or predictable nonce leaks the private key outright, which is how a well-known console signing key was recovered.

6
The signature crosses backBoundary

r, s and a recovery byte. Sixty-five bytes, from which anyone can recover the public key and verify the transaction, and from which nobody can recover the private key.

7
The application broadcasts itUntrusted host

Every node on the network verifies the signature against the sender's address. The untrusted half is back in charge, and it can no longer do any harm — the worst it can do is refuse to send something you already approved.

The whole design is one rule: the private key is created inside the secure element and never leaves it. Everything else — building, hashing, broadcasting — happens on hardware you assume is compromised, because eventually one of them will be.

Everything in that flow follows from one rule: the private key is generated inside the secure element and never leaves it. What crosses the boundary is a hash going in and a signature coming out.

Two points on that path deserve more attention than they usually get.

The device’s screen is the security boundary, not the computer’s. A hardware wallet protects you from malware precisely because it decodes and displays the transaction from the bytes it received, independently of the application that sent them. If you approve without reading — or if the payload is opaque and the device can only say “sign this hash”, which is what blind signing means — you have kept the key safe and lost the money anyway. Most large hardware-wallet losses are this, not key extraction.

Nonce reuse is fatal. ECDSA needs a random value per signature. Sign two different messages with the same nonce and anyone can solve for your private key with school algebra. This has happened in production more than once, most famously to a games console’s code-signing key. The fix is deterministic nonces derived from the key and the message, specified in RFC 6979, and it is not optional.

What are you actually defending against?

Figure 4

No row wins every column

ModelMalware on the devicePhishing / blind signingPhysical theftLosing the keyInsider or provider failureCoercion
Software walletKeys in the application's storage on a general-purpose device.
Hardware walletKeys generated and held in a secure element that never exports them.
MPC / thresholdThe key exists only as shares; signing is a protocol between them.
CustodialSomebody else holds the keys; you hold an account with them.
Select a cell

Every cell has a reason behind it. Choose one to read it.

Editorial judgement, not measurement — but the shape is not controversial. Custodial storage is strongest against the risks a normal person actually meets and weakest against the one that has cost the most money historically. Self-custody inverts both. Select any cell for the reasoning.

Work across the rows and the honest conclusion arrives on its own: there is no row that wins every column. A hardware wallet is superb against malware and does nothing about a seed phrase photographed in a drawer. A custodial account is excellent against every threat a normal person actually meets and is the only row exposed to the risk that has historically cost the most money.

Which means the design question is never “what is the most secure wallet”. It is “who is this for, what do they realistically face, and what happens on the day they make a mistake” — because they will.

What would we actually build?

A wallet for real users, with a real threat model, comes out looking like this.

Key generation in a secure element where one exists — Secure Enclave, StrongBox, TPM, or a dedicated device — and from the platform CSPRNG where one does not. Never in JavaScript, never on a server, never anywhere the value can be observed by a debugger.

Storage with keys wrapped by a key that itself lives in hardware. On mobile, that is Keychain with kSecAttrAccessibleWhenUnlockedThisDeviceOnly and biometric protection, or Android Keystore with setUserAuthenticationRequired. On a desktop, the OS keychain, never a file next to the application. Encrypt with AES-GCM or XChaCha20-Poly1305, derive the wrapping key from the passphrase with Argon2id, and pick parameters by measuring on the slowest device you support rather than by copying a blog post.

Signing behind an explicit user action, with the decoded transaction shown in terms a person can check — this address, this amount, this contract, this chain. Refuse to blind sign, or make it a deliberate, warned, per-transaction choice. Show the counterparty’s address in full, or in a form where the middle cannot be swapped without being noticed.

Recovery designed before launch, because it decides everything else. A single seed phrase is the traditional answer and it fails in both directions: users lose it, and users get it stolen. Shamir’s Secret Sharing splits it into shares where any k of n recover it. Social recovery — as in the Argent and ERC-4337 designs — replaces the phrase with guardians who can rotate the key. MPC removes the single phrase entirely by never assembling the key in one place. Each of these is a real trade of one failure mode for another, and choosing is the design work.

Rate limits and policy, which people forget are available. A daily limit, a withdrawal allowlist with a time delay before a new address becomes usable, a second approval above a threshold. These are the controls that turn a total loss into a partial one, and they are the reason institutional custody is boring.

What breaks it in practice?

Not the cryptography. Never the cryptography.

The seed phrase is photographed. In a cloud-synced screenshot, a password manager, an email to oneself. Every one of these is a plaintext copy in somebody else’s infrastructure. Metal backup plates exist for exactly this reason, and they are cheap.

The user is phished into signing. A fake front end, a real wallet, a legitimate signature that transfers everything. This is now the dominant loss vector by value, and no amount of key protection addresses it. Transaction simulation and human-readable decoding are the only real defences.

An unlimited token approval. The user signs an approval once, the contract keeps the right forever, and it is exercised a year later. Default to exact-amount approvals and show existing ones somewhere the user will find them.

Malware reads the clipboard. The recipient address is silently swapped between copy and paste. Verify addresses on a second screen, or use a name service the user can read.

A supply-chain compromise. A dependency of your wallet application is updated to exfiltrate keys. Pin dependencies, verify signatures, keep the signing path in as small a module as you can defend, and be extremely conservative about what runs in the same process as key material.

The recovery path is the attack path. Every recovery mechanism you build is also a way in. Account recovery via email means email compromise is wallet compromise. This is not a reason to skip recovery; it is a reason to design it with the same care as signing.

The short version

A wallet is a key manager. Its security is the entropy that created the key, the boundary that key never crosses, and the honesty of what you show a person before they approve something irreversible.

Get randomness from the platform. Harden the derivation path down to the account level. Keep the key in hardware and send it hashes, not secrets. Decode transactions on the trusted screen and refuse to blind sign. Then design the recovery story, and be honest with yourself that it is the second front door — because the failure that takes the funds is almost never the one in the cryptography.

Next step

Want This Built, Not Just Explained?

CodeBrewerz builds the systems these posts take apart: web, mobile, cloud and the infrastructure underneath. Tell us what you are building.

Start a conversation