NFC Backup Cards

How Numa stores an encrypted copy of your seed phrase on a physical NFC card — and how to recover it with or without the app.

What it is

Numa's signer mode can write a copy of your BIP39 seed phrase to an ordinary NFC card (NTAG215 or NTAG216), encrypted with a password you choose. Restoring is the reverse: tap the card, enter the password, and the words are filled in for you.

The format is deliberately open and fully documented on this page. If Numa disappeared tomorrow, anyone could still recover the seed using standard, widely available tools. Your backup never depends on us.

How the data is stored

The card holds a single NDEF record (the standard NFC data format readable by any phone) with the MIME type application/vnd.numa.wallet-backup. Its payload is a small JSON document:

{
  "v": 1,
  "t": "bip39",
  "kdf": "argon2id",
  "mem": 131072,
  "it": 4,
  "par": 1,
  "s": "<base64: 16-byte random salt>",
  "n": "<base64: 12-byte random nonce>",
  "c": "<base64: ciphertext + 16-byte GCM tag>"
}
Field Meaning
vFormat version (always 1)
tPayload type — a BIP39 mnemonic
kdfKey derivation function (Argon2id, version 1.3)
memArgon2 memory cost in KiB (131072 = 128 MiB)
itArgon2 passes (4)
parArgon2 parallelism (1)
sRandom salt for key derivation
nAES-GCM nonce
cThe encrypted mnemonic, with the GCM authentication tag appended

Anyone who taps the card can read this JSON — NFC has no access control. That is by design: all secrecy lives in the encryption, not in hiding the format. What an attacker gets is ciphertext they cannot open without your password.

The encryption

Your password is turned into a 256-bit key with Argon2id — the winner of the Password Hashing Competition and the OWASP first choice — configured at 128 MiB of memory and 4 passes. Every password guess costs an attacker 128 MiB of RAM and real CPU time, which cripples GPU cracking rigs: a 24 GB GPU fits ~190 parallel guesses instead of the millions it could run against a conventional hash.

The mnemonic is then encrypted with AES-256-GCM, an authenticated cipher. The GCM tag doubles as a correctness check: a wrong password fails authentication cleanly — decryption can never silently produce the wrong words.

The password is the whole defense. Argon2id makes each guess expensive, but no algorithm rescues a guessable password — avoid names, dates, and anything you use elsewhere. Write the password down and store it away from the card. Without it, the backup is unrecoverable — by you or anyone else.

How to decrypt without the app

Read the card's NDEF record with any NFC tool (for example "NFC Tools" on iOS/Android) and copy the JSON payload. Then run the reference script below — it uses only standard, audited open-source libraries:

# pip install argon2-cffi cryptography
import base64, json
from argon2.low_level import hash_secret_raw, Type
from cryptography.hazmat.primitives.ciphers.aead import AESGCM

card = json.loads(input("card JSON: "))
password = input("card password: ").strip()

key = hash_secret_raw(
    password.encode("utf-8"),
    base64.b64decode(card["s"]),
    time_cost=card["it"],
    memory_cost=card["mem"],
    parallelism=card["par"],
    hash_len=32,
    type=Type.ID,
)
words = AESGCM(key).decrypt(
    base64.b64decode(card["n"]),
    base64.b64decode(card["c"]),
    None,
).decode()
print(words)

Argon2id and AES-256-GCM are available in every major ecosystem — libsodium, the Argon2 reference implementation, hash-wasm for browsers, and countless others. Any implementation that follows the parameters above will produce the same key and decrypt the same card.

Test vector

Implementations can verify themselves against this fixed vector. Password: correct horse battery staple — it decrypts to the standard BIP39 test mnemonic (11× "abandon" + "about"):

{"v":1,"t":"bip39","kdf":"argon2id","mem":131072,"it":4,"par":1,
"s":"AAECAwQFBgcICQoLDA0ODw==","n":"oKGio6Slpqeoqaqr",
"c":"hf9UJ59swabLSp32UhLkLOUCweQlCcqoI9eh5q60V3us/Fni7lplS73pVrdUv672SapqLnaCzCuRVaXzJ5MgSYja5DeORsv4VrMGq+sAhjlvyykhCYCDd35Fr3p766wAKm7lxJsasZqNGoh9tg=="}

Good practices

Format guarantee: version 1 is frozen. Future versions of Numa may add new formats, but every released build will always be able to decrypt v1 cards, and this page will remain the authoritative spec.

← Back to Numa Wallet