electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

bruteforce_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}")