ROBIN VAULT

Security Model

Robin Vault is non-custodial: your wallet's key material is derived on-demand from your NFC card and a 6-digit PIN, held only in volatile RAM, and never written to disk by either the client or the server. Neither side alone has enough information to derive your wallet.

The flaw that shaped this design

The original design used the card's NFC UID alone as the salt for the key-derivation function. During review, this turned out to be a real problem: consumer NFC card UIDs (NTAG21x, Mifare, and similar) are not secret — they're readable by any unauthenticated reader that touches the card.

A 6-digit PIN is only 1,000,000 possible combinations. Combine a non-secret UID with a PIN-only defense and no lockout mechanism in a purely offline scheme, and an attacker who taps your physical card once could brute-force the PIN completely offline in days to weeks — even with Argon2id's deliberate slowness working against them.

TLS is non-negotiable

Without TLS, an on-path attacker who also has physical access to the card could sniff a single salt response and brute-force the PIN completely offline — permanently bypassing the lockout described below. The salt is the only secret-adjacent value that ever crosses the network, and losing it collapses the whole scheme back to PIN-only.

The fix: server-side salting

A minimal backend vends a strong, random, per-account salt over the network, and rate-limits repeated requests. Every PIN guess now costs a network round-trip that the server can throttle — turning an offline attack into an online one bounded by an escalating lockout (see Rate Limiting Policy).

What the server does and doesn't know

StoresNever stores
accountId = SHA-256(card UID)The raw card UID
A random 32-byte saltThe PIN
Rate-limit counters (attempt count, lockout timestamp)The mnemonic, seed, or any private key

The server never sees your PIN, the derived entropy, the mnemonic, or any private key. It only ever handles a one-way hash of your card's UID and a random salt — both safe to transmit, neither useful to an attacker on their own.

Brute-force economics

Attacker profileTime to exhaust the PIN space
Sustained attack, hits the 30-day lockout cap~82,000 CPU-years
"Patient" attacker, pacing requests to stay under the free-tier reset~548 years

Assumptions behind these figures (so they're auditable, not asserted): a PIN space of 106 (6 digits), a calibrated Argon2id cost of 1.5–3 seconds per guess on the client side, and the exact lockout formula in Rate Limiting Policy. Either way, brute force goes from "weeks" to operationally meaningless.

Design principle

The app never claims to know whether a PIN was "correct" — it has no way to. There is no local verification step; the wallet either derives the addresses you expect, or it doesn't.