def dict(self): """ Returns key information as dictionary """ point_x, point_y = self.key.public_point() return { 'private_hex': '' if not self.isprivate else self.private_hex, 'private_long': '' if not self.isprivate else self.secret, 'private_wif': '' if not self.isprivate else self.key.wif(), 'public_hex': self.public_hex, 'public_hash160': self.key.hash160(), 'address': self.key.address(), 'fingerprint': change_base(self.fingerprint(), 256, 16), 'point_x': point_x, 'point_y': point_y, 'key_type': self.key_type, 'chain_code': change_base(self.chain, 256, 16), 'child_index': self.child_index, 'fingerprint_parent': change_base(self.parent_fingerprint, 256, 16), 'depth': self.depth, 'extended_wif_public': self.wif_public(), 'extended_wif_private': self.wif(public=False), }
def info(self): """ Prints key information to standard output """ if self.isprivate: print("SECRET EXPONENT") print(" Private Key (hex) %s" % self.private_hex) print(" Private Key (long) %s" % self.secret) print(" Private Key (wif) %s" % self.key.wif()) print("") print("PUBLIC KEY") print(" Public Key (hex) %s" % self.public_hex) print(" Public Key Hash160 %s" % self.key.hash160()) print(" Address (b58) %s" % self.key.address()) print(" Fingerprint (hex) %s" % change_base(self.fingerprint(), 256, 16)) point_x, point_y = self.key.public_point() print(" Point x %s" % point_x) print(" Point y %s" % point_y) print("") print("EXTENDED KEY INFO") print(" Key Type %s" % self.key_type) print(" Chain code (hex) %s" % change_base(self.chain, 256, 16)) print(" Child Index %s" % self.child_index) print(" Parent Fingerprint (hex) %s" % change_base(self.parent_fingerprint, 256, 16)) print(" Depth %s" % self.depth) print(" Extended Public Key (wif) %s" % self.wif_public()) print(" Extended Private Key (wif) %s" % self.wif(public=False)) print("\n")
def child_public(self, index=0): """ Use Child Key Derivation to derive child public key of current HD Key object. :param index: Key index number :return: HD Key class object """ if index > 0x80000000: raise BKeyError("Cannot derive hardened key from public private key. Index must be less then 0x80000000") data = self.public().public_byte() + struct.pack('>L', index) key, chain = self._key_derivation(data) key = change_base(key, 256, 10) if key > secp256k1_n: raise BKeyError("Key cannot be greater then secp256k1_n. Try another index number.") x, y = self.public().public_point() Ki = ec_point(key) + ecdsa.ellipticcurve.Point(curve, x, y, secp256k1_n) if change_base(Ki.y(), 16, 10) % 2: prefix = '03' else: prefix = '02' xhex = change_base(Ki.x(), 10, 16, 64) secret = change_base(prefix + xhex, 16, 256) return HDKey(key=secret, chain=chain, depth=self._depth+1, parent_fingerprint=self.fingerprint(), child_index=index, isprivate=False)
def to_mnemonic(self, data, add_checksum=True, check_on_curve=True): """ Convert key data entropy to Mnemonic sentence >>> Mnemonic().to_mnemonic('28acfc94465fd2f6774759d6897ec122') 'chunk gun celery million wood kite tackle twenty story episode raccoon dutch' :param data: Key data entropy :type data: bytes, hexstring :param add_checksum: Included a checksum? Default is True :type add_checksum: bool :param check_on_curve: Check if data integer value is on secp256k1 curve. Should be enabled when not testing and working with crypto :type check_on_curve: bool :return str: Mnemonic passphrase consisting of a space seperated list of words """ data = to_bytes(data) data_int = change_base(data, 256, 10) if check_on_curve and not 0 < data_int < secp256k1_n: raise ValueError( "Integer value of data should be in secp256k1 domain between 1 and secp256k1_n-1" ) if add_checksum: binresult = change_base(data_int, 10, 2, len(data) * 8) + self.checksum(data) wi = change_base(binresult, 2, 2048) else: wi = change_base(data_int, 10, 2048, len(data) // 1.375 + len(data) % 1.375 > 0) return normalize_string(' '.join([self._wordlist[i] for i in wi]))
def child_public(self, index=0, network=None): """ Use Child Key Derivation to derive child public key of current HD Key object. :param index: Key index number :type index: int :return HDKey: HD Key class object """ if network is None: network = self.network.network_name if index > 0x80000000: raise BKeyError("Cannot derive hardened key from public private key. Index must be less then 0x80000000") data = self.public_byte + struct.pack('>L', index) key, chain = self._key_derivation(data) key = change_base(key, 256, 10) if key > secp256k1_n: raise BKeyError("Key cannot be greater then secp256k1_n. Try another index number.") x, y = self.key.public_point() Ki = ec_point(key) + ecdsa.ellipticcurve.Point(curve, x, y, secp256k1_n) # if change_base(Ki.y(), 16, 10) % 2: if Ki.y() % 2: prefix = '03' else: prefix = '02' xhex = change_base(Ki.x(), 10, 16, 64) secret = binascii.unhexlify(prefix + xhex) return HDKey(key=secret, chain=chain, depth=self.depth+1, parent_fingerprint=self.fingerprint(), child_index=index, isprivate=False, network=network)
def child_private(self, index=0, hardened=False): """ Use Child Key Derivation (CDK) to derive child private key of current HD Key object. :param index: Key index number :param hardened: Specify if key must be hardened (True) or normal (False) :return: HD Key class object """ if not self._isprivate: raise BKeyError("Need a private key to create child private key") if hardened: index |= 0x80000000 data = b'\0' + self._key + struct.pack('>L', index) else: data = self.public().public_byte() + struct.pack('>L', index) key, chain = self._key_derivation(data) key = change_base(key, 256, 10) if key > secp256k1_n: raise BKeyError("Key cannot be greater then secp256k1_n. Try another index number.") newkey = (key + self._secret) % secp256k1_n if newkey == 0: raise BKeyError("Key cannot be zero. Try another index number.") newkey = change_base(newkey, 10, 256, 32) return HDKey(key=newkey, chain=chain, depth=self._depth+1, parent_fingerprint=self.fingerprint(), child_index=index)
def wif(self, prefix=None): """ Get Private Key in Wallet Import Format, steps: # Convert to Binary and add 0x80 hex # Calculate Double SHA256 and add as checksum to end of key :param prefix: Specify versionbyte prefix in hexstring or bytes. Normally doesn't need to be specified, method uses default prefix from network settings :type prefix: str, bytes :return str: Base58Check encoded Private Key WIF """ if not self.secret: raise KeyError("WIF format not supported for public key") if prefix is None: versionbyte = self.network.prefix_wif else: # TODO: Test bytearray if not isinstance(prefix, (bytes, bytearray)): versionbyte = binascii.unhexlify(prefix) else: versionbyte = prefix key = versionbyte + change_base(self.secret, 10, 256, 32) if self.compressed: key += b'\1' key += hashlib.sha256(hashlib.sha256(key).digest()).digest()[:4] return change_base(key, 256, 58)
def child_private(self, index=0, hardened=False, network=None): """ Use Child Key Derivation (CDK) to derive child private key of current HD Key object. :param index: Key index number :type index: int :param hardened: Specify if key must be hardened (True) or normal (False) :type hardened: bool :return HDKey: HD Key class object """ if network is None: network = self.network.network_name if not self.isprivate: raise BKeyError("Need a private key to create child private key") if hardened: index |= 0x80000000 data = b'\0' + self.private_byte + struct.pack('>L', index) else: data = self.public_byte + struct.pack('>L', index) key, chain = self._key_derivation(data) key = change_base(key, 256, 10) if key > secp256k1_n: raise BKeyError("Key cannot be greater then secp256k1_n. Try another index number.") newkey = (key + self.secret) % secp256k1_n if newkey == 0: raise BKeyError("Key cannot be zero. Try another index number.") newkey = change_base(newkey, 10, 256, 32) return HDKey(key=newkey, chain=chain, depth=self.depth+1, parent_fingerprint=self.fingerprint(), child_index=index, network=network)
def to_mnemonic(self, hexdata, add_checksum=True): if add_checksum: binresult = change_base(hexdata, 16, 2, len(hexdata) * 4) + self.checksum(hexdata) wi = change_base(binresult, 2, 2048) else: wi = change_base(hexdata, 16, 2048) return self.normalize_string(' '.join([self._wordlist[i] for i in wi]))
def to_mnemonic(self, data, add_checksum=True, check_on_curve=True): """ Convert key data entropy to Mnemonic sentence :param data: Key data entropy :type data: bytes, hexstring :param add_checksum: Included a checksum? Default is True :type add_checksum: bool :param check_on_curve: Check if data integer value is on secp256k1 curve. Should be enabled when not testing and working with crypto :type check_on_curve: bool :return str: Mnemonic passphrase consisting of a space seperated list of words """ data = to_bytes(data) data_int = change_base(data, 256, 10) if check_on_curve and not 0 < data_int < secp256k1_n: raise ValueError( "Integer value of data should be in secp256k1 domain between 1 and secp256k1_n-1" ) if add_checksum: binresult = change_base(data_int, 10, 2, len(data) * 8) + self.checksum(data) wi = change_base(binresult, 2, 2048) else: wi = change_base(data_int, 10, 2048) return normalize_string(' '.join([self._wordlist[i] for i in wi]))
def _create_public(self): """ Create public key point and hex repressentation from private key. :return: """ if self._secret: point = ec_point(self._secret) self._x = change_base(int(point.x()), 10, 16, 64) self._y = change_base(int(point.y()), 10, 16, 64) if point.y() % 2: prefix = '03' else: prefix = '02' self._public = prefix + self._x self._public_uncompressed = '04' + self._x + self._y if hasattr(self, '_x') and hasattr(self, '_y') and self._x and self._y: if change_base(self._y, 16, 10) % 2: prefix = '03' else: prefix = '02' self._public = prefix + self._x self._public_uncompressed = '04' + self._x + self._y else: raise BKeyError("Key error, no secret key or public key point found.")
def to_entropy(self, words, includes_checksum=True): """ Convert Mnemonic words back to entrophy :param words: Mnemonic words as string of list of words :param includes_checksum: Boolean to specify if checksum is used :return: Hex entrophy string """ words = self.sanitize_mnemonic(words) if isinstance(words, (str, unicode if sys.version < '3' else str)): words = words.split(' ') wi = [] for word in words: wi.append(self._wordlist.index(word)) ent = change_base(wi, 2048, 16, output_even=0) if includes_checksum: binresult = change_base(ent, 16, 2, len(ent) * 4) ent = change_base(binresult[:-len(binresult) // 33], 2, 16) # Check checksum checksum = binresult[-len(binresult) // 33:] if checksum != self.checksum(ent): raise Warning("Invalid checksum %s for entropy %s" % (checksum, ent)) return ent
def to_entropy(self, words, includes_checksum=True): """ Convert Mnemonic words back to key data entropy >>> from bitcoinlib.encoding import to_hexstring >>> to_hexstring(Mnemonic().to_entropy('chunk gun celery million wood kite tackle twenty story episode raccoon dutch')) '28acfc94465fd2f6774759d6897ec122' :param words: Mnemonic words as string of list of words :type words: str :param includes_checksum: Boolean to specify if checksum is used. Default is True :type includes_checksum: bool :return bytes: Entropy seed """ words = self.sanitize_mnemonic(words) if isinstance(words, TYPE_TEXT): words = words.split(' ') wi = [] for word in words: wi.append(self._wordlist.index(word)) ent = change_base(wi, 2048, 256, output_even=False) if includes_checksum: binresult = change_base(ent, 256, 2, len(ent) * 4) ent = change_base(binresult[:-len(binresult) // 33], 2, 256) # Check checksum checksum = binresult[-len(binresult) // 33:] if checksum != self.checksum(ent): raise ValueError("Invalid checksum %s for entropy %s" % (checksum, ent)) return ent
def to_entropy(self, words, includes_checksum=True): """ Convert Mnemonic words back to key data entrophy :param words: Mnemonic words as string of list of words :type words: str :param includes_checksum: Boolean to specify if checksum is used. Default is True :type includes_checksum: bool :return bytes: Entrophy seed """ words = self.sanitize_mnemonic(words) if isinstance(words, (str, unicode if sys.version < '3' else str)): words = words.split(' ') wi = [] for word in words: wi.append(self._wordlist.index(word)) ent = change_base(wi, 2048, 256, output_even=False) if includes_checksum: binresult = change_base(ent, 256, 2, len(ent) * 4) ent = change_base(binresult[:-len(binresult) // 33], 2, 256) # Check checksum checksum = binresult[-len(binresult) // 33:] if checksum != self.checksum(ent): raise Warning("Invalid checksum %s for entropy %s" % (checksum, ent)) return ent
def address(self, compressed=None): if not self._public or not self._public_uncompressed: self._create_public() if (self._compressed and compressed is None) or compressed: key = change_base(self._public, 16, 256) else: key = change_base(self._public_uncompressed, 16, 256) versionbyte = NETWORKS[self._network]['address'] key = versionbyte + hashlib.new('ripemd160', hashlib.sha256(key).digest()).digest() checksum = hashlib.sha256(hashlib.sha256(key).digest()).digest()[:4] return change_base(key + checksum, 256, 58)
def checksum(hexdata): """ Calculates checksum for given hexdata key :param hexdata: key string as hexadecimal :return: Checksum of key as hex """ if len(hexdata) % 8 > 0: raise ValueError('Data length in bits should be divisible by 32, but it is not (%d bytes = %d bits).' % (len(hexdata), len(hexdata) * 8)) # data = self.normalize_string(change_base(hexdata, 16, 256)) data = change_base(hexdata, 16, 256) hashhex = hashlib.sha256(data).hexdigest() return change_base(hashhex, 16, 2, 256)[:len(data) * 8 // 32]
def wif(self, public=None, child_index=None, prefix=None): """ Get Extended WIF of current key :param public: Return public key? :type public: bool :param child_index: Change child index of output WIF key :type child_index: int :param prefix: Specify version prefix in hexstring or bytes. Normally doesn't need to be specified, method uses default prefix from network settings :type prefix: str, bytes :return str: Base58 encoded WIF key """ rkey = self.private_byte or self.public_byte if prefix and not isinstance(prefix, (bytes, bytearray)): prefix = binascii.unhexlify(prefix) if not self.isprivate and public is False: return '' if self.isprivate and not public: if not prefix: prefix = self.network.prefix_hdkey_private typebyte = b'\x00' else: if not prefix: prefix = self.network.prefix_hdkey_public typebyte = b'' if public: rkey = self.public_byte if child_index: self.child_index = child_index raw = prefix + struct.pack('B', self.depth) + self.parent_fingerprint + \ struct.pack('>L', self.child_index) + self.chain + typebyte + rkey chk = hashlib.sha256(hashlib.sha256(raw).digest()).digest()[:4] ret = raw+chk return change_base(ret, 256, 58, 111)
def hash160(self): if not self._public: self._create_public() key = change_base(self._public, 16, 256) # if sys.version_info > (3,): # key = key.encode('utf-8') return hashlib.new('ripemd160', hashlib.sha256(key).digest()).hexdigest()
def prefix_search(wif, network=None): """ Extract network, script type and public/private information from WIF prefix. :param wif: WIF string or prefix in bytes or hexadecimal string :type wif: str, bytes :param network: Limit search to specified network :type network: str :return dict: """ key_hex = change_base(wif, 58, 16) if not key_hex: key_hex = to_hexstring(wif) prefix = key_hex[:8].upper() matches = [] for nw in NETWORK_DEFINITIONS: if network is not None and nw != network: continue data = NETWORK_DEFINITIONS[nw] for pf in data['prefixes_wif']: if pf[1] == prefix: matches.append({ 'prefix': prefix, 'is_private': True if pf[0] == 'private' else False, 'prefix_str': pf[2], 'network': nw, 'script_types': pf[3] }) return matches
def wif(self): """ Get Private Key in Wallet Import Format, steps: (1) Convert to Binary and add 0x80 hex (2) Calculate Double SHA256 and add as checksum to end of key :return: Base58Check encoded Private Key WIF """ if not self._secret: return False version = NETWORKS[self._network]['wif'] key = version + change_base(str(self._secret), 10, 256, 32) if self._compressed: key += b'\1' key += hashlib.sha256(hashlib.sha256(key).digest()).digest()[:4] return change_base(key, 256, 58)
def wif_prefix_search(wif, witness_type=None, multisig=None, network=None): """ Extract network, script type and public/private information from HDKey WIF or WIF prefix. Example, get bitcoin 'xprv' info: >>> wif_prefix_search('0488ADE4', network='bitcoin', multisig=False) [{'prefix': '0488ADE4', 'is_private': True, 'prefix_str': 'xprv', 'network': 'bitcoin', 'witness_type': 'legacy', 'multisig': False, 'script_type': 'p2pkh'}] Or retreive info with full WIF string: >>> wif_prefix_search('xprv9wTYmMFdV23N21MM6dLNavSQV7Sj7meSPXx6AV5eTdqqGLjycVjb115Ec5LgRAXscPZgy5G4jQ9csyyZLN3PZLxoM1h3BoPuEJzsgeypdKj', network='bitcoin', multisig=False) [{'prefix': '0488ADE4', 'is_private': True, 'prefix_str': 'xprv', 'network': 'bitcoin', 'witness_type': 'legacy', 'multisig': False, 'script_type': 'p2pkh'}] Can return multiple items if no network is specified: >>> [nw['network'] for nw in wif_prefix_search('0488ADE4', multisig=True)] ['bitcoin', 'dash'] :param wif: WIF string or prefix in bytes or hexadecimal string :type wif: str, bytes :param witness_type: Limit search to specific witness type :type witness_type: str :param multisig: Limit search to multisig: false, true or None for both. Default is both :type multisig: bool :param network: Limit search to specified network :type network: str :return dict: """ key_hex = '' if len(wif) > 8: try: key_hex = change_base(wif, 58, 16) except: pass else: key_hex = wif if not key_hex: key_hex = to_hexstring(wif) prefix = key_hex[:8].upper() matches = [] for nw in NETWORK_DEFINITIONS: if network is not None and nw != network: continue data = NETWORK_DEFINITIONS[nw] for pf in data['prefixes_wif']: if pf[0] == prefix and (multisig is None or pf[3] is None or pf[3] == multisig) and \ (witness_type is None or pf[4] is None or pf[4] == witness_type): matches.append({ 'prefix': prefix, 'is_private': True if pf[2] == 'private' else False, 'prefix_str': pf[1], 'network': nw, 'witness_type': pf[4], 'multisig': pf[3], 'script_type': pf[5] }) return matches
def bip38_encrypt(self, passphrase): """ BIP0038 non-ec-multiply encryption. Returns BIP0038 encrypted privkey. Based on code from https://github.com/nomorecoin/python-bip38-testing :param passphrase: Required passphrase for encryption :type passphrase: str :return str: BIP38 passphrase encrypted private key """ if self.compressed: flagbyte = b'\xe0' addr = self.address() else: flagbyte = b'\xc0' addr = self.address_uncompressed() privkey = self.private_hex if isinstance(addr, str) and sys.version_info > (3,): addr = addr.encode('utf-8') addresshash = hashlib.sha256(hashlib.sha256(addr).digest()).digest()[0:4] key = scrypt.hash(passphrase, addresshash, 16384, 8, 8) derivedhalf1 = key[0:32] derivedhalf2 = key[32:64] aes = AES.new(derivedhalf2) encryptedhalf1 = aes.encrypt(binascii.unhexlify('%0.32x' % (int(privkey[0:32], 16) ^ int(binascii.hexlify(derivedhalf1[0:16]), 16)))) encryptedhalf2 = aes.encrypt(binascii.unhexlify('%0.32x' % (int(privkey[32:64], 16) ^ int(binascii.hexlify(derivedhalf1[16:32]), 16)))) encrypted_privkey = b'\x01\x42' + flagbyte + addresshash + encryptedhalf1 + encryptedhalf2 encrypted_privkey += hashlib.sha256(hashlib.sha256(encrypted_privkey).digest()).digest()[:4] return change_base(encrypted_privkey, 256, 58)
def wif(self, public=None, child_index=None): """ Get Extended WIF of current key :param public: Return public key? :type public: bool :param child_index: Change child index of output WIF key :type child_index: int :return str: Base58 encoded WIF key """ rkey = self.private_byte or self.public_byte if not self.isprivate and public is False: return '' if self.isprivate and not public: raw = self.network.prefix_hdkey_private typebyte = b'\x00' else: raw = self.network.prefix_hdkey_public typebyte = b'' if public: rkey = self.public_byte if child_index: self.child_index = child_index raw += struct.pack('B', self.depth) + self.parent_fingerprint + \ struct.pack('>L', self.child_index) + self.chain + typebyte + rkey chk = hashlib.sha256(hashlib.sha256(raw).digest()).digest()[:4] ret = raw + chk return change_base(ret, 256, 58, 111)
def wif(self): """ Get Private Key in Wallet Import Format, steps: # Convert to Binary and add 0x80 hex # Calculate Double SHA256 and add as checksum to end of key :return str: Base58Check encoded Private Key WIF """ if not self.secret: raise KeyError("WIF format not supported for public key") version = self.network.prefix_wif key = version + change_base(self.secret, 10, 256, 32) if self.compressed: key += b'\1' key += hashlib.sha256(hashlib.sha256(key).digest()).digest()[:4] return change_base(key, 256, 58)
def bip38_encrypt(self, passphrase): """ BIP0038 non-ec-multiply encryption. Returns BIP0038 encrypted privkey. Based on code from https://github.com/nomorecoin/python-bip38-testing :param passphrase: Required passphrase for encryption :return: BIP38 passphrase encrypted private key """ if self._compressed: flagbyte = b'\xe0' addr = self.address() else: flagbyte = b'\xc0' addr = self.address_uncompressed() privkey = self.private_hex() if isinstance(addr, str) and sys.version_info > (3,): addr = addr.encode('utf-8') addresshash = hashlib.sha256(hashlib.sha256(addr).digest()).digest()[0:4] key = scrypt.hash(passphrase, addresshash, 16384, 8, 8) derivedhalf1 = key[0:32] derivedhalf2 = key[32:64] aes = AES.new(derivedhalf2) encryptedhalf1 = aes.encrypt(binascii.unhexlify('%0.32x' % (int(privkey[0:32], 16) ^ int(binascii.hexlify(derivedhalf1[0:16]), 16)))) encryptedhalf2 = aes.encrypt(binascii.unhexlify('%0.32x' % (int(privkey[32:64], 16) ^ int(binascii.hexlify(derivedhalf1[16:32]), 16)))) encrypted_privkey = b'\x01\x42' + flagbyte + addresshash + encryptedhalf1 + encryptedhalf2 encrypted_privkey += hashlib.sha256(hashlib.sha256(encrypted_privkey).digest()).digest()[:4] return change_base(encrypted_privkey, 256, 58)
def address(self, compressed=None, prefix=None): """ Get address derived from public key :param compressed: Always return compressed address :type compressed: bool :param prefix: Specify versionbyte prefix in hexstring or bytes. Normally doesn't need to be specified, method uses default prefix from network settings :type prefix: str, bytes :return str: Base58 encoded address """ if (self.compressed and compressed is None) or compressed: key = self.public_byte else: key = self.public_uncompressed_byte if prefix is None: versionbyte = self.network.prefix_address else: # TODO: Test bytearray if not isinstance(prefix, (bytes, bytearray)): versionbyte = binascii.unhexlify(prefix) else: versionbyte = prefix key = versionbyte + hashlib.new('ripemd160', hashlib.sha256(key).digest()).digest() checksum = hashlib.sha256(hashlib.sha256(key).digest()).digest()[:4] return change_base(key + checksum, 256, 58)
def deserialize_address(address): """ Deserialize cryptocurrency address. Calculate public key hash and try to determine script type and network. If one and only one network is found the 'network' dictionary item with contain this network. Same applies for the script type. If more networks and or script types are found you can find these in 'networks_p2sh' and 'networks_p2pkh'. :param address: A base-58 encoded address :type address: str :return dict: with information about this address """ try: address_bytes = change_base(address, 58, 256, 25) except EncodingError as err: raise EncodingError("Invalid address %s: %s" % (address, err)) check = address_bytes[-4:] key_hash = address_bytes[:-4] checksum = hashlib.sha256(hashlib.sha256(key_hash).digest()).digest()[0:4] assert (check == checksum), "Invalid address, checksum incorrect" address_prefix = key_hash[0:1] networks_p2pkh = network_by_value('prefix_address', address_prefix) networks_p2sh = network_by_value('prefix_address_p2sh', address_prefix) public_key_hash = key_hash[1:] script_type = '' network = '' if networks_p2pkh and not networks_p2sh: script_type = 'p2pkh' if len(networks_p2pkh) == 1: network = networks_p2pkh[0] elif networks_p2sh and not networks_p2pkh: script_type = 'p2sh' if len(networks_p2sh) == 1: network = networks_p2sh[0] return { 'address': address, 'public_key_hash': change_base(public_key_hash, 256, 16), 'public_key_hash_bytes': public_key_hash, 'prefix': address_prefix, 'network': network, 'script_type': script_type, 'networks_p2sh': networks_p2sh, 'networks_p2pkh': networks_p2pkh }
def to_mnemonic(self, data, add_checksum=True): """ Convert key data entropy to Mnemonic sentence :param data: Key data entropy :type data: bytes, hexstring :param add_checksum: Included a checksum? Default is True :type add_checksum: bool :return str: Mnemonic passphrase consisting of a space seperated list of words """ data = to_bytes(data) if add_checksum: binresult = change_base(data, 256, 2, len(data) * 8) + self.checksum(data) wi = change_base(binresult, 2, 2048) else: wi = change_base(data, 256, 2048) return normalize_string(' '.join([self._wordlist[i] for i in wi]))
def _bip38_decrypt(encrypted_privkey, passphrase): """ BIP0038 non-ec-multiply decryption. Returns WIF private key. Based on code from https://github.com/nomorecoin/python-bip38-testing This method is called by Key class init function when importing BIP0038 key. :param encrypted_privkey: Encrypted private key using WIF protected key format :type encrypted_privkey: str :param passphrase: Required passphrase for decryption :type passphrase: str :return str: Private Key WIF """ # TODO: Also check first 2 bytes d = change_base(encrypted_privkey, 58, 256)[2:] flagbyte = d[0:1] d = d[1:] if flagbyte == b'\xc0': compressed = False elif flagbyte == b'\xe0': compressed = True else: raise Warning( "Unrecognised password protected key format. Flagbyte incorrect." ) addresshash = d[0:4] d = d[4:-4] key = scrypt.hash(passphrase, addresshash, 16384, 8, 8) derivedhalf1 = key[0:32] derivedhalf2 = key[32:64] encryptedhalf1 = d[0:16] encryptedhalf2 = d[16:32] aes = AES.new(derivedhalf2) decryptedhalf2 = aes.decrypt(encryptedhalf2) decryptedhalf1 = aes.decrypt(encryptedhalf1) priv = decryptedhalf1 + decryptedhalf2 priv = binascii.unhexlify('%064x' % (int(binascii.hexlify(priv), 16) ^ int(binascii.hexlify(derivedhalf1), 16))) if compressed: # FIXME: This works but does probably not follow the BIP38 standards (was before: priv = b'\0' + priv) priv += b'\1' key_format = 'wif_compressed' else: key_format = 'wif' k = Key(priv, compressed=compressed) wif = k.wif() addr = k.address() if isinstance(addr, str) and sys.version_info > (3, ): addr = addr.encode('utf-8') if hashlib.sha256( hashlib.sha256(addr).digest()).digest()[0:4] != addresshash: print( 'Addresshash verification failed! Password is likely incorrect.' ) return wif, key_format
def generate(self, strength=128, add_checksum=True): """ Generate a Mnemonic key :param strength: Key strenght in number of bits :param add_checksum: Boolean to specify if checksum needs to be included :return: Mnemonic passphrase """ data = change_base(os.urandom(strength // 8), 256, 16) return self.to_mnemonic(data, add_checksum=add_checksum)
def info(self): if self._secret: print("SECRET EXPONENT") print(" Private Key (hex) %s" % change_base(self._secret, 256, 16)) print(" Private Key (long) %s" % change_base(self._secret, 256, 10)) print(" Private Key (wif) %s" % self.wif()) else: print("PUBLIC KEY ONLY, NO SECRET EXPONENT") print("") print(" Compressed %s" % self.compressed()) print("PUBLIC KEY") print(" Public Key (hex) %s" % self.public()) print(" Public Key uncompr. (hex) %s" % self.public_uncompressed()) print(" Address (b58) %s" % self.address()) print(" Address uncompressed (b58) %s" % self.address_uncompressed()) point_x, point_y = self.public_point() print(" Point x %s" % point_x) print(" Point y %s" % point_y) print("\n")
def extended_wif(self, public=None, child_index=None): rkey = self._key if not self._isprivate and public is False: return '' if self._isprivate and not public: raw = NETWORKS[self._network]['hdkey_private'] typebyte = b'\x00' else: raw = NETWORKS[self._network]['hdkey_public'] typebyte = b'' if public: rkey = self.public().public_byte() if child_index: self._child_index = child_index raw += change_base(self._depth, 10, 256, 1) + self._parent_fingerprint + \ struct.pack('>L', self._child_index) + self._chain + typebyte + rkey chk = hashlib.sha256(hashlib.sha256(raw).digest()).digest()[:4] ret = raw+chk return change_base(ret, 256, 58, 111)
def from_seed(import_seed): """ Used by class init function, import key from seed :param import_seed: Hex representation of private key seed :return: HDKey class object """ seed = change_base(import_seed, 16, 256) I = hmac.new(b"Bitcoin seed", seed, hashlib.sha512).digest() key = I[:32] chain = I[32:] return HDKey(key=key, chain=chain)
def _check_list(self, language, vectors): mnemo = Mnemonic(language) for v in vectors: if v[0]: phrase = mnemo.to_mnemonic(v[0]) else: phrase = v[1] seed = change_base(mnemo.to_seed(phrase, v[4]), 256, 16) print("Test %s => %s" % (v[0], phrase)) self.assertEqual(v[1], phrase) self.assertEqual(v[2], seed) k = HDKey.from_seed(seed) self.assertEqual(k.extended_wif(), v[3])
def _check_list(self, language, vectors): mnemo = Mnemonic(language) for v in vectors: if v[0]: phrase = mnemo.to_mnemonic(v[0]) else: phrase = v[1] seed = change_base(mnemo.to_seed(phrase, v[4]), 256, 16) print("Test %s => %s" % (v[0], phrase)) self.assertEqual(v[1], phrase) self.assertEqual(v[2], seed) k = HDKey.from_seed(seed) self.assertEqual(k.wif(), v[3])
def _check_list(self, language, vectors): mnemo = Mnemonic(language) for v in vectors: if v[0]: phrase = mnemo.to_mnemonic(v[0], check_on_curve=False) else: phrase = v[1] seed = change_base(mnemo.to_seed(phrase, v[4], validate=False), 256, 16) # print("Test %s => %s" % (v[0], phrase)) self.assertEqual(v[1], phrase) self.assertEqual(v[2], seed) k = HDKey.from_seed(seed) self.assertEqual(k.wif(is_private=True), v[3])
def checksum(data): """ Calculates checksum for given data key :param data: key string :type data: bytes, hexstring :return str: Checksum of key in bits """ data = to_bytes(data) if len(data) % 4 > 0: raise ValueError('Data length in bits should be divisible by 32, but it is not (%d bytes = %d bits).' % (len(data), len(data) * 8)) tx_hash = hashlib.sha256(data).digest() return change_base(tx_hash, 256, 2, 256)[:len(data) * 8 // 32]
def info(self): if self._isprivate: print("SECRET EXPONENT") print(" Private Key (hex) %s" % change_base(self._key, 256, 16)) print(" Private Key (long) %s" % self._secret) print(" Private Key (wif) %s" % self.private().wif()) print("") print("PUBLIC KEY") print(" Public Key (hex) %s" % self.public()) print(" Address (b58) %s" % self.public().address()) print(" Fingerprint (hex) %s" % change_base(self.fingerprint(), 256, 16)) point_x, point_y = self.public().public_point() print(" Point x %s" % point_x) print(" Point y %s" % point_y) print("") print("EXTENDED KEY INFO") print(" Key Type %s" % self.key_type) print(" Chain code (hex) %s" % change_base(self.chain(), 256, 16)) print(" Child Index %s" % self.child_index()) print(" Parent Fingerprint (hex) %s" % change_base(self.parent_fingerprint(), 256, 16)) print(" Depth %s" % self.depth()) print(" Extended Public Key (wif) %s" % self.extended_wif_public()) print(" Extended Private Key (wif) %s" % self.extended_wif(public=False)) print("\n")
def address(self, compressed=None): """ Get address derived from public key :param compressed: Always return compressed address :type compressed: bool :return str: Base58 encoded address """ if (self.compressed and compressed is None) or compressed: key = self.public_byte else: key = self.public_uncompressed_byte versionbyte = self.network.prefix_address key = versionbyte + hashlib.new('ripemd160', hashlib.sha256(key).digest()).digest() checksum = hashlib.sha256(hashlib.sha256(key).digest()).digest()[:4] return change_base(key + checksum, 256, 58)
def check_proof_of_work(self): """ Check proof of work for this block. Block hash must be below target. This library is not optimised for mining, but you can use this for testing or learning purposes. >>> b = Block('0000000000000000000154ba9d02ddd6cee0d71d1ea232753e02c9ac6affd709', version=0x20000000, prev_block='0000000000000000000f9578cda278ae7a2002e50d8e6079d11e2ea1f672b483', merkle_root='20e86f03c24c53c12014264d0e405e014e15a02ad02c174f017ee040750f8d9d', time=1592848036, bits=387044594, nonce=791719079) >>> b.check_proof_of_work() True :return bool: """ if not self.block_hash or not self.bits: return False if change_base(self.block_hash, 256, 10) < self.target: return True return False
def _bip38_decrypt(encrypted_privkey, passphrase): """ BIP0038 non-ec-multiply decryption. Returns WIF privkey. Based on code from https://github.com/nomorecoin/python-bip38-testing This method is called by Key class init function when importing BIP0038 key. :param encrypted_privkey: Encrypted Private Key using WIF protected key format :param passphrase: Required passphrase for decryption :return: Private Key WIF """ d = change_base(encrypted_privkey, 58, 256)[2:] flagbyte = d[0:1] d = d[1:] if flagbyte == b'\xc0': compressed = False elif flagbyte == b'\xe0': compressed = True else: raise Warning("Unrecognised password protected key format. Flagbyte incorrect.") addresshash = d[0:4] d = d[4:-4] key = scrypt.hash(passphrase, addresshash, 16384, 8, 8) derivedhalf1 = key[0:32] derivedhalf2 = key[32:64] encryptedhalf1 = d[0:16] encryptedhalf2 = d[16:32] aes = AES.new(derivedhalf2) decryptedhalf2 = aes.decrypt(encryptedhalf2) decryptedhalf1 = aes.decrypt(encryptedhalf1) priv = decryptedhalf1 + decryptedhalf2 priv = binascii.unhexlify('%064x' % (int(binascii.hexlify(priv), 16) ^ int(binascii.hexlify(derivedhalf1), 16))) if compressed: priv = b'\0' + priv key_format = 'wif_compressed' else: key_format = 'wif' k = Key(priv, compressed=compressed) wif = k.wif() addr = k.address() if isinstance(addr, str) and sys.version_info > (3,): addr = addr.encode('utf-8') if hashlib.sha256(hashlib.sha256(addr).digest()).digest()[0:4] != addresshash: print('Addresshash verification failed! Password is likely incorrect.') return wif, key_format
def test_change_base_bin_b58(self): self.assertEqual('16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM', change_base("\x00\x01\tfw`\x06\x95=UgC\x9e^9\xf8j\r';\xee\xd6\x19g\xf6", 256, 58))
def __init__(self, import_key=None, key=None, chain=None, depth=0, parent_fingerprint=b'\0\0\0\0', child_index=0, isprivate=True, network=None, key_type='bip32', passphrase='', compressed=True): """ Hierarchical Deterministic Key class init function. If no import_key is specified a key will be generated with systems cryptographically random function. Import key can be any format normal or HD key (extended key) accepted by get_key_format. If a normal key with no chain part is provided, an chain with only 32 0-bytes will be used. :param import_key: HD Key to import in WIF format or as byte with key (32 bytes) and chain (32 bytes) :type import_key: str, bytes, int, bytearray :param key: Private or public key (length 32) :type key: bytes :param chain: A chain code (length 32) :type chain: bytes :param depth: Level of depth in BIP32 key path :type depth: int :param parent_fingerprint: 4-byte fingerprint of parent :type parent_fingerprint: bytes :param child_index: Index number of child as integer :type child_index: int :param isprivate: True for private, False for public key. Default is True :type isprivate: bool :param network: Network name. Derived from import_key if possible :type network: str :param key_type: HD BIP32 or normal Private Key. Default is 'bip32' :type key_type: str :return HDKey: """ self.key_format = None if (key and not chain) or (not key and chain): raise KeyError( "Please specify both key and chain, use import_key attribute " "or use simple Key class instead") self.compressed = compressed self.key = None if not (key and chain): if not import_key: # Generate new Master Key seedbits = random.SystemRandom().getrandbits(512) seed = change_base(str(seedbits), 10, 256, 64) key, chain = self._key_derivation(seed) elif isinstance(import_key, (bytearray, bytes if sys.version > '3' else bytearray)) and len(import_key) == 64: key = import_key[:32] chain = import_key[32:] elif isinstance(import_key, Key): self.key = import_key if not import_key.compressed: _logger.warning( "Uncompressed private keys are not standard for BIP32 keys, use at your own risk!" ) self.compressed = False chain = b'\0' * 32 key = self.key.private_byte key_type = 'private' else: kf = get_key_format(import_key) self.key_format = kf["format"] network = check_network_and_key(import_key, network, kf["networks"]) self.network = Network(network) if self.key_format in ['hdkey_private', 'hdkey_public']: bkey = change_base(import_key, 58, 256) # Derive key, chain, depth, child_index and fingerprint part from extended key WIF if ord(bkey[45:46]): isprivate = False key = bkey[45:78] else: key = bkey[46:78] depth = ord(bkey[4:5]) parent_fingerprint = bkey[5:9] child_index = int(change_base(bkey[9:13], 256, 10)) chain = bkey[13:45] # chk = bkey[78:82] else: try: self.key = Key(import_key, passphrase=passphrase, network=network) if not self.key.compressed: _logger.warning( "Uncompressed private keys are not standard for BIP32 keys, use at your own risk!" ) self.compressed = False # FIXME: Maybe its better to create a random chain? chain = b'\0' * 32 key = self.key.private_byte key_type = 'private' except BKeyError as e: raise BKeyError("[BKeyError] %s" % e) if not isinstance(key, (bytes, bytearray)) or not (len(key) == 32 or len(key) == 33): raise KeyError( "Invalid key specified must be in bytes with length 32. You can use " "'import_key' attribute to import keys in other formats") self.chain = chain if self.key is None: self.key = Key(key, passphrase=passphrase, network=network, compressed=compressed) self.depth = depth self.parent_fingerprint = parent_fingerprint self.child_index = child_index self.isprivate = isprivate if not network: network = DEFAULT_NETWORK self.network = Network(network) self.public_byte = self.key.public_byte self.public_hex = self.key.public_hex self.secret = None self.private_hex = None self.private_byte = None if isprivate: self.secret = self.key.secret self.private_hex = self.key.private_hex self.private_byte = self.key.private_byte self.key_hex = self.private_hex else: self.key_hex = self.public_hex self.key_type = key_type
def __init__(self, import_key=None, network=None, compressed=True, passphrase=''): """ Initialize a Key object. Import key can be in WIF, bytes, hexstring, etc. If a private key is imported a public key will be derived. If a public is imported the private key data will be empty. Both compressed and uncompressed key version is available, the Key.compressed boolean attribute tells if the original imported key was compressed or not. :param import_key: If specified import given private or public key. If not specified a new private key is generated. :type import_key: str, int, bytes, bytearray :param network: Bitcoin, testnet, litecoin or other network :type network: str :param compressed: Is key compressed or not, default is True :type compressed: bool :param passphrase: Optional passphrase if imported key is password protected :type passphrase: str :return: Key object """ self.public_hex = None self.public_uncompressed_hex = None self.public_compressed_hex = None self.public_byte = None self.public_uncompressed_byte = None self.public_compressed_byte = None self.private_byte = None self.private_hex = None self._x = None self._y = None self.secret = None self.compressed = compressed if not import_key: import_key = random.SystemRandom().randint(0, secp256k1_n) kf = get_key_format(import_key) self.key_format = kf["format"] network = check_network_and_key(import_key, network, kf["networks"]) self.network = Network(network) if kf['isprivate']: self.isprivate = True elif kf['isprivate'] is None: raise KeyError("Could not determine if key is private or public") else: self.isprivate = False if self.key_format == "wif_protected": # TODO: return key as byte to make more efficient import_key, self.key_format = self._bip38_decrypt( import_key, passphrase) if not self.isprivate: self.secret = None pub_key = to_hexstring(import_key) if len(pub_key) == 130: self.public_uncompressed_hex = pub_key self._x = pub_key[2:66] self._y = pub_key[66:130] self.compressed = False if int(self._y, 16) % 2: prefix = '03' else: prefix = '02' self.public_hex = prefix + self._x self.public_compressed_hex = prefix + self._x else: self.public_hex = pub_key self._x = pub_key[2:66] self.compressed = True # Calculate y from x with y=x^3 + 7 function sign = pub_key[:2] == '03' x = int(self._x, 16) ys = (x**3 + 7) % secp256k1_p y = ecdsa.numbertheory.square_root_mod_prime(ys, secp256k1_p) if y & 1 != sign: y = secp256k1_p - y self._y = change_base(y, 10, 16, 64) self.public_uncompressed_hex = '04' + self._x + self._y self.public_compressed_hex = pub_key self.public_compressed_byte = binascii.unhexlify(self.public_hex) self.public_uncompressed_byte = binascii.unhexlify( self.public_uncompressed_hex) if self.compressed: self.public_byte = self.public_compressed_byte else: self.public_byte = self.public_uncompressed_byte elif self.isprivate and self.key_format == 'decimal': self.secret = import_key self.private_hex = change_base(import_key, 10, 16, 64) self.private_byte = binascii.unhexlify(self.private_hex) elif self.isprivate: if self.key_format == 'hex': key_hex = import_key key_byte = binascii.unhexlify(key_hex) elif self.key_format == 'hex_compressed': key_hex = import_key[:-2] key_byte = binascii.unhexlify(key_hex) self.compressed = True elif self.key_format == 'bin': key_byte = import_key key_hex = to_hexstring(key_byte) elif self.key_format == 'bin_compressed': key_byte = import_key[:-1] key_hex = to_hexstring(key_byte) self.compressed = True elif self.isprivate and self.key_format in [ 'wif', 'wif_compressed' ]: # Check and remove Checksum, prefix and postfix tags key = change_base(import_key, 58, 256) checksum = key[-4:] key = key[:-4] if checksum != hashlib.sha256( hashlib.sha256(key).digest()).digest()[:4]: raise BKeyError("Invalid checksum, not a valid WIF key") found_networks = network_by_value('prefix_wif', key[0:1]) if not len(found_networks): raise BKeyError( "Unrecognised WIF private key, version byte unknown. Versionbyte: %s" % key[0:1]) if self.network.network_name not in found_networks: if len(found_networks) > 1: raise BKeyError( "More then one network found with this versionbyte, please specify network. " "Networks found: %s" % found_networks) else: _logger.warning( "Current network %s is different then the one found in key: %s" % (network, found_networks[0])) self.network = Network(found_networks[0]) if key[-1:] == b'\x01': self.compressed = True key = key[:-1] else: self.compressed = False key_byte = key[1:] key_hex = to_hexstring(key_byte) else: raise KeyError("Unknown key format %s" % self.key_format) if not (key_byte or key_hex): raise KeyError("Cannot format key in hex or byte format") self.private_hex = key_hex self.private_byte = key_byte self.secret = int(key_hex, 16) else: raise KeyError("Cannot import key. Public key format unknown") if self.isprivate and not (self.public_byte or self.public_hex): if not self.isprivate: raise KeyError("Private key has no known secret number") point = ec_point(self.secret) self._x = change_base(point.x(), 10, 16, 64) self._y = change_base(point.y(), 10, 16, 64) if point.y() % 2: prefix = '03' else: prefix = '02' self.public_compressed_hex = prefix + self._x self.public_uncompressed_hex = '04' + self._x + self._y self.public_hex = self.public_compressed_hex if self.compressed else self.public_uncompressed_hex self.public_byte = binascii.unhexlify(self.public_hex) self.public_compressed_byte = binascii.unhexlify( self.public_compressed_hex) self.public_uncompressed_byte = binascii.unhexlify( self.public_uncompressed_hex)
def test_change_base_hex_bin(self): self.assertEqual(b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f', change_base("000102030405060708090a0b0c0d0e0f", 16, 256))
def test_change_base_zero_int(self): self.assertEqual(0, change_base(b'\0', 256, 10))
def test_change_base32_base3(self): self.assertEqual(', . .. ., . ,. ., ,. .., . ,.. .,. ,..,, . . .,.,.. ,...,, ,, .,.,,,.,,,... . , .,,,' '. ...,, .,.,. ,,..,,,,.', change_base("Oh what a fun we have !", 256, 3))
def test_change_base_leading_zeros4(self): self.assertEqual(b'\x04G\x81', change_base('044781', 16, 256))
def test_change_base_b58_hex(self): self.assertEqual('00D77BF7628C19E699010D29787A29AFCF8E92AD5A053D55D7', change_base('1LeNnaRtV52nNtZXvtw6PaGKpk46hU1Xmx', 58, 16).upper())
def test_change_base_encodings_bytes(self): self.assertEqual('4c52127a72fb42b82439ab18697dcfcfb96ac63ba8209833b2e29f2302b8993f45e743412d65c7a571da70259d4' 'f6795e98af20e6e57603314a662a49c198199', change_base(b'LR\x12zr\xfbB\xb8$9\xab\x18i}\xcf\xcf\xb9j\xc6;\xa8 \x983\xb2\xe2\x9f#\x02\xb8' b'\x99?E\xe7CA-e\xc7\xa5q\xdap%\x9dOg\x95\xe9\x8a\xf2\x0enW`3\x14\xa6b\xa4\x9c' b'\x19\x81\x99', 256, 16))
def test_change_base_encodings_utf8(self): self.assertEqual('e782ba20e5ae8b20e69ab420e6b2bb20e4bcaf20e58f8a20e7819820e586b620e5bf9920e9808320e6b99820e8' '898720e4be8b20e8ae9320e5bfa0', change_base("為 宋 暴 治 伯 及 灘 冶 忙 逃 湘 艇 例 讓 忠", 256, 16))
def bech32_to_hash160(address): return change_base(addr_bech32_to_pubkeyhash(address), 256, 16)
def test_change_base_leading_zeros_binascii(self): y = 251863285056225027460663457133976813779860093019161001622713253221998044380 self.assertEqual(64, len(change_base(y, 10, 16, 64)))
def test_change_base_padding(self): self.assertEqual('0011', change_base(3, 10, 2, 4))
def test_change_base_leading_zeros3(self): self.assertEqual('1L', change_base('013', 16, 58))
def test_change_base_dec_b58(self): self.assertEqual('LeNnaRtV52nNtZXvtw6PaGKpk46hU1Xmx', change_base('5283658277747592673868818217239156372404875337009783985623', 10, 58, 33))
def test_change_base_leading_zeros(self): self.assertEqual(b'\x00\x00\x03', change_base("000003", 16, 256))
def get_key_format(key, isprivate=None): """ Determins the type (private or public), format and network key. This method does not validate if a key is valid. :param key: Any private or public key :type key: str, int, bytes, bytearray :param isprivate: Is key private or not? :type isprivate: bool :return dict: Dictionary with format, network and isprivate """ if not key: raise BKeyError("Key empty, please specify a valid key") key_format = "" networks = None if isinstance(key, (bytes, bytearray)) and len(key) in [128, 130]: key = to_hexstring(key) if not (isprivate is None or isinstance(isprivate, bool)): raise BKeyError("Attribute 'is_private' must be False or True") elif isinstance(key, numbers.Number): key_format = 'decimal' isprivate = True elif isinstance(key, (bytes, bytearray)) and len(key) in [ 33, 65 ] and key[:1] in [b'\2', b'\3']: key_format = 'bin_compressed' isprivate = False elif isinstance(key, (bytes, bytearray)) and (len(key) in [33, 65] and key[:1] == b'\4'): key_format = 'bin' isprivate = False elif isinstance( key, (bytes, bytearray)) and len(key) == 33 and key[-1:] == b'\1': key_format = 'bin_compressed' isprivate = True elif isinstance(key, (bytes, bytearray)) and len(key) == 32: key_format = 'bin' isprivate = True elif len(key) == 130 and key[:2] == '04' and not isprivate: key_format = 'public_uncompressed' isprivate = False elif len(key) == 128: key_format = 'hex' if isprivate is None: isprivate = True elif len(key) == 66 and key[:2] in ['02', '03'] and not isprivate: key_format = 'public' isprivate = False elif len(key) == 64: key_format = 'hex' if isprivate is None: isprivate = True elif len(key) == 66 and key[-2:] in ['01'] and not (isprivate is False): key_format = 'hex_compressed' isprivate = True elif len(key) == 58 and key[:2] == '6P': key_format = 'wif_protected' isprivate = True else: try: key_hex = change_base(key, 58, 16) networks = network_by_value('prefix_wif', key_hex[:2]) if networks: if key_hex[-10:-8] == '01': key_format = 'wif_compressed' else: key_format = 'wif' isprivate = True else: networks = network_by_value('prefix_hdkey_private', key_hex[:8]) if networks: key_format = 'hdkey_private' isprivate = True else: networks = network_by_value('prefix_hdkey_public', key_hex[:8]) if networks: key_format = 'hdkey_public' isprivate = False # TODO: Recognise address key and implement in Key en HDKey classs # else: # networks = network_by_value('prefix_address_p2sh', key_hex[:2]) + \ # network_by_value('prefix_address', key_hex[:2]) # if networks: # key_format = 'address' # isprivate = False except (TypeError, EncodingError): pass if not key_format: try: int(key) if 70 < len(key) < 78: key_format = 'decimal' isprivate = True except (TypeError, ValueError): pass if not key_format: raise BKeyError("Key: %s. Unrecognised key format" % key) else: return { "format": key_format, "networks": networks, "isprivate": isprivate }
if __name__ == '__main__': # # SOME EXAMPLES # from bitcoinlib.keys import HDKey # Convert hexadecimal to mnemonic and back again to hex print("\nConvert hexadecimal to mnemonic and back again to hex") pk = '7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f' words = Mnemonic().to_mnemonic(pk) print("Hex %s" % pk) print("Checksum bin %s" % Mnemonic().checksum(pk)) print("Mnemonic %s" % words) print(Mnemonic().to_seed(words, 'test')) print("Seed for HD Key %s" % change_base(Mnemonic().to_seed(words, 'test'), 256, 16)) print("Back to Hex %s" % Mnemonic().to_entropy(words)) # Generate a random Mnemonic HD Key print("\nGenerate a random Mnemonic HD Key") entsize = 128 words = Mnemonic('english').generate(entsize) print("Your Mnemonic is %s" % words) print(" (An avarage of %d tries is needed to brute-force this password)" % ((2 ** entsize) // 2)) seed = change_base(Mnemonic().to_seed(words), 256, 16) hdk = HDKey().from_seed(seed) print("Seed for HD Key %s" % change_base(seed, 256, 16)) print("HD Key WIF is %s" % hdk) # Generate a key from a Mnemonic sentence print("\nGenerate a key from a Mnemonic sentence")