def find_name_index(name_address, master_privkey_hex, max_tries=25, start=0): """ Given a name's device-specific address and device-specific master key, find index from which it was derived. Return the index on success Return None on failure. """ hdwallet = HDWallet(master_privkey_hex) for i in xrange(start, max_tries): child_privkey = hdwallet.get_child_privkey(index=i) child_pubkey = get_pubkey_hex(child_privkey) child_addresses = [ keylib.public_key_to_address( keylib.key_formatting.compress(child_pubkey)), keylib.public_key_to_address( keylib.key_formatting.decompress(child_pubkey)) ] if str(name_address) in child_addresses: return i return None
def get_master_address(self): if self.master_address is not None: return self.master_address hex_privkey = self.get_master_privkey() hex_pubkey = get_pubkey_hex(hex_privkey) return virtualchain.address_reencode(keylib.public_key_to_address(hex_pubkey))
def get_master_address(self): if self.master_address is not None: return self.master_address hex_privkey = self.get_master_privkey() hex_pubkey = get_pubkey_hex(hex_privkey) return keylib.public_key_to_address(hex_pubkey)
def get_master_address(self): if self.master_address is not None: return self.master_address hex_privkey = self.get_master_privkey() hex_pubkey = get_pubkey_hex(hex_privkey) return virtualchain.address_reencode( keylib.public_key_to_address(hex_pubkey))
def get_child_address(self, index=0): """ @index is the child index Returns: child address for given @index """ if self.child_addresses is not None: return self.child_addresses[index] hex_privkey = self.get_child_privkey(index) hex_pubkey = get_pubkey_hex(hex_privkey) return keylib.public_key_to_address(hex_pubkey)
def get_child_address(self, index=0): """ @index is the child index Returns: child address for given @index """ if self.child_addresses is not None: return self.child_addresses[index] # force decompressed... hex_privkey = self.get_child_privkey(index) hex_pubkey = get_pubkey_hex(hex_privkey) return virtualchain.address_reencode(keylib.public_key_to_address(hex_pubkey))
def get_child_address(self, index=0): """ @index is the child index Returns: child address for given @index """ if self.child_addresses is not None: return self.child_addresses[index] # force decompressed... hex_privkey = self.get_child_privkey(index) hex_pubkey = get_pubkey_hex(hex_privkey) return virtualchain.address_reencode( keylib.public_key_to_address(hex_pubkey))
def parse_mutable_data_v2(mutable_data_json_txt, public_key_hex, public_key_hash=None, data_hash=None, raw=False): """ Version 2 parser Parse a piece of mutable data back into the serialized payload. Verify that it was signed by the given public key, or the public key hash. If neither are given, then verify that it has the given hash. Return the data on success Return None on error """ pubk_hex = None sig_b64 = None data_txt = None original_data_txt = None if not raw: # format: bsk2.pubkey.sigb64.data_len:data, parts = mutable_data_json_txt.split(".", 3) if len(parts) != 4: log.debug("Malformed data: {}".format(mutable_data_json_txt)) return None if parts[0] != 'bsk2': log.debug("Not v2 data") return None pubk_hex = str(parts[1]) sig_b64 = str(parts[2]) data_txt = str(parts[3]) # basic sanity checks if not re.match('^[0-9a-fA-F]+$', pubk_hex): log.debug("Not a v2 mutable datum: Invalid public key") return None if not re.match(schemas.OP_BASE64_PATTERN_SECTION, sig_b64): log.debug("Not a v2 mutable datum: Invalid signature data") return None try: sig_bin = base64.b64decode(sig_b64) except: log.error("Incorrect base64-encoding") return None # data_txt must be a netstring (format: 'len(payload):payload,') serialized_len = len(data_txt) original_data_txt = data_txt[:] data_txt = parse_data_payload(data_txt) if data_txt is None: log.debug( "Invalid data payload of {} bytes".format(serialized_len)) return None else: data_txt = mutable_data_json_txt original_data_txt = mutable_data_json_txt # shortcut: if hash is given, we're done if data_hash is not None: dh = hash_data_payload(str(data_txt)) if dh == data_hash: # done! log.debug("Verified with hash {}".format(data_hash)) return data_txt else: log.debug( "Hash mismatch: expected {}, got {}\noriginal_data_text ({}): '{}'\nlen(original_data_text): {}\nparsed payload: '{}'\nhash_data_payload: {}" .format(data_hash, dh, type(original_data_txt), original_data_txt, len(original_data_txt), parse_data_payload(original_data_txt), hash_data_payload(data_txt))) # validate if keylib.key_formatting.get_pubkey_format(pubk_hex) == 'hex_compressed': pubk_hex = keylib.key_formatting.decompress(pubk_hex) if public_key_hex is not None: # make sure uncompressed given_pubkey_hex = str(public_key_hex) if keylib.key_formatting.get_pubkey_format( given_pubkey_hex) == 'hex_compressed': given_pubkey_hex = keylib.key_formatting.decompress( given_pubkey_hex) log.debug("Try verify with {}".format(pubk_hex)) if given_pubkey_hex == pubk_hex: if verify_data_payload(data_txt, pubk_hex, sig_b64): log.debug( "Verified payload with public key {}".format(pubk_hex)) return data_txt else: log.debug("Signature failed") else: log.debug("Public key mismatch: {} != {}".format( given_pubkey_hex, pubk_hex)) if public_key_hash is not None: pubkey_hash = keylib.address_formatting.bin_hash160_to_address( keylib.address_formatting.address_to_bin_hash160( str(public_key_hash), ), version_byte=0) log.debug("Try verify with {}".format(pubkey_hash)) pubk_compressed = keylib.key_formatting.compress(pubk_hex) pubk_uncompressed = keylib.key_formatting.decompress(pubk_hex) if keylib.public_key_to_address( pubk_compressed ) == pubkey_hash or keylib.public_key_to_address( pubk_uncompressed) == pubkey_hash: if verify_data_payload(data_txt, pubk_hex, sig_b64): log.debug( "Verified payload with public key hash {} ({})".format( pubk_hex, pubkey_hash)) return data_txt else: log.debug("Signature failed with pubkey hash") else: log.debug("Public key hash mismatch") log.debug("Failed to verify v2 mutable datum") return None
def test_public_key_to_address_uncompressed(self): address = public_key_to_address(self.ref['uncompressed_public_key']) self.assertEqual(address, self.ref['uncompressed_address'])
def parse_mutable_data_v2(mutable_data_json_txt, public_key_hex, public_key_hash=None, data_hash=None): """ Version 2 parser Parse a piece of mutable data back into the serialized payload. Verify that it was signed by the given public key, or the public key hash. If neither are given, then verify that it has the given hash. Return the data on success Return None on error """ parts = mutable_data_json_txt.split(".", 2) if len(parts) != 3: log.debug("Malformed data: {}".format(mutable_data_json_txt)) return None pubk_hex = str(parts[0]) sig_b64 = str(parts[1]) data_txt = str(parts[2]) if not re.match('^[0-9a-fA-F]+$', pubk_hex): log.debug("Not a v2 mutable datum: Invalid public key") return None if not re.match(schemas.OP_BASE64_PATTERN_SECTION, sig_b64): log.debug("Not a v2 mutable datum: Invalid signature data") return None # shortcut: if hash is given, we're done if data_hash is not None: dh = hashlib.sha256(data_txt.encode('utf-8')).hexdigest() if dh == data_hash: # done! log.debug("Verified with hash {}".format(data_hash)) return data_txt else: log.debug("Hash mismatch: expected {}, got {}".format(data_hash, dh)) # validate if keylib.key_formatting.get_pubkey_format(pubk_hex) == 'hex_compressed': pubk_hex = keylib.key_formatting.decompress(pubk_hex) try: sig_bin = base64.b64decode(sig_b64) except: log.error("Incorrect base64-encoding") return None if public_key_hex is not None: # make sure uncompressed given_pubkey_hex = str(public_key_hex) if keylib.key_formatting.get_pubkey_format(given_pubkey_hex) == 'hex_compressed': given_pubkey_hex = keylib.key_formatting.decompress(given_pubkey_hex) log.debug("Try verify with {}".format(pubk_hex)) if given_pubkey_hex == pubk_hex: if verify_raw_data(data_txt, pubk_hex, sig_bin): log.debug("Verified with public key {}".format(pubk_hex)) return data_txt else: log.debug("Signature failed") else: log.debug("Public key mismatch: {} != {}".format(given_pubkey_hex, pubk_hex)) if public_key_hash is not None: pubkey_hash = keylib.address_formatting.bin_hash160_to_address( keylib.address_formatting.address_to_bin_hash160( str(public_key_hash), ), version_byte=0 ) log.debug("Try verify with {}".format(pubkey_hash)) if keylib.public_key_to_address(pubk_hex) == pubkey_hash: if verify_raw_data(data_txt, pubk_hex, sig_bin): log.debug("Verified with public key hash {} ({})".format(pubk_hex, pubkey_hash)) return data_txt else: log.debug("Signature failed with pubkey hash") else: log.debug("Public key hash mismatch") log.debug("Failed to verify v2 mutable datum") return None
def parse_mutable_data(mutable_data_json_txt, public_key, public_key_hash=None, data_hash=None, bsk_version=None, return_public_key=False): """ Given the serialized JSON for a piece of mutable data, parse it into a JSON document. Verify that it was signed by public_key's or public_key_hash's private key. Try to verify with both keys, if given. Returns: * the parsed JSON dict on success (if a profile) * the raw data (otherwise) * the dict {'data': ..., 'public_key': ...} (if return_public_key is True) Return None on error """ # newer version? if mutable_data_json_txt.startswith("bsk2.") or bsk_version == 2: raw = False if not mutable_data_json_txt.startswith("bsk2."): # raw data; will authenticate with data hash raw = True if data_hash is None: log.error( "Corrupt data: data text does not start with 'bsk2.', and no data hash given" ) return None return parse_mutable_data_v2(mutable_data_json_txt, public_key, public_key_hash=public_key_hash, data_hash=data_hash, raw=raw, return_public_key=return_public_key) # legacy parser assert public_key is not None or public_key_hash is not None, 'Need a public key or public key hash' mutable_data_jwt = None try: mutable_data_jwt = json.loads(mutable_data_json_txt) assert isinstance(mutable_data_jwt, (dict, list)) except: # TODO: Check use of catchall exception handler log.error('Invalid JSON') return None mutable_data_json = None # try pubkey, if given if public_key is not None: mutable_data_json = blockstack_profiles.get_profile_from_tokens( mutable_data_jwt, str(public_key)) if len(mutable_data_json) > 0: if return_public_key: return { 'data': mutable_data_json, 'public_key': str(public_key) } else: return mutable_data_json msg = 'Failed to verify with public key "{}"' log.warn(msg.format(public_key)) # try pubkey address if public_key_hash is not None: # NOTE: these should always have version byte 0 # TODO: use jsontokens directly public_key_hash_0 = keylib.address_formatting.bin_hash160_to_address( keylib.address_formatting.address_to_bin_hash160( str(public_key_hash)), version_byte=0) mutable_data_json = blockstack_profiles.get_profile_from_tokens( mutable_data_jwt, public_key_hash_0) if len(mutable_data_json) > 0: log.debug('Verified with {}'.format(public_key_hash)) if return_public_key: profile_token = jsontokens.decode_token( mutable_data_jwt[0]['token']) issuer_public_key = profile_token['payload']['issuer'][ 'publicKey'] # use the one that corresponds to the address ret_pubkey = None if virtualchain.address_reencode( keylib.public_key_to_address( keylib.key_formatting.compress( str(issuer_public_key))) ) == virtualchain.address_reencode(str(public_key_hash)): ret_pubkey = keylib.key_formatting.compress( issuer_public_key) elif virtualchain.address_reencode( keylib.public_key_to_address( keylib.key_formatting.decompress( str(issuer_public_key))) ) == virtualchain.address_reencode(str(public_key_hash)): ret_pubkey = keylib.key_formatting.decompress( issuer_public_key) else: raise Exception( "BUG: public key {} does not match {}".format( issuer_public_key, public_key_hash)) return {'data': mutable_data_json, 'public_key': ret_pubkey} else: return mutable_data_json msg = 'Failed to verify with public key hash "{}" ("{}")' log.warn(msg.format(public_key_hash, public_key_hash_0)) # try sha256 hash if data_hash is not None: log.error("Verifying profiles by hash it not supported") return None
def test_public_key_to_address_2(self): public_key = "0479BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8" address = public_key_to_address(public_key) reference_address = "1EHNa6Q4Jz2uvNExL497mE43ikXhwF6kZm" self.assertEqual(address, reference_address)
def test_public_key_to_address(self): public_key = "030589ee559348bd6a7325994f9c8eff12bd5d73cc683142bd0dd1a17abc99b0dc" address = public_key_to_address(public_key) reference_address = "1KbUJ4x8epz6QqxkmZbTc4f79JbWWz6g37" self.assertEqual(address, reference_address)
def test_public_key_to_address_2(self): public_key = "0479BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8" address = public_key_to_address(public_key) reference_address = "1EHNa6Q4Jz2uvNExL497mE43ikXhwF6kZm" self.assertEqual(address, reference_address)
def test_public_key_to_address(self): public_key = "030589ee559348bd6a7325994f9c8eff12bd5d73cc683142bd0dd1a17abc99b0dc" address = public_key_to_address(public_key) reference_address = "1KbUJ4x8epz6QqxkmZbTc4f79JbWWz6g37" self.assertEqual(address, reference_address)
def get_compressed_and_decompressed_private_key_info(privkey_info): """ Get the compressed and decompressed versions of private keys and addresses Return {'compressed_addr': ..., 'compressed_private_key_info': ..., 'decompressed_addr': ..., 'decompressed_private_key_info': ...} on success """ if virtualchain.is_multisig( privkey_info) or virtualchain.btc_is_multisig_segwit(privkey_info): # get both compressed and decompressed addresses privkeys = privkey_info['private_keys'] m, _ = virtualchain.parse_multisig_redeemscript( privkey_info['redeem_script']) privkeys_hex = [ecdsa_private_key(pk).to_hex() for pk in privkeys] decompressed_privkeys = map( lambda pk: pk if len(pk) == 64 else pk[:-2], privkeys_hex) compressed_privkeys = map( lambda pk: pk if len(pk) == 66 and pk[:-2] == '01' else pk, privkeys_hex) decompressed_multisig = virtualchain.make_multisig_info( m, decompressed_privkeys, compressed=True) compressed_multisig = virtualchain.make_multisig_info( m, compressed_privkeys, compressed=False) decompressed_addr = virtualchain.address_reencode( decompressed_multisig['address']) compressed_addr = virtualchain.address_reencode( compressed_multisig['address']) return { 'decompressed_private_key_info': decompressed_multisig, 'compressed_private_key_info': compressed_multisig, 'compressed_addr': compressed_addr, 'decompressed_addr': decompressed_addr } elif virtualchain.is_singlesig( privkey_info) or virtualchain.btc_is_singlesig_segwit( privkey_info): pk = virtualchain.get_singlesig_privkey(privkey_info) # get both compressed and decompressed addresses compressed_pk = None decompressed_pk = None if len(pk) == 66 and pk.endswith('01'): compressed_pk = pk decompressed_pk = pk[:-2] else: compressed_pk = pk decompressed_pk = pk + '01' compressed_pubk = ecdsa_private_key( compressed_pk).public_key().to_hex() decompressed_pubk = ecdsa_private_key( decompressed_pk).public_key().to_hex() compressed_addr = virtualchain.address_reencode( keylib.public_key_to_address(compressed_pubk)) decompressed_addr = virtualchain.address_reencode( keylib.public_key_to_address(decompressed_pubk)) return { 'decompressed_private_key_info': decompressed_pk, 'compressed_private_key_info': compressed_pk, 'compressed_addr': compressed_addr, 'decompressed_addr': decompressed_addr } else: raise ValueError("Invalid key bundle")