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