tbruteforce_pw.py - electrum - Electrum Bitcoin wallet
HTML git clone https://git.parazyd.org/electrum
DIR Log
DIR Files
DIR Refs
DIR Submodules
---
tbruteforce_pw.py (3913B)
---
1 #!/usr/bin/env python3
2 #
3 # This script is just a demonstration how one could go about bruteforcing an
4 # Electrum wallet file password. As it is pure-python and runs in the CPU,
5 # it is horribly slow. It could be changed to utilise multiple threads
6 # but any serious attempt would need at least GPU acceleration.
7 #
8 # There are two main types of password encryption that need to be disambiguated
9 # for Electrum wallets:
10 # (1) keystore-encryption: The wallet file itself is mostly plaintext (json),
11 # only the Bitcoin private keys themselves are encrypted.
12 # (e.g. seed words, xprv are encrypted; addresses are not)
13 # Even in memory (at runtime), the private keys are typically
14 # stored encrypted, and only when needed the user is prompted
15 # for their password to decrypt the keys briefly.
16 # (2) storage-encryption: The file itself is encrypted. When opened in a text editor,
17 # it is base64 ascii text. Normally storage-encrypted wallets
18 # also have keystore-encryption (unless they don't have private keys).
19 # Storage-encryption was introduced in Electrum 2.8, keystore-encryption predates that.
20 # Newly created wallets in modern Electrum have storage-encryption enabled by default.
21 #
22 # Storage encryption uses a stronger KDF than keystore-encryption.
23 # As is, this script can test around ~1000 passwords per second for storage-encryption.
24
25 import sys
26 from string import digits, ascii_uppercase, ascii_lowercase
27 from itertools import product
28 from typing import Callable
29 from functools import partial
30
31 from electrum.wallet import Wallet, Abstract_Wallet
32 from electrum.storage import WalletStorage
33 from electrum.wallet_db import WalletDB
34 from electrum.simple_config import SimpleConfig
35 from electrum.util import InvalidPassword
36
37
38 ALLOWED_CHARS = digits + ascii_uppercase + ascii_lowercase
39 MAX_PASSWORD_LEN = 12
40
41
42 def test_password_for_storage_encryption(storage: WalletStorage, password: str) -> bool:
43 try:
44 storage.decrypt(password)
45 except InvalidPassword:
46 return False
47 else:
48 return True
49
50
51 def test_password_for_keystore_encryption(wallet: Abstract_Wallet, password: str) -> bool:
52 try:
53 wallet.check_password(password)
54 except InvalidPassword:
55 return False
56 else:
57 return True
58
59
60 def bruteforce_loop(test_password: Callable[[str], bool]) -> str:
61 num_tested = 0
62 for pw_len in range(1, MAX_PASSWORD_LEN + 1):
63 for pw_tuple in product(ALLOWED_CHARS, repeat=pw_len):
64 password = "".join(pw_tuple)
65 if test_password(password):
66 return password
67 num_tested += 1
68 if num_tested % 5000 == 0:
69 print(f"> tested {num_tested} passwords so far... most recently tried: {password!r}")
70
71
72 if __name__ == '__main__':
73 if len(sys.argv) < 2:
74 print("ERROR. usage: bruteforce_pw.py <path_to_wallet_file>")
75 sys.exit(1)
76 path = sys.argv[1]
77
78 config = SimpleConfig()
79 storage = WalletStorage(path)
80 if not storage.file_exists():
81 print(f"ERROR. wallet file not found at path: {path}")
82 sys.exit(1)
83 if storage.is_encrypted():
84 test_password = partial(test_password_for_storage_encryption, storage)
85 print(f"wallet found: with storage encryption.")
86 else:
87 db = WalletDB(storage.read(), manual_upgrades=True)
88 wallet = Wallet(db, storage, config=config)
89 if not wallet.has_password():
90 print("wallet found but it is not encrypted.")
91 sys.exit(0)
92 test_password = partial(test_password_for_keystore_encryption, wallet)
93 print(f"wallet found: with keystore encryption.")
94 password = bruteforce_loop(test_password)
95 print(f"====================")
96 print(f"password found: {password}")