upgraded bip38 (from halfmoonlabs/badge)

This commit is contained in:
Muneeb Ali
2014-07-30 02:10:44 -07:00
parent 2075792184
commit f2c2886709
2 changed files with 28 additions and 21 deletions

View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
"""
BIP 0038
~~~~~
:copyright: (c) 2014 by Halfmoon Labs
:license: MIT, see LICENSE for more details.
"""
from .bip38 import bip38_decrypt, bip38_encrypt

View File

@@ -1,10 +1,4 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
#-----------------------
# Copyright 2014 Halfmoon Labs, Inc.
# All Rights Reserved
#-----------------------
""" """
BIP 0038 BIP 0038
~~~~~ ~~~~~
@@ -14,12 +8,18 @@
""" """
import re, os, random, struct, hashlib, binascii, scrypt import re, os, random, struct, hashlib, binascii, scrypt
from binascii import hexlify, unhexlify
from hashlib import sha256 from hashlib import sha256
from coinkit import BitcoinKeypair, b58check_encode, b58check_decode from coinkit import BitcoinKeypair, b58check_encode, b58check_decode
from Crypto.Cipher import AES from Crypto.Cipher import AES
#----------------------------
def get_new_aes(key):
return AES.new(key, AES.MODE_ECB)
#----------------------------
def bip38_encrypt(private_key, passphrase, n=16384, r=8, p=8, compressed=False): def bip38_encrypt(private_key, passphrase, n=16384, r=8, p=8, compressed=False):
# determine the flagbyte # determine the flagbyte
if compressed: if compressed:
flagbyte = '\xe0' flagbyte = '\xe0'
else: else:
@@ -36,17 +36,11 @@ def bip38_encrypt(private_key, passphrase, n=16384, r=8, p=8, compressed=False):
derived_half_1 = scrypt_derived_key[0:32] derived_half_1 = scrypt_derived_key[0:32]
derived_half_2 = scrypt_derived_key[32:64] derived_half_2 = scrypt_derived_key[32:64]
# combine parts of the private key and scrypt hash and AES encrypt them # combine parts of the private key and scrypt hash and AES encrypt them
aes = AES.new(derived_half_2) aes = get_new_aes(derived_half_2)
encrypted_half_1 = aes.encrypt( xor_result_1 = long(hex_private_key[0:32], 16) ^ long(hexlify(derived_half_1[0:16]), 16)
binascii.unhexlify( xor_result_2 = long(hex_private_key[32:64], 16) ^ long(hexlify(derived_half_1[16:32]), 16)
'%0.32x' % (long(hex_private_key[0:32], 16) ^ long(binascii.hexlify(derived_half_1[0:16]), 16)) encrypted_half_1 = aes.encrypt(unhexlify('%0.32x' % (xor_result_1)))
) encrypted_half_2 = aes.encrypt(unhexlify('%0.32x' % (xor_result_2)))
)
encrypted_half_2 = aes.encrypt(
binascii.unhexlify(
'%0.32x' % (long(hex_private_key[32:64], 16) ^ long(binascii.hexlify(derived_half_1[16:32]), 16))
)
)
# build the encrypted private key from the checksum and encrypted parts # build the encrypted private key from the checksum and encrypted parts
encrypted_private_key = ('\x42' + flagbyte + address_checksum + encrypted_half_1 + encrypted_half_2) encrypted_private_key = ('\x42' + flagbyte + address_checksum + encrypted_half_1 + encrypted_half_2)
# base 58 encode the encrypted private key # base 58 encode the encrypted private key
@@ -54,6 +48,7 @@ def bip38_encrypt(private_key, passphrase, n=16384, r=8, p=8, compressed=False):
# return the encrypted private key # return the encrypted private key
return b58check_encrypted_private_key return b58check_encrypted_private_key
#----------------------------
def bip38_decrypt(b58check_encrypted_private_key, passphrase, n=16384, r=8, p=8): def bip38_decrypt(b58check_encrypted_private_key, passphrase, n=16384, r=8, p=8):
# decode private key from base 58 check to binary # decode private key from base 58 check to binary
encrypted_private_key = b58check_decode(b58check_encrypted_private_key) encrypted_private_key = b58check_decode(b58check_encrypted_private_key)
@@ -68,16 +63,18 @@ def bip38_decrypt(b58check_encrypted_private_key, passphrase, n=16384, r=8, p=8)
derived_half_1 = scrypt_derived_key[0:32] derived_half_1 = scrypt_derived_key[0:32]
derived_half_2 = scrypt_derived_key[32:64] derived_half_2 = scrypt_derived_key[32:64]
# decrypt the encrypted halves # decrypt the encrypted halves
aes = AES.new(derived_half_2) aes = get_new_aes(derived_half_2)
decrypted_half_1 = aes.decrypt(encrypted_half_1) decrypted_half_1 = aes.decrypt(encrypted_half_1)
decrypted_half_2 = aes.decrypt(encrypted_half_2) decrypted_half_2 = aes.decrypt(encrypted_half_2)
# get the original private key from the encrypted halves + the derived half # get the original private key from the encrypted halves + the derived half
decrypted_private_key = '%064x' % (long(binascii.hexlify(decrypted_half_1 + decrypted_half_2), 16) ^ long(binascii.hexlify(derived_half_1), 16)) xor_result = long(hexlify(decrypted_half_1 + decrypted_half_2), 16) ^ long(hexlify(derived_half_1), 16)
decrypted_private_key = '%064x' % (xor_result)
# get the address corresponding to the private key # get the address corresponding to the private key
k = BitcoinKeypair(decrypted_private_key) k = BitcoinKeypair(decrypted_private_key)
address = k.address() address = k.address()
# make sure the address matches the checksum in the original encrypted key # make sure the address matches the checksum in the original encrypted key
if address_checksum != sha256(sha256(address).digest()).digest()[0:4]: if address_checksum != sha256(sha256(address).digest()).digest()[0:4]:
raise ValueError('Invalid private key and password combo.') raise ValueError('Invalid private key and password combo.')
# return the decrypted private key # return the decrypted private key
return k.private_key() return k.wif_pk()