add watchonly cloning and a working wallet recovery for all OS's

This commit is contained in:
jackjack-jj 2013-07-30 15:01:38 +02:00
parent 682f4c1af2
commit a1211a10d7

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python
#-*- coding: utf-8 -*-
pywversion="2.0.16"
pywversion="2.1.0"
never_update=False
#
@ -21,7 +21,7 @@ try:
except:
missing_dep.append('bsddb')
import os, sys, time
import os, sys, time, re
pyw_filename = os.path.basename(__file__)
pyw_path = os.path.dirname(os.path.realpath(__file__))
@ -64,6 +64,9 @@ except:
from datetime import datetime
from subprocess import *
import os
import os.path
import platform
max_version = 81000
addrtype = 0
@ -103,10 +106,14 @@ def iais(a):
else:
return ''
def systype():
if platform.system() == "Darwin":
return 'Mac'
elif platform.system() == "Windows":
return 'Win'
return 'Linux'
def determine_db_dir():
import os
import os.path
import platform
if wallet_dir in "":
if platform.system() == "Darwin":
return os.path.expanduser("~/Library/Application Support/Bitcoin/")
@ -823,10 +830,13 @@ class Crypter_pure(object):
if crypter == 'pycrypto':
crypter = Crypter_pycrypto()
# print "Crypter: pycrypto"
elif crypter == 'ssl':
crypter = Crypter_ssl()
# print "Crypter: ssl"
else:
crypter = Crypter_pure()
# print "Crypter: pure"
logging.warning("pycrypto or libssl not found, decryption may be slow")
##########################################
@ -835,7 +845,11 @@ else:
# secp256k1
_p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2FL
try:
_p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2FL
except:
print "Python 3 is not supported, you need Python 2.7.x"
exit(1)
_r = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141L
_b = 0x0000000000000000000000000000000000000000000000000000000000000007L
_a = 0x0000000000000000000000000000000000000000000000000000000000000000L
@ -1282,6 +1296,327 @@ def parse_setting(setting, vds):
class SerializationError(Exception):
""" Thrown when there's a problem deserializing or serializing """
def search_patterns_on_disk(device, size, inc, patternlist): # inc must be higher than 1k
try:
otype=os.O_RDONLY|os.O_BINARY
except:
otype=os.O_RDONLY
try:
fd = os.open(device, otype)
except Exception as e:
print "Can't open %s, check the path or try as root"%device
print " Error:", e.args
exit(0)
i = 0
data=''
tzero=time.time()
sizetokeep=0
BlocksToInspect=dict(map(lambda x:[x,[]], patternlist))
syst=systype()
lendataloaded=None
writeProgressEvery=100*Mo
while i < int(size) and (lendataloaded!=0 or lendataloaded==None):
if int(i/writeProgressEvery)!=int((i+inc)/writeProgressEvery):
print "%.2f Go read"%(i/1e9)
try:
datakept=data[-sizetokeep:]
data = datakept+os.read(fd, inc)
lendataloaded = len(data)-len(datakept) #should be inc
for text in patternlist:
if text in data:
BlocksToInspect[text].append([i-len(datakept), data, len(datakept)])
pass
sizetokeep=20 # 20 because all the patterns have a len<20. Could be higher.
i += lendataloaded
except Exception as exc:
if lendataloaded%512>0:
raise Exception("SPOD error 1: %d, %d"%(lendataloaded, i-len(datakept)))
os.lseek(fd, lendataloaded, os.SEEK_CUR)
print str(exc)
i += lendataloaded
continue
os.close(fd)
AllOffsets=dict(map(lambda x:[x,[]], patternlist))
for text,blocks in BlocksToInspect.items():
for offset,data,ldk in blocks: #ldk = len(datakept)
offsetslist=[offset+m.start() for m in re.finditer(text, data)]
AllOffsets[text].extend(offsetslist)
AllOffsets['PRFdevice']=device
AllOffsets['PRFdt']=time.time()-tzero
AllOffsets['PRFsize']=i
return AllOffsets
def multiextract(s, ll):
r=[]
cursor=0
for length in ll:
r.append(s[cursor:cursor+length])
cursor+=length
if s[cursor:]!='':
r.append(s[cursor:])
return r
class RecovCkey(object):
def __init__(self, epk, pk):
self.encrypted_pk=epk
self.public_key=pk
self.mkey=None
self.privkey=None
class RecovMkey(object):
def __init__(self, ekey, salt, nditer, ndmethod, nid):
self.encrypted_key=ekey
self.salt=salt
self.iterations=nditer
self.method=ndmethod
self.id=nid
def readpartfile(fd, offset, length): #make everything 512*n because of windows...
rest=offset%512
new_offset=offset-rest
big_length=512*(int((length+rest-1)/512)+1)
os.lseek(fd, new_offset, os.SEEK_SET)
d=os.read(fd, big_length)
return d[rest:rest+length]
def recov_ckey(fd, offset):
d=readpartfile(fd, offset-49, 122)
me=multiextract(d, [1,48,4,4,1])
checks=[]
checks.append([0, '30'])
checks.append([3, '636b6579'])
if sum(map(lambda x:int(me[x[0]]!=x[1].decode('hex')), checks)): #number of false statements
return None
return me
def recov_mkey(fd, offset):
d=readpartfile(fd, offset-72, 84)
me=multiextract(d, [4,48,1,8,4,4,1,2,8,4])
checks=[]
checks.append([0, '43000130'])
checks.append([2, '08'])
checks.append([6, '00'])
checks.append([8, '090001046d6b6579'])
if sum(map(lambda x:int(me[x[0]]!=x[1].decode('hex')), checks)): #number of false statements
return None
return me
def recov_uckey(fd, offset):
checks=[]
d=readpartfile(fd, offset-217, 223)
if d[-7]=='\x26':
me=multiextract(d, [2,1,4,1,32,141,33,2,1,6])
checks.append([0, '3081'])
checks.append([2, '02010104'])
elif d[-7]=='\x46':
d=readpartfile(fd, offset-282, 286)
me=multiextract(d, [2,1,4,1,32,173,65,1,2,5])
checks.append([0, '8201'])
checks.append([2, '02010104'])
checks.append([-1, '460001036b'])
else:
return None
if sum(map(lambda x:int(me[x[0]]!=x[1].decode('hex')), checks)): #number of false statements
return None
return me
def starts_with(s, b):
return len(s)>=len(b) and s[:len(b)]==b
def recov(device, passes, size=102400, inc=10240, outputdir='.'):
if inc%512>0:
inc-=inc%512 #inc must be 512*n on Windows... Don't ask me why...
nameToDBName={'mkey':'\x09\x00\x01\x04mkey','ckey':'\x27\x00\x01\x04ckey','key':'\x00\x01\x03key',}
if not starts_with(device, 'PartialRecoveryFile:'):
r=search_patterns_on_disk(device, size, inc, map(lambda x:nameToDBName[x], ['mkey', 'ckey', 'key']))
f=open(outputdir+'/pywallet_partial_recovery_%d.dat'%ts(), 'w')
f.write(str(r))
f.close()
print "\nRead %.1f Go in %.1f minutes\n"%(r['PRFsize']/1e9, r['PRFdt']/60.0)
else:
prf=device[20:]
f=open(prf, 'r')
content=f.read()
f.close()
cmd=("z = "+content+"")
exec cmd in locals()
r=z
device=r['PRFdevice']
print "\nLoaded %.1f Go from %s\n"%(r['PRFsize']/1e9, device)
try:
otype=os.O_RDONLY|os.O_BINARY
except:
otype=os.O_RDONLY
fd = os.open(device, otype)
mkeys=[]
crypters=[]
syst=systype()
for offset in r[nameToDBName['mkey']]:
s=recov_mkey(fd, offset)
if s==None:
continue
newmkey=RecovMkey(s[1],s[3],int(s[5][::-1].encode('hex'), 16),int(s[4][::-1].encode('hex'), 16),int(s[-1][::-1].encode('hex'), 16))
mkeys.append([offset,newmkey])
print "Found", len(mkeys), 'possible wallets'
ckeys=[]
for offset in r[nameToDBName['ckey']]:
s=recov_ckey(fd, offset)
if s==None:
continue
newckey=RecovCkey(s[1], s[5][:int(s[4].encode('hex'),16)])
ckeys.append([offset,newckey])
print "Found", len(ckeys), 'possible encrypted keys'
uckeys=[]
for offset in r[nameToDBName['key']]:
s=recov_uckey(fd, offset)
if s==None:
continue
uckeys.append(s[4])
print "Found", len(uckeys), 'possible unencrypted keys'
os.close(fd)
list_of_possible_keys_per_master_key=dict(map(lambda x:[x[1],[]], mkeys))
for cko,ck in ckeys:
tl=map(lambda x:[abs(x[0]-cko)]+x, mkeys)
tl=sorted(tl, key=lambda x:x[0])
list_of_possible_keys_per_master_key[tl[0][2]].append(ck)
cpt=0
mki=1
tzero=time.time()
if len(passes)==0:
if len(ckeys)>0:
print "Can't decrypt them as you didn't provide any passphrase."
else:
for mko,mk in mkeys:
list_of_possible_keys=list_of_possible_keys_per_master_key[mk]
sys.stdout.write( "\nPossible wallet #"+str(mki))
sys.stdout.flush()
for ppi,pp in enumerate(passes):
sys.stdout.write( "\n with passphrase #"+str(ppi+1)+" ")
sys.stdout.flush()
failures_in_a_row=0
# print "SKFP params:", pp, mk.salt, mk.iterations, mk.method
res = crypter.SetKeyFromPassphrase(pp, mk.salt, mk.iterations, mk.method)
if res == 0:
print "Unsupported derivation method"
sys.exit(1)
masterkey = crypter.Decrypt(mk.encrypted_key)
crypter.SetKey(masterkey)
for ck in list_of_possible_keys:
if cpt%10==9 and failures_in_a_row==0:
sys.stdout.write('.')
sys.stdout.flush()
if failures_in_a_row>5:
break
crypter.SetIV(Hash(ck.public_key))
secret = crypter.Decrypt(ck.encrypted_pk)
compressed = ck.public_key[0] != '\04'
pkey = EC_KEY(int('0x' + secret.encode('hex'), 16))
if ck.public_key != GetPubKey(pkey, compressed):
failures_in_a_row+=1
else:
failures_in_a_row=0
ck.mkey=mk
ck.privkey=secret
cpt+=1
mki+=1
print "\n"
tone=time.time()
calcspeed=1.0*cpt/(tone-tzero)*60 #calc/min
ckeys_not_decrypted=filter(lambda x:x[1].privkey==None, ckeys)
refused_to_test_all_pps=True
if len(ckeys_not_decrypted)==0:
print "All the found encrypted private keys have been decrypted."
return map(lambda x:x[1].privkey, ckeys)
else:
print "Private keys not decrypted: %d"%len(ckeys_not_decrypted)
print "Trying all the remaining possibilities (%d) might take up to %d minutes."%(len(ckeys_not_decrypted)*len(passes)*len(mkeys),int(len(ckeys_not_decrypted)*len(passes)*len(mkeys)/calcspeed))
cont=raw_input("Do you want to test them? (y/n): ")
while len(cont)==0:
cont=raw_input("Do you want to test them? (y/n): ")
if cont[0]=='y':
refused_to_test_all_pps=False
cpt=0
for dist,mko,mk in tl:
for ppi,pp in enumerate(passes):
res = crypter.SetKeyFromPassphrase(pp, mk.salt, mk.iterations, mk.method)
if res == 0:
logging.error("Unsupported derivation method")
sys.exit(1)
masterkey = crypter.Decrypt(mk.encrypted_key)
crypter.SetKey(masterkey)
for cko,ck in ckeys_not_decrypted:
tl=map(lambda x:[abs(x[0]-cko)]+x, mkeys)
tl=sorted(tl, key=lambda x:x[0])
if mk==tl[0][2]:
continue #because already tested
crypter.SetIV(Hash(ck.public_key))
secret = crypter.Decrypt(ck.encrypted_pk)
compressed = ck.public_key[0] != '\04'
pkey = EC_KEY(int('0x' + secret.encode('hex'), 16))
if ck.public_key == GetPubKey(pkey, compressed):
ck.mkey=mk
ck.privkey=secret
cpt+=1
print
ckeys_not_decrypted=filter(lambda x:x[1].privkey==None, ckeys)
if len(ckeys_not_decrypted)==0:
print "All the found encrypted private keys have been finally decrypted."
elif not refused_to_test_all_pps:
print "Private keys not decrypted: %d"%len(ckeys_not_decrypted)
print "Try another password, check the size of your partition or seek help"
uncrypted_ckeys=filter(lambda x:x!=None, map(lambda x:x[1].privkey, ckeys))
uckeys.extend(uncrypted_ckeys)
return uckeys
def ts():
return int(time.mktime(datetime.now().timetuple()))
@ -1906,7 +2241,7 @@ def merge_wallets(wadir, wa, wbdir, wb, wrdir, wr, passphrase_a, passphrase_b, p
'nID' : 1,
'otherParams' : ''.decode('hex'),
"salt": NPP_salt
})
})
dbr.close()
@ -1931,7 +2266,7 @@ def random_string(l, alph="0123456789abcdef"):
r+=alph[int(la*(random.random()))]
return r
def update_wallet(db, type, data):
def update_wallet(db, types, datas, paramsAreLists=False):
"""Write a single item to the wallet.
db must be open with writable=True.
type and data are the type code and data dictionary as parse_wallet would
@ -1939,84 +2274,95 @@ def update_wallet(db, type, data):
data's __key__, __value__ and __type__ are ignored; only the primary data
fields are used.
"""
d = data
kds = BCDataStream()
vds = BCDataStream()
if not paramsAreLists:
types=[types]
datas=[datas]
if len(types)!=len(datas):
raise Exception("UpdateWallet: sizes are different")
# Write the type code to the key
kds.write_string(type)
vds.write("") # Ensure there is something
for it,type in enumerate(types):
data=datas[it]
d = data
kds = BCDataStream()
vds = BCDataStream()
try:
if type == "tx":
# raise NotImplementedError("Writing items of type 'tx'")
kds.write(d['txi'][6:].decode('hex_codec'))
vds.write(d['txv'].decode('hex_codec'))
elif type == "name":
kds.write_string(d['hash'])
vds.write_string(d['name'])
elif type == "version":
vds.write_uint32(d['version'])
elif type == "minversion":
vds.write_uint32(d['minversion'])
elif type == "setting":
raise NotImplementedError("Writing items of type 'setting'")
kds.write_string(d['setting'])
#d['value'] = parse_setting(d['setting'], vds)
elif type == "key":
kds.write_string(d['public_key'])
vds.write_string(d['private_key'])
elif type == "wkey":
kds.write_string(d['public_key'])
vds.write_string(d['private_key'])
vds.write_int64(d['created'])
vds.write_int64(d['expires'])
vds.write_string(d['comment'])
elif type == "defaultkey":
vds.write_string(d['key'])
elif type == "pool":
kds.write_int64(d['n'])
vds.write_int32(d['nVersion'])
vds.write_int64(d['nTime'])
vds.write_string(d['public_key'])
elif type == "acc":
kds.write_string(d['account'])
vds.write_int32(d['nVersion'])
vds.write_string(d['public_key'])
elif type == "acentry":
kds.write_string(d['account'])
kds.write_uint64(d['n'])
vds.write_int32(d['nVersion'])
vds.write_int64(d['nCreditDebit'])
vds.write_int64(d['nTime'])
vds.write_string(d['otherAccount'])
vds.write_string(d['comment'])
elif type == "bestblock":
vds.write_int32(d['nVersion'])
vds.write_compact_size(len(d['hashes']))
for h in d['hashes']:
vds.write(h)
elif type == "ckey":
kds.write_string(d['public_key'])
vds.write_string(d['encrypted_private_key'])
elif type == "mkey":
kds.write_uint32(d['nID'])
vds.write_string(d['encrypted_key'])
vds.write_string(d['salt'])
vds.write_uint32(d['nDerivationMethod'])
vds.write_uint32(d['nDerivationIterations'])
vds.write_string(d['otherParams'])
# Write the type code to the key
kds.write_string(type)
vds.write("") # Ensure there is something
else:
print "Unknown key type: "+type
try:
if type == "tx":
# raise NotImplementedError("Writing items of type 'tx'")
kds.write(d['txi'][6:].decode('hex_codec'))
vds.write(d['txv'].decode('hex_codec'))
elif type == "name":
kds.write_string(d['hash'])
vds.write_string(d['name'])
elif type == "version":
vds.write_uint32(d['version'])
elif type == "minversion":
vds.write_uint32(d['minversion'])
elif type == "setting":
raise NotImplementedError("Writing items of type 'setting'")
kds.write_string(d['setting'])
#d['value'] = parse_setting(d['setting'], vds)
elif type == "key":
kds.write_string(d['public_key'])
vds.write_string(d['private_key'])
elif type == "wkey":
kds.write_string(d['public_key'])
vds.write_string(d['private_key'])
vds.write_int64(d['created'])
vds.write_int64(d['expires'])
vds.write_string(d['comment'])
elif type == "defaultkey":
vds.write_string(d['key'])
elif type == "pool":
kds.write_int64(d['n'])
vds.write_int32(d['nVersion'])
vds.write_int64(d['nTime'])
vds.write_string(d['public_key'])
elif type == "acc":
kds.write_string(d['account'])
vds.write_int32(d['nVersion'])
vds.write_string(d['public_key'])
elif type == "acentry":
kds.write_string(d['account'])
kds.write_uint64(d['n'])
vds.write_int32(d['nVersion'])
vds.write_int64(d['nCreditDebit'])
vds.write_int64(d['nTime'])
vds.write_string(d['otherAccount'])
vds.write_string(d['comment'])
elif type == "bestblock":
vds.write_int32(d['nVersion'])
vds.write_compact_size(len(d['hashes']))
for h in d['hashes']:
vds.write(h)
elif type == "ckey":
kds.write_string(d['public_key'])
vds.write_string(d['encrypted_private_key'])
elif type == "mkey":
kds.write_uint32(d['nID'])
vds.write_string(d['encrypted_key'])
vds.write_string(d['salt'])
vds.write_uint32(d['nDerivationMethod'])
vds.write_uint32(d['nDerivationIterations'])
vds.write_string(d['otherParams'])
# Write the key/value pair to the database
db.put(kds.input, vds.input)
else:
print "Unknown key type: "+type
except Exception, e:
print("ERROR writing to wallet.dat, type %s"%type)
print("data dictionary: %r"%data)
traceback.print_exc()
# Write the key/value pair to the database
db.put(kds.input, vds.input)
except Exception, e:
print("ERROR writing to wallet.dat, type %s"%type)
print("data dictionary: %r"%data)
traceback.print_exc()
def create_new_wallet(db_env, walletfile, version):
db_out = DB(db_env)
@ -2106,7 +2452,7 @@ def read_wallet(json_db, db_env, walletfile, print_wallet, print_wallet_transact
addr = public_key_to_bc_address(d['public_key'])
compressed = d['public_key'][0] != '\04'
sec = SecretToASecret(PrivKeyToSecret(d['private_key']), compressed)
hexsec = ASecretToSecret(sec).encode('hex')
hexsec = ASecretToSecret(sec).encode('hex')[:32]
private_keys.append(sec)
addr_to_keys[addr]=[hexsec, d['public_key'].encode('hex')]
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')})
@ -2227,7 +2573,7 @@ def read_wallet(json_db, db_env, walletfile, print_wallet, print_wallet_transact
sec = SecretToASecret(secret, compressed)
k['sec'] = sec
k['hexsec'] = sec
k['hexsec'] = secret[:32].encode('hex')
k['secret'] = secret.encode('hex')
k['compressed'] = compressed
addr_to_keys[k['addr']]=[sec, k['pubkey']]
@ -2363,6 +2709,7 @@ def keyinfo(sec, keyishex):
print "Address (%s): %s" % ( aversions[addrtype], addr )
print "Privkey (%s): %s" % ( aversions[addrtype], SecretToASecret(secret, compressed) )
print "Hexprivkey: %s" % secret.encode('hex')
print "Hash160: %s"%(bc_address_to_hash_160(addr).encode('hex'))
return True
@ -4306,9 +4653,56 @@ def restart_pywallet():
def start_pywallet():
a=Popen("python "+pyw_path+"/"+pyw_filename+" --web --port "+str(webport)+" --wait 3", shell=True, bufsize=-1, stdout=PIPE).stdout
a.close()
def clone_wallet(parentPath, clonePath):
types,datas=[],[]
parentdir,parentname=os.path.split(parentPath)
wdir,wname=os.path.split(clonePath)
db_env = create_env(parentdir)
read_wallet(json_db, db_env, parentname, True, True, "", False)
types.append('version')
datas.append({'version':json_db['version']})
types.append('defaultkey')
datas.append({'key':json_db['defaultkey']})
for k in json_db['keys']:
types.append('ckey')
datas.append({'public_key':k['pubkey'].decode('hex'),'encrypted_private_key':random_string(96).decode('hex')})
for k in json_db['pool']:
types.append('pool')
datas.append({'n':k['n'],'nVersion':k['nVersion'],'nTime':k['nTime'],'public_key':k['public_key_hex'].decode('hex')})
for addr,label in json_db['names'].items():
types.append('name')
datas.append({'hash':addr,'name':'Watch:'+label})
db_env = create_env(wdir)
create_new_wallet(db_env, wname, 60000)
db = open_wallet(db_env, wname, True)
NPP_salt=random_string(16).decode('hex')
NPP_rounds=int(50000+random.random()*20000)
NPP_method=0
NPP_MK=random_string(64).decode('hex')
crypter.SetKeyFromPassphrase(random_string(64), NPP_salt, NPP_rounds, NPP_method)
NPP_EMK = crypter.Encrypt(NPP_MK)
update_wallet(db, 'mkey', {
"encrypted_key": NPP_EMK,
'nDerivationIterations' : NPP_rounds,
'nDerivationMethod' : NPP_method,
'nID' : 1,
'otherParams' : ''.decode('hex'),
"salt": NPP_salt
})
db.close()
read_wallet(json_db, db_env, wname, True, True, "", False)
db = open_wallet(db_env, wname, writable=True)
update_wallet(db, types, datas, True)
db.close()
print "Wallet successfully cloned to:\n %s"%clonePath
import thread
md5_last_pywallet = [False, ""]
@ -4380,7 +4774,7 @@ if __name__ == '__main__':
help="recover your deleted keys, use with recov_size and recov_device")
parser.add_option("--recov_device", dest="recov_device",
help="device to read (e.g. /dev/sda1)")
help="device to read (e.g. /dev/sda1 or E: or a file)")
parser.add_option("--recov_size", dest="recov_size",
help="number of bytes to read (e.g. 20Mo or 50Gio)")
@ -4388,6 +4782,12 @@ if __name__ == '__main__':
parser.add_option("--recov_outputdir", dest="recov_outputdir",
help="output directory where the recovered wallet will be put")
parser.add_option("--clone_watchonly_from", dest="clone_watchonly_from",
help="path of the original wallet")
parser.add_option("--clone_watchonly_to", dest="clone_watchonly_to",
help="path of the resulting watch-only wallet")
parser.add_option("--dont_check_walletversion", dest="dcv", action="store_true",
help="don't check if wallet version > %d before running (WARNING: this may break your wallet, be sure you know what you do)"%max_version)
@ -4415,34 +4815,81 @@ if __name__ == '__main__':
if options.passphrase:
passphrase = options.passphrase
if options.clone_watchonly_from is not None and options.clone_watchonly_to:
clone_wallet(options.clone_watchonly_from, options.clone_watchonly_to)
exit(0)
if options.recover:
if options.recov_size is None or options.recov_device is None or options.recov_outputdir is None:
print("You must provide the device, the number of bytes to read and the output directory")
exit(0)
device = options.recov_device
if len(device) in [2,3] and device[1]==':':
device="\\\\.\\"+device
size = read_device_size(options.recov_size)
r = first_read(device, size, prekeys, 10000)
r = shrink_intervals(device, r, prekeys, 1000)
[to_read, o] = find_offsets(device, r, prekeys)
print("%dko to read"%(to_read/1000))
keys = read_keys(device, o)
print("%d key%s found"%(len(keys), iais(len(keys))))
passphraseRecov=''
while passphraseRecov=='':
passphraseRecov=raw_input("Enter the passphrase for the wallet that will contain all the recovered keys: ")
passphrase=passphraseRecov
passes=[]
p=' '
print '\nEnter the possible passphrases used in your deleted wallets.'
print "Don't forget that more passphrases = more time to test the possibilities."
print 'Write one passphrase per line and end with an empty line.'
while p!='':
p=raw_input("Possible passphrase: ")
if p!='':
passes.append(p)
print "\nStarting recovery."
recoveredKeys=recov(device, passes, size, 10240, options.recov_outputdir)
recoveredKeys=list(set(recoveredKeys))
# print recoveredKeys[0:5]
db_env = create_env(options.recov_outputdir)
recov_wallet_name = "recovered_wallet_%s.dat"%ts()
create_new_wallet(db_env, recov_wallet_name, 32500)
create_new_wallet(db_env, recov_wallet_name, 32500)
if passphraseRecov!="I don't want to put a password on the recovered wallet and I know what can be the consequences.":
db = open_wallet(db_env, recov_wallet_name, True)
NPP_salt=random_string(16).decode('hex')
NPP_rounds=int(50000+random.random()*20000)
NPP_method=0
NPP_MK=random_string(64).decode('hex')
crypter.SetKeyFromPassphrase(passphraseRecov, NPP_salt, NPP_rounds, NPP_method)
NPP_EMK = crypter.Encrypt(NPP_MK)
update_wallet(db, 'mkey', {
"encrypted_key": NPP_EMK,
'nDerivationIterations' : NPP_rounds,
'nDerivationMethod' : NPP_method,
'nID' : 1,
'otherParams' : ''.decode('hex'),
"salt": NPP_salt
})
db.close()
read_wallet(json_db, db_env, recov_wallet_name, True, True, "", False)
db = open_wallet(db_env, recov_wallet_name, True)
i = 0
for sec in keys:
print("\nImporting key %4d/%d:"%(i+1, len(keys)))
print "\n\nImporting:"
for sec in recoveredKeys:
sec=sec.encode('hex')
print("\nKey %4d/%d imported:"%(i+1, len(recoveredKeys)))
importprivkey(db, sec, "recovered: %s"%sec, None, True)
i += 1
db.close()
print("\n\nThe new wallet %s/%s contains the %d recovered key%s\n"%(options.recov_outputdir, recov_wallet_name, len(keys), iais(len(keys))))
print("\n\nThe new wallet %s/%s contains the %d recovered key%s"%(options.recov_outputdir, recov_wallet_name, len(recoveredKeys), iais(len(recoveredKeys))))
exit(0)