mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-05-11 15:02:15 +08:00
99 lines
4.2 KiB
Python
99 lines
4.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
Blockstack-client
|
|
~~~~~
|
|
copyright: (c) 2014-2015 by Halfmoon Labs, Inc.
|
|
copyright: (c) 2016 by Blockstack.org
|
|
|
|
This file is part of Blockstack-client.
|
|
|
|
Blockstack-client is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Blockstack-client is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
You should have received a copy of the GNU General Public License
|
|
along with Blockstack-client. If not, see <http://www.gnu.org/licenses/>.
|
|
"""
|
|
|
|
import re
|
|
import os
|
|
import random
|
|
import struct
|
|
import hashlib
|
|
import binascii
|
|
import scrypt
|
|
from hashlib import sha256
|
|
from pybitcoin import BitcoinKeypair, b58check_encode, b58check_decode
|
|
from Crypto.Cipher import AES
|
|
|
|
|
|
def bip38_encrypt(private_key, passphrase, n=16384, r=8, p=8, compressed=False):
|
|
# determine the flagbyte
|
|
if compressed:
|
|
flagbyte = '\xe0'
|
|
else:
|
|
flagbyte = '\xc0'
|
|
# get the private key's address and equivalent in hex format and wif format
|
|
k = BitcoinKeypair(private_key)
|
|
address = k.address()
|
|
hex_private_key = k.private_key()
|
|
wif_private_key = k.wif_pk()
|
|
# calculate the address's checksum
|
|
address_checksum = sha256(sha256(address).digest()).digest()[0:4]
|
|
# calculate the scrypt hash and split it in half
|
|
scrypt_derived_key = scrypt.hash(passphrase, address_checksum, n, r, p)
|
|
derived_half_1 = scrypt_derived_key[0:32]
|
|
derived_half_2 = scrypt_derived_key[32:64]
|
|
# combine parts of the private key and scrypt hash and AES encrypt them
|
|
aes = AES.new(derived_half_2)
|
|
encrypted_half_1 = aes.encrypt(
|
|
binascii.unhexlify(
|
|
'%0.32x' % (long(hex_private_key[0:32], 16) ^ long(binascii.hexlify(derived_half_1[0:16]), 16))
|
|
)
|
|
)
|
|
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
|
|
encrypted_private_key = ('\x42' + flagbyte + address_checksum + encrypted_half_1 + encrypted_half_2)
|
|
# base 58 encode the encrypted private key
|
|
b58check_encrypted_private_key = b58check_encode(encrypted_private_key, version_byte=1)
|
|
# return the encrypted private key
|
|
return b58check_encrypted_private_key
|
|
|
|
|
|
def bip38_decrypt(b58check_encrypted_private_key, passphrase, n=16384, r=8, p=8):
|
|
# decode private key from base 58 check to binary
|
|
encrypted_private_key = b58check_decode(b58check_encrypted_private_key)
|
|
# parse the encrypted key different byte sections
|
|
bip38_key_identification_byte = encrypted_private_key[0:1]
|
|
flagbyte = encrypted_private_key[1:2]
|
|
address_checksum = encrypted_private_key[2:6]
|
|
encrypted_half_1 = encrypted_private_key[6:6+16]
|
|
encrypted_half_2 = encrypted_private_key[6+16:6+32]
|
|
# derive a unique key from the passphrase and the address checksum
|
|
scrypt_derived_key = scrypt.hash(passphrase, address_checksum, n, r, p)
|
|
derived_half_1 = scrypt_derived_key[0:32]
|
|
derived_half_2 = scrypt_derived_key[32:64]
|
|
# decrypt the encrypted halves
|
|
aes = AES.new(derived_half_2)
|
|
decrypted_half_1 = aes.decrypt(encrypted_half_1)
|
|
decrypted_half_2 = aes.decrypt(encrypted_half_2)
|
|
# 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))
|
|
# get the address corresponding to the private key
|
|
k = BitcoinKeypair(decrypted_private_key)
|
|
address = k.address()
|
|
# make sure the address matches the checksum in the original encrypted key
|
|
if address_checksum != sha256(sha256(address).digest()).digest()[0:4]:
|
|
raise ValueError('Invalid private key and password combo.')
|
|
# return the decrypted private key
|
|
return k.private_key()
|