The Ethereum keystore v3 format
Defined in the original Web3 Secret Storage spec, v3 is a JSON document with a deliberate, memory-hard KDF.
{
"version": 3,
"id": "3198bc9c-6672-5ab3-d995-4942343ae5b6",
"address": "008aeeda4d805471df9b2a5b0f38a0c3bcba786b",
"crypto": {
"ciphertext": "5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46",
"cipherparams": { "iv": "6087dab2f9fdbbfaddc31a909735c1e6" },
"cipher": "aes-128-ctr",
"kdf": "scrypt",
"kdfparams": {
"dklen": 32,
"salt": "ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd",
"n": 262144,
"r": 8,
"p": 1
},
"mac": "517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2"
}
}Why Scrypt is so slow on purpose
Scrypt (Colin Percival, 2009) was designed as a memory-hard KDF specifically to make GPU and ASIC attacks expensive. The parameters in the keystore above — N=262144, r=8, p=1 — mean each password guess requires:
- Allocating
128 × N × r = 128 × 262,144 × 8 = 256 MBof working memory - Filling that memory with a pseudorandom mixing function
- Performing N dependent random-access reads
Because modern GPUs have limited VRAM per core, Scrypt kills parallelism. An RTX 4090 with 24 GB VRAM can run at most ~80 parallel guesses. Compare that to MD5 where the same GPU runs 160 billion parallel guesses.
Decryption algorithm
# Python pseudocode for verifying a keystore password
import scrypt, hashlib
from Crypto.Cipher import AES
def verify(password, keystore):
k = keystore["crypto"]
salt = bytes.fromhex(k["kdfparams"]["salt"])
derived = scrypt.hash(
password.encode(),
salt,
N=k["kdfparams"]["n"],
r=k["kdfparams"]["r"],
p=k["kdfparams"]["p"],
buflen=k["kdfparams"]["dklen"],
)
# MAC check: first verify without decrypting
mac = hashlib.sha3_256(derived[16:32] + bytes.fromhex(k["ciphertext"])).hexdigest()
if mac != k["mac"]:
return None
# MAC matched, decrypt
cipher = AES.new(derived[0:16], AES.MODE_CTR,
initial_value=bytes.fromhex(k["cipherparams"]["iv"]),
nonce=b"")
return cipher.decrypt(bytes.fromhex(k["ciphertext"])) # 32-byte private keyThe important detail: the MAC is Keccak-256 of derived[16:32] || ciphertext. This lets an attacker skip AES decryption on every wrong guess — but the Scrypt step still costs 256 MB and ~200 ms per try.
Extracting the hash for hashcat
$ python3 ethereum2john.py UTC--2018-07-01T14-42-25.123Z--abc.json > eth.hash
$ cat eth.hash
$ethereum$s*262144*8*1*ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd*5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46*517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2
$ hashcat -m 15700 -a 0 eth.hash wordlist.txt -r OneRuleToRuleThemAll.ruleReal GPU speeds
| GPU | Mode 15700 (Scrypt N=262144) | Mode 15600 (PBKDF2) | Guesses/day |
|---|---|---|---|
| RTX 4090 | ~4–5 H/s | ~380 k H/s | ~430,000 (Scrypt) |
| RTX 3090 | ~3 H/s | ~250 k H/s | ~260,000 |
| CMP 90HX | ~1.8 H/s | ~180 k H/s | ~155,000 |
| CPU (i7-12700K) | ~30 H/s (all cores) | ~8 k H/s | ~2.6 M |
Yes, CPU is faster than GPU for Scrypt at N=262144 — the memory bandwidth wins. A modern desktop CPU can out-perform an RTX 4090 by 6×. A 10-CPU cluster beats a 10-GPU cluster. This is by design.
What this means for recovery feasibility
430,000 guesses/day (1 GPU)
A dictionary of 14 M common passwords × 50 rules = 700 M variants. Exhausting this takes ~4.5 years on one GPU, ~2 months on 30 GPUs. A well-tuned rule set + hints finishes in hours for realistic cases.
Sweet spot
Under 50 M candidate variants is recoverable within a day on a small farm. That means: a focused custom dictionary + tight masks — not rockyou + dive.rule.
MetaMask vault — much faster
MetaMask stores its encrypted seed in browser LocalStorage under data. The payload is a JSON blob using PBKDF2-HMAC-SHA256 with 10,000 iterations — much weaker than keystore Scrypt.
{
"data": "base64...", // AES-GCM ciphertext
"iv": "base64...",
"salt": "base64...",
"keyMetadata": {
"algorithm": "PBKDF2",
"params": { "iterations": 10000 }
}
}Hashcat mode 26600 handles MetaMask vaults at ~500,000 H/s on an RTX 4090 — roughly 100,000× faster than Ethereum keystore. Recovery of a typical MetaMask password (8–10 chars, word + numbers) is usually done in under an hour.
Realistic recovery workflow
- Identify the Scrypt N value.
N=1024(Parity light wallet) cracks 256× faster thanN=262144. - Build a personal dictionary from password manager exports, old text files, browser autofill dumps — not generic wordlists.
- Short rule set first. best64.rule (77 rules) before dive.rule (99,092 rules). One-shot tests of "did I use this exact word" are cheap.
- Masks for structure. "I always use Word + 4 digits" =
?u?l?l?l?l?l?l?d?d?d?d. - PRINCE for multi-word passphrases — expensive but the only feasible attack on "my dog's name plus my birth year" patterns.
Frequently asked questions
What hashcat mode is used for Ethereum keystore?
Mode 15700 for Scrypt-based v3 keystores (most common). Mode 15600 for the legacy PBKDF2 variant (older MyEtherWallet exports).
Why is Ethereum keystore so slow to crack?
Scrypt N=262144, r=8 requires 256 MB of working memory and ~200 ms of compute per guess — GPU parallelism collapses. A 4090 manages ~4 H/s, not 4 billion.
Can MetaMask vault passwords be recovered?
Yes — MetaMask uses PBKDF2 with only 10,000 iterations. Hashcat mode 26600 runs at ~500,000 H/s on a 4090. Weak passwords fall in minutes.
How is keystore.json different from wallet.dat?
wallet.dat uses SHA-512 iterations (~100 k) — ~55–100 k H/s on GPU. Keystore uses Scrypt — ~4 H/s. Roughly 15,000× harder per guess.
Can I reduce Scrypt parameters to speed up cracking?
No. N/r/p are stored in the keystore and used during verification. Change them and the derived key will not match. Older wallets that shipped with N=1024 are naturally faster.
Submit your keystore for recovery
Because Scrypt is so slow, a tuned attack on a 10-GPU farm is usually the only realistic option for consumers. Pay only on success.