To whom it may concern - I added encrypted wallets support this morning -
Here is the shorter, more readable version - PoC and simultaneously the unit test.
#!/usr/bin/env python
# Bitcoin wallet keys AES encryption / decryption (see http://github.com/joric/pywallet)
# Uses pycrypto or libssl or libeay32.dll or aes.py from http://code.google.com/p/slowaes
crypter = None
try:
from Crypto.Cipher import AES
crypter = 'pycrypto'
except:
pass
class Crypter_pycrypto( object ):
def SetKeyFromPassphrase(self, vKeyData, vSalt, nDerivIterations, nDerivationMethod):
data = vKeyData + vSalt
for i in range(nDerivIterations):
data = hashlib.sha512(data).digest()
self.SetKey(data[0:32])
self.SetIV(data[32:32+16])
return len(data)
def SetKey(self, key):
self.chKey = key
def SetIV(self, iv):
self.chIV = iv[0:16]
def Encrypt(self, data):
return AES.new(self.chKey,AES.MODE_CBC,self.chIV).encrypt(data)[0:32]
def Decrypt(self, data):
return AES.new(self.chKey,AES.MODE_CBC,self.chIV).decrypt(data)[0:32]
try:
if not crypter:
import ctypes
import ctypes.util
ssl = ctypes.cdll.LoadLibrary (ctypes.util.find_library ('ssl') or 'libeay32')
crypter = 'ssl'
except:
pass
class Crypter_ssl(object):
def __init__(self):
self.chKey = ctypes.create_string_buffer (32)
self.chIV = ctypes.create_string_buffer (16)
def SetKeyFromPassphrase(self, vKeyData, vSalt, nDerivIterations, nDerivationMethod):
strKeyData = ctypes.create_string_buffer (vKeyData)
chSalt = ctypes.create_string_buffer (vSalt)
return ssl.EVP_BytesToKey(ssl.EVP_aes_256_cbc(), ssl.EVP_sha512(), chSalt, strKeyData,
len(vKeyData), nDerivIterations, ctypes.byref(self.chKey), ctypes.byref(self.chIV))
def SetKey(self, key):
self.chKey = ctypes.create_string_buffer(key)
def SetIV(self, iv):
self.chIV = ctypes.create_string_buffer(iv)
def Encrypt(self, data):
buf = ctypes.create_string_buffer(len(data) + 16)
written = ctypes.c_int(0)
final = ctypes.c_int(0)
ctx = ssl.EVP_CIPHER_CTX_new()
ssl.EVP_CIPHER_CTX_init(ctx)
ssl.EVP_EncryptInit_ex(ctx, ssl.EVP_aes_256_cbc(), None, self.chKey, self.chIV)
ssl.EVP_EncryptUpdate(ctx, buf, ctypes.byref(written), data, len(data))
output = buf.raw[:written.value]
ssl.EVP_EncryptFinal_ex(ctx, buf, ctypes.byref(final))
output += buf.raw[:final.value]
return output
def Decrypt(self, data):
buf = ctypes.create_string_buffer(len(data) + 16)
written = ctypes.c_int(0)
final = ctypes.c_int(0)
ctx = ssl.EVP_CIPHER_CTX_new()
ssl.EVP_CIPHER_CTX_init(ctx)
ssl.EVP_DecryptInit_ex(ctx, ssl.EVP_aes_256_cbc(), None, self.chKey, self.chIV)
ssl.EVP_DecryptUpdate(ctx, buf, ctypes.byref(written), data, len(data))
output = buf.raw[:written.value]
ssl.EVP_DecryptFinal_ex(ctx, buf, ctypes.byref(final))
output += buf.raw[:final.value]
return output
try:
if not crypter:
from aes import *
crypter = 'pure'
except:
pass
class Crypter_pure(object):
def __init__(self):
self.m = AESModeOfOperation()
self.cbc = self.m.modeOfOperation["CBC"]
self.sz = self.m.aes.keySize["SIZE_256"]
def SetKeyFromPassphrase(self, vKeyData, vSalt, nDerivIterations, nDerivationMethod):
data = vKeyData + vSalt
for i in range(nDerivIterations):
data = hashlib.sha512(data).digest()
self.SetKey(data[0:32])
self.SetIV(data[32:32+16])
return len(data)
def SetKey(self, key):
self.chKey = [ord(i) for i in key]
def SetIV(self, iv):
self.chIV = [ord(i) for i in iv]
def Encrypt(self, data):
mode, size, cypher = self.m.encrypt(data, self.cbc, self.chKey, self.sz, self.chIV)
return ''.join(map(chr, cypher))
def Decrypt(self, data):
chData = [ord(i) for i in data]
return self.m.decrypt(chData, self.sz, self.cbc, self.chKey, self.sz, self.chIV)
import hashlib
def Hash(data):
return hashlib.sha256(hashlib.sha256(data).digest()).digest()
def main():
#address
addr = '1AJ3vE2NNYW2Jzv3fLwyjKF1LYbZ65Ez64'
sec = '5JMhGPWc3pkdgPd9jqVZkRtEp3QB3Ze8ihv62TmmvzABmkNzBHw'
secret = '47510706d76bc74a5d57bdcffc68c9bbbc2d496bef87c91de7f616129ac62b5f'.decode('hex')
pubkey = '046211d9b7836892c8eef49c4d0cad7797815eff95108e1d30745c03577596c9c00d2cb1ab27c7f95c28771278f89b7ff40da49fe9b4ee834a3f6a88324db837d8'.decode('hex')
ckey = '0f8c75e4c6ab3c642dd06786af80ca3a93e391637d029f1da919dad77d3c8e477efd479814ddf4c459aeba042420868f'.decode('hex')
#master key
crypted_key = '1e1d7ab34d8007f214eb528a1007c6721b9cd1d2c257adb25378ea8e47e3bdd22cfe93a8b6f18dcbe4206fe8c8178ff1'.decode('hex')
salt = '3f94e3c670b695dd'.decode('hex')
rounds = 47135
method = 0
password = '12345'
global crypter
if crypter == 'pycrypto':
crypter = Crypter_pycrypto()
elif crypter == 'ssl':
crypter = Crypter_ssl()
print "using ssl"
elif crypter == 'pure':
crypter = Crypter_pure()
print "using slowaes"
else:
print("Need pycrypto of libssl or libeay32.dll or http://code.google.com/p/slowaes")
exit(1)
crypter.SetKeyFromPassphrase(password, salt, rounds, method)
masterkey = crypter.Decrypt(crypted_key)
crypter.SetKey(masterkey)
crypter.SetIV(Hash(pubkey))
d = crypter.Decrypt(ckey)
e = crypter.Encrypt(d)
print "masterkey:", masterkey.encode('hex')
print 'c:', ckey.encode('hex')
print 'd:', d.encode('hex')
print 'e:', e.encode('hex')
if __name__ == '__main__':
main()