How the XRPL.to Wallet Protects Your Keys
Keys are generated, encrypted and used entirely inside your browser — no seed ever touches our servers. One rule shapes the whole design: the cryptographic parameters are fixed at the top. If a device is too slow to meet them, unlocking takes longer. We never scale the security down to fit the hardware.
Key generation
Entropy comes from crypto.getRandomValues() — the OS random generator via the Web Cryptography API. No Math.random(), no time-seeded fallback, anywhere. We draw 256 bits; the XRPL family seed format encodes 128 of them — the protocol's structural maximum, shared by every XRPL wallet (ripple-keypairs generateSeed takes exactly 16 bytes). Enumerating a 128-bit keyspace is not a hardware problem; it is a thermodynamics problem.
What a stored seed looks like
A seed is written to disk exactly one way: AES-256-GCM inside IndexedDB, key derived from your password with PBKDF2-HMAC-SHA-256. GCM is authenticated encryption (NIST SP 800-38D) — one flipped bit and decryption returns nothing, never a plausible-but-wrong seed. The derived key is non-extractable: the browser will not hand its raw bytes to any JavaScript, including ours. localStorage never holds a seed.
Salt and nonce are freshly random on every write — never reused. The header rides outside the ciphertext, but it can't cheat: a blob claiming fewer than 600,000 iterations is rejected before key derivation is even attempted.
The floor and the ceiling
The OWASP recommendation for PBKDF2-HMAC-SHA-256 is 600,000 iterations. That is our minimum. On first use the wallet times a probe, calibrates how many iterations fit in a 250 ms unlock, and re-checks weekly — capped at 4,000,000. A fast desktop doesn't get a faster unlock; it gets a harder key derivation for the same quarter-second.
Slow hardware waits — it doesn't get weaker crypto
Calibration only ever moves up from the floor. A low-end phone still runs all 600,000 iterations; its unlock simply takes longer than 250 ms. We price the defense against the attacker's hardware, not the slowest phone's — whoever steals your encrypted blob gets to choose their GPUs. The same policy applies to platforms: the wallet hard-requires HTTPS and crypto.subtle. A browser that can't provide them is unsupported. The bar does not move.
The Coldcard test
Three days before this article, on July 30, 2026, an attacker swept ~594 BTC (≈$38M) in about 25 minutes from Coldcard hardware wallets — devices built for exactly one job: keeping keys safe. Coinkite's advisory and the hotfix changelog put the effective seed entropy at ~40 bits on Mk3 and ~72 bits on later models — instead of 128. The flaw shipped in March 2021 and sat in production firmware for over five years. And it didn't stop at one sweep: by August 2, Galaxy Research counted three attack waves totalling 1,367 BTC (≈$88.6M) across 4,585 addresses.
The firmware is open source, so the fix commit shows the mechanism precisely. The board's hardware-TRNG file never exported the global rng_get() symbol — so the linker silently bound RNG calls to MicroPython's fallback software PRNG ("yasmarang"), compiled into every build for boards without hardware RNG. Its entire seed:
pad = *(uint32_t *)MP_HAL_UNIQUE_ID_ADDRESS ^ SysTick->VAL; n = RTC->TR; // wall-clock time d = RTC->SSR; // sub-second counter
A public chip ID, a tick counter, and the time. Nothing errored — the hardware TRNG sat unused while every seed came from an enumerable keyspace. The hotfix comment says it plainly: "this selected some PRNG code we really didnt want." The fix is the right one: the fallback is now compiled to an empty object so any reference breaks the build, and a symbol check asserts the hardware path is linked on every release.
| Coldcard, pre-hotfix | This wallet | |
|---|---|---|
| Randomness source | hardware TRNG — silently replaced by a linked-in software PRNG | OS CSPRNG via crypto.getRandomValues() |
| Fallback path | compiled into every build, waiting | none exists — strong or throw |
| Effective entropy | ~40 bits (Mk3) · ~72 bits (Mk4/Q) | 128 bits |
| How failure surfaces | silent for five years | loud — refuses to run |
This is not a hot-wallet-beats-hardware-wallet story — a Coldcard resists threats a browser never will. The lesson is narrower and harder: key generation is the single point of failure everywhere, an air gap cannot save a seed that was born weak, and no one can tell a weak seed from a strong one by looking at it. The only defense is what this page keeps repeating — the weak path must not exist, and where it cannot exist, it cannot be silently chosen.
Around the ciphertext
Failed attempts, counted in memory, localStorage and IndexedDB at once — worst case wins, so clearing one layer doesn't reset the clock.
- Cached credentials are XOR-masked with a random pad and cleared on lock and disconnect — plaintext passwords never sit recognizably in the heap.
- A revealed seed starts blurred, hides itself from printing and PDF export, and wipes on a countdown, on tab switch, and on unmount — and lives outside React state, invisible to DevTools.
- The backup screen interrupts live scams: before showing anything it asks whether anyone — a "support agent", a caller, a website — told you to open it. Answering yes lands on a hard stop that explains the scam playbook, because every real-world seed theft walks its victim through exactly this screen.
- A portable blob format exists for exactly one flow: the explicit QR transfer between your own devices.
The transaction layer
Every trustline the platform creates sets NoRipple — an open rippling path is an actively exploited way to drain same-currency tokens without a signature. Swap safety values (DeliverMin, slippage) are computed server-side from live ledger state, and transactions are simulated before submission so a failing transaction aborts instead of burning a fee. More in Sandwich Attacks on XRPL.
Adding a YubiKey
The wallet can also be locked with a hardware security key (Menu, then Security, then YubiKey). The key to the encrypted store is then wrapped under a secret only your YubiKey can produce, through the browser’s WebAuthn standard, and every unlock needs the key’s PIN and a touch; you choose how long one touch keeps the wallet open.
- Key only. Once you have a second key, or confirm your seeds are backed up, the password copy is removed: a copied browser profile then holds nothing that opens the wallet.
- The wallet on the key. In desktop Chrome and Edge, an encrypted copy of your accounts can live on the YubiKey itself, so another computer restores them with the key and its PIN.
- Still back up the seeds. Eight wrong PINs wipe a YubiKey’s keys, and a lost key takes its copy with it.
What this means for you
- Your seed never leaves your device — there is nothing server-side to breach.
- Your password is the last line — unless you add a YubiKey: the iteration floor makes each guess expensive; a strong password makes the math unwinnable.
- Back up your seed — device binding means a lost device is a lost wallet unless you exported it. The nudge in the wallet is load-bearing.
- Old hardware works, slowly — a sluggish unlock is the security budget being spent. There is no setting to lower it.