1#!/usr/bin/env python 2 3# This software is Copyright (c) 2012, Dhiru Kholia <dhiru at openwall.com> and 4# it is hereby placed in the public domain. 5# 6# This utility (bitcoin2john.py) is based on jackjack's pywallet.py [1] which 7# is forked from Joric's pywallet.py whose licensing information follows, 8# 9# [1] https://github.com/jackjack-jj/pywallet 10# 11# PyWallet 1.2.1 (Public Domain) 12# http://github.com/joric/pywallet 13# Most of the actual PyWallet code placed in the public domain. 14# PyWallet includes portions of free software, listed below. 15# 16# BitcoinTools (wallet.dat handling code, MIT License) 17# https://github.com/gavinandresen/bitcointools 18# Copyright (c) 2010 Gavin Andresen 19# 20# python-ecdsa (EC_KEY implementation, MIT License) 21# http://github.com/warner/python-ecdsa 22# "python-ecdsa" Copyright (c) 2010 Brian Warner 23# Portions written in 2005 by Peter Pearson and placed in the public domain. 24# 25# SlowAES (aes.py code, Apache 2 License) 26# http://code.google.com/p/slowaes/ 27# Copyright (c) 2008, Josh Davis (http://www.josh-davis.org), 28# Alex Martelli (http://www.aleax.it) 29# Ported from C code written by Laurent Haan (http://www.progressive-coding.com) 30 31missing_dep = [] 32 33try: 34 from bsddb.db import * 35except: 36 from bsddb3.db import * 37 # missing_dep.append('bsddb') 38 39import os, sys, time 40pyw_filename = sys.argv[0].split('/')[len(sys.argv[0].split('/')) - 1] 41pyw_path = os.getcwd() 42 43try: 44 import json 45except: 46 try: 47 import simplejson as json 48 except: 49 sys.stdout.write("json or simplejson package is needed") 50 51import logging 52import struct 53import traceback 54import types 55import string 56import hashlib 57import random 58import math 59import binascii 60 61max_version = 81000 62addrtype = 0 63json_db = {} 64private_keys = [] 65private_hex_keys = [] 66passphrase = "" 67global_merging_message = ["", ""] 68 69wallet_dir = "" 70wallet_name = "" 71 72ko = 1e3 73kio = 1024 74Mo = 1e6 75Mio = 1024 ** 2 76Go = 1e9 77Gio = 1024 ** 3 78To = 1e12 79Tio = 1024 ** 4 80 81prekeys = [binascii.unhexlify("308201130201010420"), binascii.unhexlify("308201120201010420")] 82postkeys = [binascii.unhexlify("a081a530"), binascii.unhexlify("81a530")] 83 84def hash_160(public_key): 85 md = hashlib.new('ripemd160') 86 md.update(hashlib.sha256(public_key).digest()) 87 return md.digest() 88 89def public_key_to_bc_address(public_key): 90 h160 = hash_160(public_key) 91 return hash_160_to_bc_address(h160) 92 93def hash_160_to_bc_address(h160): 94 vh160 = chr(addrtype) + h160 95 h = Hash(vh160) 96 addr = vh160 + h[0:4] 97 return b58encode(addr) 98 99def bc_address_to_hash_160(addr): 100 bytes = b58decode(addr, 25) 101 return bytes[1:21] 102 103__b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' 104__b58base = len(__b58chars) 105 106def b58encode(v): 107 """ encode v, which is a string of bytes, to base58. 108 """ 109 110 long_value = 0 111 for (i, c) in enumerate(v[::-1]): 112 long_value += (256 ** i) * ord(c) 113 114 result = '' 115 while long_value >= __b58base: 116 div, mod = divmod(long_value, __b58base) 117 result = __b58chars[mod] + result 118 long_value = div 119 result = __b58chars[long_value] + result 120 121 # Bitcoin does a little leading-zero-compression: 122 # leading 0-bytes in the input become leading-1s 123 nPad = 0 124 for c in v: 125 if c == '\0': nPad += 1 126 else: break 127 128 return (__b58chars[0] * nPad) + result 129 130def b58decode(v, length): 131 """ decode v into a string of len bytes 132 """ 133 long_value = 0 134 for (i, c) in enumerate(v[::-1]): 135 long_value += __b58chars.find(c) * (__b58base ** i) 136 137 result = '' 138 while long_value >= 256: 139 div, mod = divmod(long_value, 256) 140 result = chr(mod) + result 141 long_value = div 142 result = chr(long_value) + result 143 144 nPad = 0 145 for c in v: 146 if c == __b58chars[0]: nPad += 1 147 else: break 148 149 result = chr(0) * nPad + result 150 if length is not None and len(result) != length: 151 return None 152 153 return result 154 155# end of bitcointools base58 implementation 156 157def Hash(data): 158 return hashlib.sha256(hashlib.sha256(data).digest()).digest() 159 160# bitcointools wallet.dat handling code 161 162def create_env(db_dir): 163 db_env = DBEnv(0) 164 r = db_env.open(db_dir, (DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_THREAD | DB_RECOVER)) 165 return db_env 166 167def parse_CAddress(vds): 168 d = {'ip':'0.0.0.0', 'port':0, 'nTime': 0} 169 try: 170 d['nVersion'] = vds.read_int32() 171 d['nTime'] = vds.read_uint32() 172 d['nServices'] = vds.read_uint64() 173 d['pchReserved'] = vds.read_bytes(12) 174 d['ip'] = socket.inet_ntoa(vds.read_bytes(4)) 175 d['port'] = vds.read_uint16() 176 except: 177 pass 178 return d 179 180def deserialize_CAddress(d): 181 return d['ip'] + ":" + str(d['port']) 182 183def parse_BlockLocator(vds): 184 d = { 'hashes' : [] } 185 nHashes = vds.read_compact_size() 186 for i in xrange(nHashes): 187 d['hashes'].append(vds.read_bytes(32)) 188 return d 189 190def deserialize_BlockLocator(d): 191 result = "Block Locator top: " + d['hashes'][0][::-1].encode('hex_codec') 192 return result 193 194def parse_setting(setting, vds): 195 if setting[0] == "f": # flag (boolean) settings 196 return str(vds.read_boolean()) 197 elif setting[0:4] == "addr": # CAddress 198 d = parse_CAddress(vds) 199 return deserialize_CAddress(d) 200 elif setting == "nTransactionFee": 201 return vds.read_int64() 202 elif setting == "nLimitProcessors": 203 return vds.read_int32() 204 return 'unknown setting' 205 206class SerializationError(Exception): 207 """ Thrown when there's a problem deserializing or serializing """ 208 209def ts(): 210 return int(time.mktime(datetime.now().timetuple())) 211 212def check_postkeys(key, postkeys): 213 for i in postkeys: 214 if key[:len(i)] == i: 215 return True 216 return False 217 218def one_element_in(a, string): 219 for i in a: 220 if i in string: 221 return True 222 return False 223 224def first_read(device, size, prekeys, inc=10000): 225 t0 = ts() - 1 226 try: 227 fd = os.open (device, os.O_RDONLY) 228 except: 229 sys.stdout.write("Can't open %s, check the path or try as root" % device) 230 exit(0) 231 prekey = prekeys[0] 232 data = "" 233 i = 0 234 data = os.read (fd, i) 235 before_contained_key = False 236 contains_key = False 237 ranges = [] 238 239 while i < int(size): 240 if i % (10 * Mio) > 0 and i % (10 * Mio) <= inc: 241 sys.stdout.write("\n%.2f/%.2f Go" % (i / 1e9, size / 1e9)) 242 t = ts() 243 speed = i / (t - t0) 244 ETAts = size / speed + t0 245 d = datetime.fromtimestamp(ETAts) 246 sys.stdout.write(d.strftime(" ETA: %H:%M:%S")) 247 248 try: 249 data = os.read (fd, inc) 250 except Exception as exc: 251 os.lseek(fd, inc, os.SEEK_CUR) 252 sys.stdout.write(str(exc)) 253 i += inc 254 continue 255 256 contains_key = one_element_in(prekeys, data) 257 258 if not before_contained_key and contains_key: 259 ranges.append(i) 260 261 if before_contained_key and not contains_key: 262 ranges.append(i) 263 264 before_contained_key = contains_key 265 266 i += inc 267 268 os.close (fd) 269 return ranges 270 271def shrink_intervals(device, ranges, prekeys, inc=1000): 272 prekey = prekeys[0] 273 nranges = [] 274 fd = os.open (device, os.O_RDONLY) 275 for j in range(len(ranges) / 2): 276 before_contained_key = False 277 contains_key = False 278 bi = ranges[2 * j] 279 bf = ranges[2 * j + 1] 280 281 mini_blocks = [] 282 k = bi 283 while k <= bf + len(prekey) + 1: 284 mini_blocks.append(k) 285 k += inc 286 mini_blocks.append(k) 287 288 for k in range(len(mini_blocks) / 2): 289 mini_blocks[2 * k] -= len(prekey) + 1 290 mini_blocks[2 * k + 1] += len(prekey) + 1 291 292 293 bi = mini_blocks[2 * k] 294 bf = mini_blocks[2 * k + 1] 295 296 os.lseek(fd, bi, 0) 297 298 data = os.read(fd, bf - bi + 1) 299 contains_key = one_element_in(prekeys, data) 300 301 if not before_contained_key and contains_key: 302 nranges.append(bi) 303 304 if before_contained_key and not contains_key: 305 nranges.append(bi + len(prekey) + 1 + len(prekey) + 1) 306 307 before_contained_key = contains_key 308 309 os.close (fd) 310 311 return nranges 312 313def find_offsets(device, ranges, prekeys): 314 prekey = prekeys[0] 315 list_offsets = [] 316 to_read = 0 317 fd = os.open (device, os.O_RDONLY) 318 for i in range(len(ranges) / 2): 319 bi = ranges[2 * i] - len(prekey) - 1 320 os.lseek(fd, bi, 0) 321 bf = ranges[2 * i + 1] + len(prekey) + 1 322 to_read += bf - bi + 1 323 buf = "" 324 for j in range(len(prekey)): 325 buf += "\x00" 326 curs = bi 327 328 while curs <= bf: 329 data = os.read(fd, 1) 330 buf = buf[1:] + data 331 if buf in prekeys: 332 list_offsets.append(curs) 333 curs += 1 334 335 os.close (fd) 336 337 return [to_read, list_offsets] 338 339def read_keys(device, list_offsets): 340 found_hexkeys = [] 341 fd = os.open (device, os.O_RDONLY) 342 for offset in list_offsets: 343 os.lseek(fd, offset + 1, 0) 344 data = os.read(fd, 40) 345 hexkey = data[1:33].encode('hex') 346 after_key = data[33:39].encode('hex') 347 if hexkey not in found_hexkeys and check_postkeys(after_key.decode('hex'), postkeys): 348 found_hexkeys.append(hexkey) 349 350 os.close (fd) 351 352 return found_hexkeys 353 354 355def md5_2(a): 356 return hashlib.md5(a).digest() 357 358def md5_file(nf): 359 fichier = file(nf, 'r').read() 360 return md5_2(fichier) 361 362 363class KEY: 364 365 def __init__ (self): 366 self.prikey = None 367 self.pubkey = None 368 369 def generate (self, secret=None): 370 if secret: 371 exp = int ('0x' + secret.encode ('hex'), 16) 372 self.prikey = ecdsa.SigningKey.from_secret_exponent (exp, curve=secp256k1) 373 else: 374 self.prikey = ecdsa.SigningKey.generate (curve=secp256k1) 375 self.pubkey = self.prikey.get_verifying_key() 376 return self.prikey.to_der() 377 378 def set_privkey (self, key): 379 if len(key) == 279: 380 seq1, rest = der.remove_sequence (key) 381 integer, rest = der.remove_integer (seq1) 382 octet_str, rest = der.remove_octet_string (rest) 383 tag1, cons1, rest, = der.remove_constructed (rest) 384 tag2, cons2, rest, = der.remove_constructed (rest) 385 point_str, rest = der.remove_bitstring (cons2) 386 self.prikey = ecdsa.SigningKey.from_string(octet_str, curve=secp256k1) 387 else: 388 self.prikey = ecdsa.SigningKey.from_der (key) 389 390 def set_pubkey (self, key): 391 key = key[1:] 392 self.pubkey = ecdsa.VerifyingKey.from_string (key, curve=secp256k1) 393 394 def get_privkey (self): 395 _p = self.prikey.curve.curve.p () 396 _r = self.prikey.curve.generator.order () 397 _Gx = self.prikey.curve.generator.x () 398 _Gy = self.prikey.curve.generator.y () 399 encoded_oid2 = der.encode_oid (*(1, 2, 840, 10045, 1, 1)) 400 encoded_gxgy = "\x04" + ("%64x" % _Gx).decode('hex') + ("%64x" % _Gy).decode('hex') 401 param_sequence = der.encode_sequence ( 402 ecdsa.der.encode_integer(1), 403 der.encode_sequence ( 404 encoded_oid2, 405 der.encode_integer (_p), 406 ), 407 der.encode_sequence ( 408 der.encode_octet_string("\x00"), 409 der.encode_octet_string("\x07"), 410 ), 411 der.encode_octet_string (encoded_gxgy), 412 der.encode_integer (_r), 413 der.encode_integer (1), 414 ); 415 encoded_vk = "\x00\x04" + self.pubkey.to_string () 416 return der.encode_sequence ( 417 der.encode_integer (1), 418 der.encode_octet_string (self.prikey.to_string ()), 419 der.encode_constructed (0, param_sequence), 420 der.encode_constructed (1, der.encode_bitstring (encoded_vk)), 421 ) 422 423 def get_pubkey (self): 424 return "\x04" + self.pubkey.to_string() 425 426 def sign (self, hash): 427 sig = self.prikey.sign_digest (hash, sigencode=ecdsa.util.sigencode_der) 428 return sig.encode('hex') 429 430 def verify (self, hash, sig): 431 return self.pubkey.verify_digest (sig, hash, sigdecode=ecdsa.util.sigdecode_der) 432 433def bool_to_int(b): 434 if b: 435 return 1 436 return 0 437 438class BCDataStream(object): 439 def __init__(self): 440 self.input = None 441 self.read_cursor = 0 442 443 def clear(self): 444 self.input = None 445 self.read_cursor = 0 446 447 def write(self, bytes): # Initialize with string of bytes 448 if self.input is None: 449 self.input = bytes 450 else: 451 self.input += bytes 452 453 def map_file(self, file, start): # Initialize with bytes from file 454 self.input = mmap.mmap(file.fileno(), 0, access=mmap.ACCESS_READ) 455 self.read_cursor = start 456 def seek_file(self, position): 457 self.read_cursor = position 458 def close_file(self): 459 self.input.close() 460 461 def read_string(self): 462 # Strings are encoded depending on length: 463 # 0 to 252 : 1-byte-length followed by bytes (if any) 464 # 253 to 65,535 : byte'253' 2-byte-length followed by bytes 465 # 65,536 to 4,294,967,295 : byte '254' 4-byte-length followed by bytes 466 # ... and the Bitcoin client is coded to understand: 467 # greater than 4,294,967,295 : byte '255' 8-byte-length followed by bytes of string 468 # ... but I don't think it actually handles any strings that big. 469 if self.input is None: 470 raise SerializationError("call write(bytes) before trying to deserialize") 471 472 try: 473 length = self.read_compact_size() 474 except IndexError: 475 raise SerializationError("attempt to read past end of buffer") 476 477 return self.read_bytes(length) 478 479 def write_string(self, string): 480 # Length-encoded as with read-string 481 self.write_compact_size(len(string)) 482 self.write(string) 483 484 def read_bytes(self, length): 485 try: 486 result = self.input[self.read_cursor:self.read_cursor + length] 487 self.read_cursor += length 488 return result 489 except IndexError: 490 raise SerializationError("attempt to read past end of buffer") 491 492 return '' 493 494 def read_boolean(self): return self.read_bytes(1)[0] != chr(0) 495 def read_int16(self): return self._read_num('<h') 496 def read_uint16(self): return self._read_num('<H') 497 def read_int32(self): return self._read_num('<i') 498 def read_uint32(self): return self._read_num('<I') 499 def read_int64(self): return self._read_num('<q') 500 def read_uint64(self): return self._read_num('<Q') 501 502 def write_boolean(self, val): return self.write(chr(bool_to_int(val))) 503 def write_int16(self, val): return self._write_num('<h', val) 504 def write_uint16(self, val): return self._write_num('<H', val) 505 def write_int32(self, val): return self._write_num('<i', val) 506 def write_uint32(self, val): return self._write_num('<I', val) 507 def write_int64(self, val): return self._write_num('<q', val) 508 def write_uint64(self, val): return self._write_num('<Q', val) 509 510 def read_compact_size(self): 511 size = self.input[self.read_cursor] 512 if isinstance(size, str): 513 size = ord(self.input[self.read_cursor]) 514 self.read_cursor += 1 515 if size == 253: 516 size = self._read_num('<H') 517 elif size == 254: 518 size = self._read_num('<I') 519 elif size == 255: 520 size = self._read_num('<Q') 521 return size 522 523 def write_compact_size(self, size): 524 if size < 0: 525 raise SerializationError("attempt to write size < 0") 526 elif size < 253: 527 self.write(chr(size)) 528 elif size < 2 ** 16: 529 self.write('\xfd') 530 self._write_num('<H', size) 531 elif size < 2 ** 32: 532 self.write('\xfe') 533 self._write_num('<I', size) 534 elif size < 2 ** 64: 535 self.write('\xff') 536 self._write_num('<Q', size) 537 538 def _read_num(self, format): 539 (i,) = struct.unpack_from(format, self.input, self.read_cursor) 540 self.read_cursor += struct.calcsize(format) 541 return i 542 543 def _write_num(self, format, num): 544 s = struct.pack(format, num) 545 self.write(s) 546 547def open_wallet(walletfile, writable=False): 548 db = DB() 549 DB_TYPEOPEN = DB_RDONLY 550 flags = DB_THREAD | DB_TYPEOPEN 551 try: 552 r = db.open(walletfile, "main", DB_BTREE, flags) 553 except DBError as e: 554 logging.error("{0}:{1}".format(e[0], e[1])) 555 r = True 556 557 if r is not None: 558 logging.error("Couldn't open wallet.dat/main. Try quitting Bitcoin and running this again.") 559 sys.exit(1) 560 561 return db 562 563def inversetxid(txid): 564 if len(txid) is not 64: 565 sys.stdout.write("Bad txid") 566 return "CORRUPTEDTXID:" + txid 567 new_txid = "" 568 for i in range(32): 569 new_txid += txid[62 - 2 * i]; 570 new_txid += txid[62 - 2 * i + 1]; 571 return new_txid 572 573def parse_wallet(db, item_callback): 574 kds = BCDataStream() 575 vds = BCDataStream() 576 577 578 def parse_TxIn(vds): 579 d = {} 580 d['prevout_hash'] = vds.read_bytes(32).encode('hex') 581 d['prevout_n'] = vds.read_uint32() 582 d['scriptSig'] = vds.read_bytes(vds.read_compact_size()).encode('hex') 583 d['sequence'] = vds.read_uint32() 584 return d 585 586 587 def parse_TxOut(vds): 588 d = {} 589 d['value'] = vds.read_int64() / 1e8 590 d['scriptPubKey'] = vds.read_bytes(vds.read_compact_size()).encode('hex') 591 return d 592 593 594 for (key, value) in db.items(): 595 d = { } 596 597 kds.clear(); kds.write(key) 598 vds.clear(); vds.write(value) 599 600 type = kds.read_string() 601 602 d["__key__"] = key 603 d["__value__"] = value 604 d["__type__"] = type 605 606 try: 607 if type == "tx": 608 d["tx_id"] = inversetxid(kds.read_bytes(32).encode('hex_codec')) 609 start = vds.read_cursor 610 d['version'] = vds.read_int32() 611 n_vin = vds.read_compact_size() 612 d['txIn'] = [] 613 for i in xrange(n_vin): 614 d['txIn'].append(parse_TxIn(vds)) 615 n_vout = vds.read_compact_size() 616 d['txOut'] = [] 617 for i in xrange(n_vout): 618 d['txOut'].append(parse_TxOut(vds)) 619 d['lockTime'] = vds.read_uint32() 620 d['tx'] = vds.input[start:vds.read_cursor].encode('hex_codec') 621 d['txv'] = value.encode('hex_codec') 622 d['txk'] = key.encode('hex_codec') 623 elif type == "name": 624 d['hash'] = kds.read_string() 625 d['name'] = vds.read_string() 626 elif type == "version": 627 d['version'] = vds.read_uint32() 628 elif type == "minversion": 629 d['minversion'] = vds.read_uint32() 630 elif type == "setting": 631 d['setting'] = kds.read_string() 632 d['value'] = parse_setting(d['setting'], vds) 633 elif type == "key": 634 d['public_key'] = kds.read_bytes(kds.read_compact_size()) 635 d['private_key'] = vds.read_bytes(vds.read_compact_size()) 636 elif type == "wkey": 637 d['public_key'] = kds.read_bytes(kds.read_compact_size()) 638 d['private_key'] = vds.read_bytes(vds.read_compact_size()) 639 d['created'] = vds.read_int64() 640 d['expires'] = vds.read_int64() 641 d['comment'] = vds.read_string() 642 elif type == "defaultkey": 643 d['key'] = vds.read_bytes(vds.read_compact_size()) 644 elif type == "pool": 645 d['n'] = kds.read_int64() 646 d['nVersion'] = vds.read_int32() 647 d['nTime'] = vds.read_int64() 648 d['public_key'] = vds.read_bytes(vds.read_compact_size()) 649 elif type == "acc": 650 d['account'] = kds.read_string() 651 d['nVersion'] = vds.read_int32() 652 d['public_key'] = vds.read_bytes(vds.read_compact_size()) 653 elif type == "acentry": 654 d['account'] = kds.read_string() 655 d['n'] = kds.read_uint64() 656 d['nVersion'] = vds.read_int32() 657 d['nCreditDebit'] = vds.read_int64() 658 d['nTime'] = vds.read_int64() 659 d['otherAccount'] = vds.read_string() 660 d['comment'] = vds.read_string() 661 elif type == "bestblock": 662 d['nVersion'] = vds.read_int32() 663 # d.update(parse_BlockLocator(vds)) 664 elif type == "ckey": 665 d['public_key'] = kds.read_bytes(kds.read_compact_size()) 666 d['encrypted_private_key'] = vds.read_bytes(vds.read_compact_size()) 667 elif type == "mkey": 668 d['nID'] = kds.read_uint32() 669 d['encrypted_key'] = vds.read_string() 670 d['salt'] = vds.read_string() 671 d['nDerivationMethod'] = vds.read_uint32() 672 d['nDerivationIterations'] = vds.read_uint32() 673 d['otherParams'] = vds.read_string() 674 675 item_callback(type, d) 676 677 except Exception: 678 traceback.print_exc() 679 sys.stdout.write("ERROR parsing wallet.dat, type %s" % type) 680 sys.stdout.write("key data: %s" % key) 681 sys.stdout.write("key data in hex: %s" % key.encode('hex_codec')) 682 sys.stdout.write("value data in hex: %s" % value.encode('hex_codec')) 683 sys.exit(1) 684 685 686# end of bitcointools wallet.dat handling code 687 688# wallet.dat reader / writer 689 690def read_wallet(json_db, walletfile, print_wallet, print_wallet_transactions, transaction_filter, include_balance, vers= -1, FillPool=False): 691 global passphrase 692 crypted = False 693 694 private_keys = [] 695 private_hex_keys = [] 696 697 if vers > -1: 698 global addrtype 699 oldaddrtype = addrtype 700 addrtype = vers 701 702 db = open_wallet(walletfile, writable=FillPool) 703 704 json_db['keys'] = [] 705 json_db['pool'] = [] 706 json_db['tx'] = [] 707 json_db['names'] = {} 708 json_db['ckey'] = [] 709 json_db['mkey'] = {} 710 711 def item_callback(type, d): 712 if type == "tx": 713 json_db['tx'].append({"tx_id" : d['tx_id'], "txin" : d['txIn'], "txout" : d['txOut'], "tx_v" : d['txv'], "tx_k" : d['txk']}) 714 715 elif type == "name": 716 json_db['names'][d['hash']] = d['name'] 717 718 elif type == "version": 719 json_db['version'] = d['version'] 720 721 elif type == "minversion": 722 json_db['minversion'] = d['minversion'] 723 724 elif type == "setting": 725 if not json_db.has_key('settings'): json_db['settings'] = {} 726 json_db["settings"][d['setting']] = d['value'] 727 728 elif type == "defaultkey": 729 json_db['defaultkey'] = public_key_to_bc_address(d['key']) 730 731 elif type == "key": 732 addr = public_key_to_bc_address(d['public_key']) 733 compressed = d['public_key'][0] != '\04' 734 sec = SecretToASecret(PrivKeyToSecret(d['private_key']), compressed) 735 hexsec = ASecretToSecret(sec).encode('hex') 736 private_keys.append(sec) 737 json_db['keys'].append({'addr' : addr, 'sec' : sec, 'hexsec' : hexsec, 'secret' : hexsec, 'pubkey':d['public_key'].encode('hex'), 'compressed':compressed, 'private':d['private_key'].encode('hex')}) 738 739 elif type == "wkey": 740 if not json_db.has_key('wkey'): json_db['wkey'] = [] 741 json_db['wkey']['created'] = d['created'] 742 743 elif type == "pool": 744 """ d['n'] = kds.read_int64() 745 d['nVersion'] = vds.read_int32() 746 d['nTime'] = vds.read_int64() 747 d['public_key'] = vds.read_bytes(vds.read_compact_size())""" 748 try: 749 json_db['pool'].append({'n': d['n'], 'addr': public_key_to_bc_address(d['public_key']), 'addr2': public_key_to_bc_address(d['public_key'].decode('hex')), 'addr3': public_key_to_bc_address(d['public_key'].encode('hex')), 'nTime' : d['nTime'], 'nVersion' : d['nVersion'], 'public_key_hex' : d['public_key'] }) 750 except: 751 json_db['pool'].append({'n': d['n'], 'addr': public_key_to_bc_address(d['public_key']), 'nTime' : d['nTime'], 'nVersion' : d['nVersion'], 'public_key_hex' : d['public_key'].encode('hex') }) 752 753 elif type == "acc": 754 json_db['acc'] = d['account'] 755 sys.stdout.write("Account %s (current key: %s)" % (d['account'], public_key_to_bc_address(d['public_key']))) 756 757 elif type == "acentry": 758 json_db['acentry'] = (d['account'], d['nCreditDebit'], d['otherAccount'], time.ctime(d['nTime']), d['n'], d['comment']) 759 760 elif type == "bestblock": 761 pass 762 # json_db['bestblock'] = d['hashes'][0][::-1].encode('hex_codec') 763 764 elif type == "ckey": 765 crypted = True 766 compressed = d['public_key'][0] != '\04' 767 json_db['keys'].append({ 'pubkey': d['public_key'].encode('hex'), 'addr': public_key_to_bc_address(d['public_key']), 'encrypted_privkey': d['encrypted_private_key'].encode('hex_codec'), 'compressed':compressed}) 768 769 elif type == "mkey": 770 json_db['mkey']['nID'] = d['nID'] 771 json_db['mkey']['encrypted_key'] = d['encrypted_key'].encode('hex_codec') 772 json_db['mkey']['salt'] = d['salt'].encode('hex_codec') 773 json_db['mkey']['nDerivationMethod'] = d['nDerivationMethod'] 774 json_db['mkey']['nDerivationIterations'] = d['nDerivationIterations'] 775 json_db['mkey']['otherParams'] = d['otherParams'] 776 777 if passphrase: 778 res = crypter.SetKeyFromPassphrase(passphrase, d['salt'], d['nDerivationIterations'], d['nDerivationMethod']) 779 if res == 0: 780 logging.error("Unsupported derivation method") 781 sys.exit(1) 782 masterkey = crypter.Decrypt(d['encrypted_key']) 783 crypter.SetKey(masterkey) 784 785 else: 786 json_db[type] = 'unsupported' 787 788 parse_wallet(db, item_callback) 789 790 791 nkeys = len(json_db['keys']) 792 i = 0 793 for k in json_db['keys']: 794 i += 1 795 addr = k['addr'] 796 if addr in json_db['names'].keys(): 797 k["label"] = json_db['names'][addr] 798 k["reserve"] = 0 799 800 db.close() 801 802 crypted = 'salt' in json_db['mkey'] 803 804 if not crypted: 805 sys.stdout.write("%s : this wallet is not encrypted!" % walletfile) 806 return -1 807 808 for k in json_db['keys']: 809 if k['compressed'] and 'secret' in k: 810 k['secret'] += "01" 811 812 if vers > -1: 813 addrtype = oldaddrtype 814 815 return {'crypted':crypted} 816 817 818 819if __name__ == '__main__': 820 821 822 if len(sys.argv) < 2: 823 print >> sys.stderr, "Usage: %s [Bitcoin/Litecoin/PRiVCY wallet (.dat) files]" % sys.argv[0] 824 sys.exit(-1) 825 826 for i in range(1, len(sys.argv)): 827 filename = sys.argv[i] 828 if read_wallet(json_db, filename, True, True, "", False) == -1: 829 continue 830 831 # Use btcrecover/btcrpass.py -> "Bitcoin Core" logic in case of problems 832 # with the code in this file. 833 minversion = json_db.get("minversion", None) 834 if minversion and minversion > max_version: 835 sys.stderr.write("WARNING: %s has previously unseen minversion '%s'!\n" % 836 (os.path.basename(filename), minversion)) 837 838 cry_master = json_db['mkey']['encrypted_key'].decode('hex') 839 cry_salt = json_db['mkey']['salt'].decode('hex') 840 cry_rounds = json_db['mkey']['nDerivationIterations'] 841 cry_method = json_db['mkey']['nDerivationMethod'] 842 843 crypted = 'salt' in json_db['mkey'] 844 845 if not crypted: 846 print >> sys.stderr, "%s : this wallet is not encrypted" % os.path.basename(filename) 847 continue 848 849 for k in json_db['keys']: 850 pass # dirty hack but it works! 851 852 ckey = k['encrypted_privkey'] 853 public_key = k['pubkey'] 854 cry_master = json_db['mkey']['encrypted_key'][-64:] # last two aes blocks should be enough 855 cry_salt = json_db['mkey']['salt'] 856 857 sys.stdout.write("$bitcoin$%s$%s$%s$%s$%s$%s$%s$%s$%s\n" % 858 (len(cry_master), cry_master, len(cry_salt), 859 cry_salt, cry_rounds, len(ckey), ckey, len(public_key), 860 public_key)) 861