From 303945f73c29a85881b4279dd48f8caa16dff3dd Mon Sep 17 00:00:00 2001 From: Banaanhangwagen Date: Fri, 16 Sep 2022 21:40:50 +0200 Subject: [PATCH] Create bisq2hashcat.py --- tools/bisq2hashcat.py | 127 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 tools/bisq2hashcat.py diff --git a/tools/bisq2hashcat.py b/tools/bisq2hashcat.py new file mode 100644 index 000000000..253d33f07 --- /dev/null +++ b/tools/bisq2hashcat.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python + +# This software is Copyright (c) 2017, Dhiru Kholia +# and it is hereby released under GPL v2 license. +# +# Major parts are borrowed from the "btcrecover" program which is, +# Copyright (C) 2014-2016 Christopher Gurnee and under GPL v2. +# +# See https://github.com/gurnec/btcrecover for details. +# +# References, +# +# https://github.com/gurnec/btcrecover/blob/master/btcrecover/btcrpass.py +# + +# Updated script in order to work with Bisq wallet v1.9.5 (2022) +# Author: +# https://github.com/Banaanhangwagen +# License: MIT + +import os +import sys +import base64 +import binascii + +# The protobuf parsing code is borrowed from btcrecover, and was +# initially automatically generated by Google's protoc utility. +# The precompiled file is shipped in protobuf/wallet_pb2.py with John. +from protobuf.wallet_pb2 import * + + +def process_file(filename): + bname = os.path.basename(filename) + try: + f = open(filename, "rb") + data = f.read() + except IOError: + e = sys.exc_info()[1] + sys.stderr.write("%s\n" % str(e)) + return + + if "wallet" in bname or b"org.bitcoin.production" in data: + # sys.stderr.write("[WARNING] Cracking .wallet files is a very slow process, try cracking the associated .key file instead!\n") + version = 3 # MultiBit Classic .wallet file + # def is_wallet_file(wallet_file) from btcrecover + wallet_file = open(filename, "rb") + wallet_file.seek(0) + is_valid_bitcoinj_wallet = False + if wallet_file.read(1) == b"\x0a": # protobuf field number 1 of type length-delimited + network_identifier_len = ord(wallet_file.read(1)) + if 1 <= network_identifier_len < 128: + wallet_file.seek(2 + network_identifier_len) + c = wallet_file.read(1) + if c and c in b"\x12\x1a": # field number 2 or 3 of type length-delimited + is_valid_bitcoinj_wallet = True + if is_valid_bitcoinj_wallet: + pb_wallet = Wallet() + pb_wallet.ParseFromString(data) + if pb_wallet.encryption_type == Wallet.UNENCRYPTED: + raise ValueError("bitcoinj wallet is not encrypted") + if pb_wallet.encryption_type != Wallet.ENCRYPTED_SCRYPT_AES: + raise NotImplementedError("Unsupported bitcoinj encryption type " + str(pb_wallet.encryption_type)) + if not pb_wallet.HasField("encryption_parameters"): + raise ValueError("bitcoinj wallet is missing its scrypt encryption parameters") + for key in pb_wallet.key: + if key.type in (Key.ENCRYPTED_SCRYPT_AES, Key.DETERMINISTIC_KEY) and key.HasField("encrypted_data"): + encrypted_len = len(key.encrypted_data.encrypted_private_key) + if encrypted_len == 48: + # only need the final 2 encrypted blocks (half of it padding) plus the scrypt parameters + part_encrypted_key = key.encrypted_data.encrypted_private_key[-32:] + salt = pb_wallet.encryption_parameters.salt + n = pb_wallet.encryption_parameters.n + r = pb_wallet.encryption_parameters.r + p = pb_wallet.encryption_parameters.p + encrypted_data = binascii.hexlify(part_encrypted_key).decode("ascii") + salt = binascii.hexlify(salt).decode("ascii") + sys.stdout.write("%s:$bisq$%d*%s*%s*%s*%s*%s\n" % (bname, version, n, r, p, salt, encrypted_data)) + return + return + + pdata = b"".join(data.split()) + if len(pdata) < 64: + sys.stderr.write("%s: Short length for a bisq wallet file!\n" % bname) + return + + try: + pdata = base64.b64decode(pdata[:64]) + + if pdata.startswith(b"Salted__"): + version = 1 # MultiBit Classic + else: + version = 2 # MultiBit HD possibly? We need more tests! + except: + version = 2 # MultiBit HD possibly? + + if version == 1: + encrypted_data = pdata[16:48] # two AES blocks + salt = pdata[8:16] + encrypted_data = binascii.hexlify(encrypted_data).decode("ascii") + salt = binascii.hexlify(salt).decode("ascii") + sys.stdout.write("%s:$bisq$%d*%s*%s\n" % (bname, version, salt, encrypted_data)) + return + else: + version = 2 + # sanity check but not a very good one + if "wallet" not in bname and "aes" not in bname: + sys.stderr.write("%s: Make sure that this is a Bisq wallet!\n" % bname) + # two possibilities + iv = data[:16] # v0.5.0+ + block_iv = data[16:32] # v0.5.0+ + block_noiv = data[:16] # encrypted using hardcoded iv, < v0.5.0 + iv = binascii.hexlify(iv).decode("ascii") + block_iv = binascii.hexlify(block_iv).decode("ascii") + block_noiv = binascii.hexlify(block_noiv).decode("ascii") + sys.stdout.write("%s:$bisq$%d*%s*%s*%s\n" % (bname, version, iv, block_iv, block_noiv)) + return + + f.close() + +if __name__ == "__main__": + if len(sys.argv) < 2: + sys.stderr.write("Usage: %s [Bisq wallet file (.wallet)]\n" % sys.argv[0]) + # sys.stderr.write("\nMultiBit Classic -> for a wallet named 'xyz', we need the xyz-data/key-backup/xyz*.key OR xyz-data/wallet-backup/xyz*.wallet file\n") + sys.exit(1) + + for j in range(1, len(sys.argv)): + process_file(sys.argv[j])