URI: 
       tscripts: add script to showcase bruteforcing wallet file password - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit c9bf1d4c800d8a07ae04de97f27cee51707f10de
   DIR parent f265acd2348697170ec7d23af69ebb7af91de0e7
  HTML Author: SomberNight <somber.night@protonmail.com>
       Date:   Tue,  1 Sep 2020 18:15:37 +0200
       
       scripts: add script to showcase bruteforcing wallet file password
       
       Diffstat:
         A electrum/scripts/bruteforce_pw.py   |      96 +++++++++++++++++++++++++++++++
       
       1 file changed, 96 insertions(+), 0 deletions(-)
       ---
   DIR diff --git a/electrum/scripts/bruteforce_pw.py b/electrum/scripts/bruteforce_pw.py
       t@@ -0,0 +1,96 @@
       +#!/usr/bin/env python3
       +#
       +# This script is just a demonstration how one could go about bruteforcing an
       +# Electrum wallet file password. As it is pure-python and runs in the CPU,
       +# it is horribly slow. It could be changed to utilise multiple threads
       +# but any serious attempt would need at least GPU acceleration.
       +#
       +# There are two main types of password encryption that need to be disambiguated
       +# for Electrum wallets:
       +# (1) keystore-encryption: The wallet file itself is mostly plaintext (json),
       +#                          only the Bitcoin private keys themselves are encrypted.
       +#                          (e.g. seed words, xprv are encrypted; addresses are not)
       +#                          Even in memory (at runtime), the private keys are typically
       +#                          stored encrypted, and only when needed the user is prompted
       +#                          for their password to decrypt the keys briefly.
       +# (2) storage-encryption: The file itself is encrypted. When opened in a text editor,
       +#                         it is base64 ascii text. Normally storage-encrypted wallets
       +#                         also have keystore-encryption (unless they don't have private keys).
       +# Storage-encryption was introduced in Electrum 2.8, keystore-encryption predates that.
       +# Newly created wallets in modern Electrum have storage-encryption enabled by default.
       +#
       +# Storage encryption uses a stronger KDF than keystore-encryption.
       +# As is, this script can test around ~1000 passwords per second for storage-encryption.
       +
       +import sys
       +from string import digits, ascii_uppercase, ascii_lowercase
       +from itertools import product
       +from typing import Callable
       +from functools import partial
       +
       +from electrum.wallet import Wallet, Abstract_Wallet
       +from electrum.storage import WalletStorage
       +from electrum.wallet_db import WalletDB
       +from electrum.simple_config import SimpleConfig
       +from electrum.util import InvalidPassword
       +
       +
       +ALLOWED_CHARS = digits + ascii_uppercase + ascii_lowercase
       +MAX_PASSWORD_LEN = 12
       +
       +
       +def test_password_for_storage_encryption(storage: WalletStorage, password: str) -> bool:
       +    try:
       +        storage.decrypt(password)
       +    except InvalidPassword:
       +        return False
       +    else:
       +        return True
       +
       +
       +def test_password_for_keystore_encryption(wallet: Abstract_Wallet, password: str) -> bool:
       +    try:
       +        wallet.check_password(password)
       +    except InvalidPassword:
       +        return False
       +    else:
       +        return True
       +
       +
       +def bruteforce_loop(test_password: Callable[[str], bool]) -> str:
       +    num_tested = 0
       +    for pw_len in range(1, MAX_PASSWORD_LEN + 1):
       +        for pw_tuple in product(ALLOWED_CHARS, repeat=pw_len):
       +            password = "".join(pw_tuple)
       +            if test_password(password):
       +                return password
       +            num_tested += 1
       +            if num_tested % 5000 == 0:
       +                print(f"> tested {num_tested} passwords so far... most recently tried: {password!r}")
       +
       +
       +if __name__ == '__main__':
       +    if len(sys.argv) < 2:
       +        print("ERROR. usage: bruteforce_pw.py <path_to_wallet_file>")
       +        sys.exit(1)
       +    path = sys.argv[1]
       +
       +    config = SimpleConfig()
       +    storage = WalletStorage(path)
       +    if not storage.file_exists():
       +        print(f"ERROR. wallet file not found at path: {path}")
       +        sys.exit(1)
       +    if storage.is_encrypted():
       +        test_password = partial(test_password_for_storage_encryption, storage)
       +        print(f"wallet found: with storage encryption.")
       +    else:
       +        db = WalletDB(storage.read(), manual_upgrades=True)
       +        wallet = Wallet(db, storage, config=config)
       +        if not wallet.has_password():
       +            print("wallet found but it is not encrypted.")
       +            sys.exit(0)
       +        test_password = partial(test_password_for_keystore_encryption, wallet)
       +        print(f"wallet found: with keystore encryption.")
       +    password = bruteforce_loop(test_password)
       +    print(f"====================")
       +    print(f"password found: {password}")