import os import sys import time import random from secp256k1 import ffi parentPath = os.path.abspath("../..") if parentPath not in sys.path: sys.path.insert(0, parentPath) from pybtc.constants import * from .hash import * from .encode import * from .hash import * def create_private_key(compressed=True, testnet=False, wif=True, hex=False): """ Create private key :param compressed: (optional) Type of public key, by default set to compressed. Using uncompressed public keys is deprecated in new SEGWIT addresses, use this option only for backward compatibility. :param testnet: (optional) flag for testnet network, by default is False. :param wif: (optional) If set to True return key in WIF format, by default is True. :param hex: (optional) If set to True return key in HEX format, by default is False. :return: Private key in wif format (default), hex encoded byte string in case of hex flag or raw bytes string in case wif and hex flags set to False. """ a = random.SystemRandom().randint(0, MAX_INT_PRIVATE_KEY) i = int((time.time() % 0.01 ) * 100000) h = a.to_bytes(32, byteorder="big") # more entropy from system timer and sha256 derivation while i: h = hashlib.sha256(h).digest() i -= 1 if not i and int.from_bytes(h, byteorder="big") > MAX_INT_PRIVATE_KEY: i += 1 if wif: return private_key_to_wif(h, compressed=compressed, testnet=testnet) elif hex: return h.hex() return h def private_key_to_wif(h, compressed=True, testnet=False): """ Encode private key in HEX or RAW bytes format to WIF format. :param h: private key 32 byte string or HEX encoded string. :param compressed: (optional) flag of public key compressed format, by default set to True. :param testnet: (optional) flag for testnet network, by default is False. :return: Private key in WIF format. """ # uncompressed: 0x80 + [32-byte secret] + [4 bytes of Hash() of previous 33 bytes], base58 encoded. # compressed: 0x80 + [32-byte secret] + 0x01 + [4 bytes of Hash() previous 34 bytes], base58 encoded. if isinstance(h, str): h = bytes.fromhex(h) if len(h) != 32 and isinstance(h, bytes): raise TypeError("private key must be a 32 bytes or hex encoded string") if testnet: h = TESTNET_PRIVATE_KEY_BYTE_PREFIX + h else: h = MAINNET_PRIVATE_KEY_BYTE_PREFIX + h if compressed: h += b'\x01' h += double_sha256(h)[:4] return encode_base58(h) def wif_to_private_key(h, hex=True): """ Decode WIF private key to bytes string or HEX encoded string :param hex: (optional) if set to True return key in HEX format, by default is True. :return: Private key HEX encoded string or raw bytes string. """ if not is_wif_valid(h): raise TypeError("invalid wif key") h = decode_base58(h) if hex: return h[1:33].hex() return h[1:33] def is_wif_valid(wif): """ Check is private key in WIF format string is valid. :param wif: private key in WIF format string. :return: boolean. """ if not isinstance(wif, str): raise TypeError("invalid wif key") if wif[0] not in PRIVATE_KEY_PREFIX_LIST: return False try: h = decode_base58(wif) except: return False checksum = h[-4:] if wif[0] in (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX, TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX): if len(h) != 37: return False elif len(h) != 38: return False if double_sha256(h[:-4])[:4] != checksum: return False return True def private_to_public_key(private_key, compressed=True, hex=True): """ Get public key from private key using ECDSA secp256k1 :param private_key: private key in WIF, HEX or bytes. :param compressed: (optional) flag of public key compressed format, by default set to True. In case private_key in WIF format, this flag is set in accordance with the key format specified in WIF string. :param hex: (optional) if set to True return key in HEX format, by default is True. :return: 33/65 bytes public key in HEX or bytes string. """ if not isinstance(private_key, bytes): if isinstance(private_key, bytearray): private_key = bytes(private_key) elif isinstance(private_key, str): if not is_wif_valid(private_key): private_key = bytes.fromhex(private_key) else: if private_key[0] in (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX, TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX): compressed = False private_key = wif_to_private_key(private_key, hex=0) else: raise TypeError("private key must be a bytes or WIF or hex encoded string") pubkey_ptr = ffi.new('secp256k1_pubkey *') r = secp256k1.secp256k1_ec_pubkey_create(ECDSA_CONTEXT_ALL, pubkey_ptr, private_key) if not r: raise RuntimeError("secp256k1 error") len_key = 33 if compressed else 65 pubkey = ffi.new('char [%d]' % len_key) outlen = ffi.new('size_t *', len_key) compflag = EC_COMPRESSED if compressed else EC_UNCOMPRESSED r = secp256k1.secp256k1_ec_pubkey_serialize(ECDSA_CONTEXT_VERIFY, pubkey, outlen, pubkey_ptr, compflag) pub = bytes(ffi.buffer(pubkey, len_key)) if not r: raise RuntimeError("secp256k1 error") return pub.hex() if hex else pub def is_public_key_valid(key): """ Check public key is valid. :param key: public key in HEX or bytes string format. :return: boolean. """ if isinstance(key, str): key = bytes.fromhex(key) if len(key) < 33: return False elif key[0] == 0x04 and len(key) != 65: return False elif key[0] == 0x02 or key[0] == 0x03: if len(key) != 33: return False return True