def test_sigs(self):
        fake_privkey_hex = "5512612ed6ef10ea8c5f9839c63f62107c73db7306b98588a46d0cd2c3d15ea5"
        sk = keylib.ECPrivateKey(fake_privkey_hex)
        pk = sk.public_key()

        for t in ["foo", "bar", "bassoon"]:
            self.assertTrue(
                subdomains.verify(pk.address(), t, subdomains.sign(sk, t)), t)

        subdomain = subdomains.Subdomain("bar.id", "foo",
                                         subdomains.encode_pubkey_entry(sk), 3,
                                         "")

        user_zf = {'$origin': 'foo', '$ttl': 3600, 'txt': [], 'uri': []}

        user_zf['uri'].append(
            zonefile.url_to_uri_record("https://foo_foo.com/profile.json"))
        jsonschema.validate(user_zf, USER_ZONEFILE_SCHEMA)

        subdomain.zonefile_str = blockstack_zones.make_zone_file(user_zf)

        subdomain.add_signature(sk)

        self.assertTrue(subdomain.verify_signature(pk.address()))

        parsed_zf = zonefile.decode_name_zonefile(subdomain.subdomain_name,
                                                  subdomain.zonefile_str)
        urls = user_db.user_zonefile_urls(parsed_zf)

        self.assertEqual(len(urls), 1)
        self.assertIn("https://foo_foo.com/profile.json", urls)

        self.assertRaises(
            NotImplementedError,
            lambda: subdomains.encode_pubkey_entry(fake_privkey_hex))
Beispiel #2
0
def subdomain_record_to_profile(my_rec):
    owner_addr = my_rec.address

    assert isinstance(my_rec.zonefile_str, (str, unicode))

    parsed_zf = bs_zonefile.decode_name_zonefile(my_rec.subdomain_name, my_rec.zonefile_str)
    urls = user_db.user_zonefile_urls(parsed_zf)

    # try to get pubkey from zonefile, or default to ``owner`` pubkey
    user_data_pubkey = None
    try:
        user_data_pubkey = user_db.user_zonefile_data_pubkey(parsed_zf)
        if user_data_pubkey is not None:
            user_data_pubkey = str(user_data_pubkey)
    except ValueError:
        pass # no pubkey defined in zonefile

    try:
        user_profile = storage.get_mutable_data(
            None, user_data_pubkey, blockchain_id=None,
            data_address=owner_addr, owner_address=None,
            urls=urls, drivers=None, decode=True,
        )
    except:
        user_profile = None

    if user_profile is None:
        user_profile = {'error' :
                        'Error fetching the data for subdomain {}'.format(my_rec.get_fqn())}

    data = { 'profile' : user_profile,
             'zonefile' : parsed_zf }
    return data
    def test_sigs(self):
        fake_privkey_hex = "5512612ed6ef10ea8c5f9839c63f62107c73db7306b98588a46d0cd2c3d15ea5"
        sk = keylib.ECPrivateKey(fake_privkey_hex)
        pk = sk.public_key()

        for t in ["foo", "bar", "bassoon"]:
            self.assertTrue(subdomains.verify(pk.address(), t,
                                              subdomains.sign(sk, t)), t)

        subdomain = subdomains.Subdomain("bar.id", "foo", subdomains.encode_pubkey_entry(sk), 3, "")

        user_zf = {
            '$origin': 'foo',
            '$ttl': 3600,
            'txt' : [], 'uri' : []
        }

        user_zf['uri'].append(zonefile.url_to_uri_record("https://foo_foo.com/profile.json"))
        jsonschema.validate(user_zf, USER_ZONEFILE_SCHEMA)

        subdomain.zonefile_str = blockstack_zones.make_zone_file(user_zf)

        subdomain.add_signature(sk)

        self.assertTrue(subdomain.verify_signature(pk.address()))

        parsed_zf = zonefile.decode_name_zonefile(subdomain.subdomain_name, subdomain.zonefile_str)
        urls = user_db.user_zonefile_urls(parsed_zf)

        self.assertEqual(len(urls), 1)
        self.assertIn("https://foo_foo.com/profile.json", urls)

        self.assertRaises( NotImplementedError, lambda : subdomains.encode_pubkey_entry( fake_privkey_hex ) )
Beispiel #4
0
def get_profile(name,
                zonefile_storage_drivers=None,
                profile_storage_drivers=None,
                proxy=None,
                user_zonefile=None,
                name_record=None,
                include_name_record=False,
                include_raw_zonefile=False,
                use_zonefile_urls=True,
                use_legacy=False,
                use_legacy_zonefile=True,
                decode_profile=True):
    """
    Given a name, look up an associated profile.
    Do so by first looking up the zonefile the name points to,
    and then loading the profile from that zonefile's public key.

    Notes on backwards compatibility (activated if use_legacy=True and use_legacy_zonefile=True):

    * (use_legacy=True) If the user's zonefile is really a legacy profile from Onename, then
    the profile returned will be the converted legacy profile.  The returned zonefile will still
    be a legacy profile, however.
    The caller can check this and perform the conversion automatically.

    * (use_legacy_zonefile=True) If the name points to a current zonefile that does not have a
    data public key, then the owner address of the name will be used to verify
    the profile's authenticity.

    Returns {'status': True, 'profile': profile, 'zonefile': zonefile, 'public_key': ...} on success.
    * If include_name_record is True, then include 'name_record': name_record with the user's blockchain information
    * If include_raw_zonefile is True, then include 'raw_zonefile': raw_zonefile with unparsed zone file

    Returns {'error': ...} on error
    """

    proxy = get_default_proxy() if proxy is None else proxy
    user_profile_pubkey = None

    res = subdomains.is_address_subdomain(str(name))
    if res:
        subdomain, domain = res[1]
        try:
            return subdomains.resolve_subdomain(subdomain, domain)
        except subdomains.SubdomainNotFound as e:
            log.exception(e)
            return {
                'error': "Failed to find name {}.{}".format(subdomain, domain)
            }

    raw_zonefile = None
    if user_zonefile is None:
        user_zonefile = get_name_zonefile(
            name,
            proxy=proxy,
            name_record=name_record,
            include_name_record=True,
            storage_drivers=zonefile_storage_drivers,
            include_raw_zonefile=include_raw_zonefile,
            allow_legacy=True)

        if 'error' in user_zonefile:
            return user_zonefile

        raw_zonefile = None
        if include_raw_zonefile:
            raw_zonefile = user_zonefile.pop('raw_zonefile')

        user_zonefile = user_zonefile['zonefile']

    # is this really a legacy profile?
    if blockstack_profiles.is_profile_in_legacy_format(user_zonefile):
        if not use_legacy:
            return {'error': 'Profile is in legacy format'}

        # convert it
        log.debug('Converting legacy profile to modern profile')
        user_profile = blockstack_profiles.get_person_from_legacy_format(
            user_zonefile)

    elif not user_db.is_user_zonefile(user_zonefile):
        if not use_legacy:
            return {'error': 'Name zonefile is non-standard'}

        # not a legacy profile, but a custom profile
        log.debug('Using custom legacy profile')
        user_profile = copy.deepcopy(user_zonefile)

    else:
        # get user's data public key
        data_address, owner_address = None, None

        try:
            user_data_pubkey = user_db.user_zonefile_data_pubkey(user_zonefile)
            if user_data_pubkey is not None:
                user_data_pubkey = str(user_data_pubkey)
                data_address = keylib.ECPublicKey(user_data_pubkey).address()

        except ValueError:
            # multiple keys defined; we don't know which one to use
            user_data_pubkey = None

        if not use_legacy_zonefile and user_data_pubkey is None:
            # legacy zonefile without a data public key
            return {'error': 'Name zonefile is missing a public key'}

        # find owner address
        if name_record is None:
            name_record = get_name_blockchain_record(name, proxy=proxy)
            if name_record is None or 'error' in name_record:
                log.error(
                    'Failed to look up name record for "{}"'.format(name))
                return {'error': 'Failed to look up name record'}

        assert 'address' in name_record.keys(), json.dumps(name_record,
                                                           indent=4,
                                                           sort_keys=True)
        owner_address = name_record['address']

        # get user's data public key from the zonefile
        urls = None
        if use_zonefile_urls and user_zonefile is not None:
            urls = user_db.user_zonefile_urls(user_zonefile)

        user_profile = None
        user_profile_pubkey = None

        try:
            user_profile_res = storage.get_mutable_data(
                name,
                user_data_pubkey,
                blockchain_id=name,
                data_address=data_address,
                owner_address=owner_address,
                urls=urls,
                drivers=profile_storage_drivers,
                decode=decode_profile,
                return_public_key=True)

            user_profile = user_profile_res['data']
            user_profile_pubkey = user_profile_res['public_key']

        except Exception as e:
            log.exception(e)
            return {'error': 'Failure in parsing and fetching profile'}

        if user_profile is None or json_is_error(user_profile):
            if user_profile is None:
                log.error('no user profile for {}'.format(name))
            else:
                log.error('failed to load profile for {}: {}'.format(
                    name, user_profile['error']))

            return {'error': 'Failed to load user profile'}

    # finally, if the caller asked for the name record, and we didn't get a chance to look it up,
    # then go get it.
    ret = {
        'status': True,
        'profile': user_profile,
        'zonefile': user_zonefile,
        'public_key': user_profile_pubkey
    }

    if include_name_record:
        if name_record is None:
            name_record = get_name_blockchain_record(name, proxy=proxy)

        if name_record is None or 'error' in name_record:
            log.error('Failed to look up name record for "{}"'.format(name))
            return {'error': 'Failed to look up name record'}

        ret['name_record'] = name_record

    if include_raw_zonefile:
        if raw_zonefile is not None:
            ret['raw_zonefile'] = raw_zonefile

    return ret
Beispiel #5
0
        log.exception(v)
        user_data_pubkey = None

    if user_data_pubkey is None and data_address is None and owner_address is None:
        raise Exception(
            "Missing user data public key and address; cannot verify profile")

    if user_data_pubkey is None:
        log.warn(
            "No data public key set; falling back to hash of data and/or owner public key for profile authentication"
        )

    # get user's data public key from the zonefile
    urls = None
    if use_zonefile_urls:
        urls = user_db.user_zonefile_urls(user_zonefile)

    user_profile = storage.get_mutable_data(name,
                                            user_data_pubkey,
                                            data_address=data_address,
                                            owner_address=owner_address,
                                            urls=urls,
                                            drivers=storage_drivers,
                                            decode=decode)
    return user_profile


def load_data_pubkey_for_new_zonefile(wallet_keys={}, config_path=CONFIG_PATH):
    """
    Find the right public key to use for data when creating a new zonefile.
    If the wallet has a data keypair defined, use that.
def get_profile(name, zonefile_storage_drivers=None, profile_storage_drivers=None,
                proxy=None, user_zonefile=None, name_record=None,
                include_name_record=False, include_raw_zonefile=False, use_zonefile_urls=True,
                use_legacy=False, use_legacy_zonefile=True, decode_profile=True):
    """
    Given a name, look up an associated profile.
    Do so by first looking up the zonefile the name points to,
    and then loading the profile from that zonefile's public key.

    Notes on backwards compatibility (activated if use_legacy=True and use_legacy_zonefile=True):
    
    * (use_legacy=True) If the user's zonefile is really a legacy profile from Onename, then
    the profile returned will be the converted legacy profile.  The returned zonefile will still
    be a legacy profile, however.
    The caller can check this and perform the conversion automatically.

    * (use_legacy_zonefile=True) If the name points to a current zonefile that does not have a 
    data public key, then the owner address of the name will be used to verify
    the profile's authenticity.

    Returns {'status': True, 'profile': profile, 'zonefile': zonefile} on success.
    * If include_name_record is True, then include 'name_record': name_record with the user's blockchain information
    * If include_raw_zonefile is True, then include 'raw_zonefile': raw_zonefile with unparsed zone file

    Returns {'error': ...} on error
    """

    proxy = get_default_proxy() if proxy is None else proxy

    raw_zonefile = None
    if user_zonefile is None:
        user_zonefile = get_name_zonefile(
            name, proxy=proxy,
            name_record=name_record, include_name_record=True,
            storage_drivers=zonefile_storage_drivers,
            include_raw_zonefile=include_raw_zonefile,
            allow_legacy=True
        )

        if 'error' in user_zonefile:
            return user_zonefile

        raw_zonefile = None
        if include_raw_zonefile:
            raw_zonefile = user_zonefile.pop('raw_zonefile')

        user_zonefile = user_zonefile['zonefile']

    # is this really a legacy profile?
    if blockstack_profiles.is_profile_in_legacy_format(user_zonefile):
        if not use_legacy:
            return {'error': 'Profile is in legacy format'}

        # convert it
        log.debug('Converting legacy profile to modern profile')
        user_profile = blockstack_profiles.get_person_from_legacy_format(user_zonefile)

    elif not user_db.is_user_zonefile(user_zonefile):
        if not use_legacy:
            return {'error': 'Name zonefile is non-standard'}

        # not a legacy profile, but a custom profile
        log.debug('Using custom legacy profile')
        user_profile = copy.deepcopy(user_zonefile)

    else:
        # get user's data public key
        data_address, owner_address = None, None

        try:
            user_data_pubkey = user_db.user_zonefile_data_pubkey(user_zonefile)
            if user_data_pubkey is not None:
                user_data_pubkey = str(user_data_pubkey)
                data_address = keylib.ECPublicKey(user_data_pubkey).address()

        except ValueError:
            # multiple keys defined; we don't know which one to use
            user_data_pubkey = None

        if not use_legacy_zonefile and user_data_pubkey is None:
            # legacy zonefile without a data public key 
            return {'error': 'Name zonefile is missing a public key'}

        # find owner address
        if name_record is None:
            name_record = get_name_blockchain_record(name, proxy=proxy)
            if name_record is None or 'error' in name_record:
                log.error('Failed to look up name record for "{}"'.format(name))
                return {'error': 'Failed to look up name record'}

        assert 'address' in name_record.keys(), json.dumps(name_record, indent=4, sort_keys=True)
        owner_address = name_record['address']

        # get user's data public key from the zonefile
        urls = None
        if use_zonefile_urls and user_zonefile is not None:
            urls = user_db.user_zonefile_urls(user_zonefile)

        user_profile = storage.get_mutable_data(
            name, user_data_pubkey, blockchain_id=name,
            data_address=data_address, owner_address=owner_address,
            urls=urls, drivers=profile_storage_drivers, decode=decode_profile,
        )

        if user_profile is None or json_is_error(user_profile):
            if user_profile is None:
                log.error('no user profile for {}'.format(name))
            else:
                log.error('failed to load profile for {}: {}'.format(name, user_profile['error']))

            return {'error': 'Failed to load user profile'}

    # finally, if the caller asked for the name record, and we didn't get a chance to look it up,
    # then go get it.
    ret = {
        'status': True,
        'profile': user_profile,
        'zonefile': user_zonefile
    }

    if include_name_record:
        if name_record is None:
            name_record = get_name_blockchain_record(name, proxy=proxy)

        if name_record is None or 'error' in name_record:
            log.error('Failed to look up name record for "{}"'.format(name))
            return {'error': 'Failed to look up name record'}

        ret['name_record'] = name_record

    if include_raw_zonefile:
        if raw_zonefile is not None:
            ret['raw_zonefile'] = raw_zonefile

    return ret
Beispiel #7
0
def get_profile(name,
                zonefile_storage_drivers=None,
                profile_storage_drivers=None,
                proxy=None,
                user_zonefile=None,
                name_record=None,
                include_name_record=False,
                include_raw_zonefile=False,
                use_zonefile_urls=True,
                use_legacy=False,
                use_legacy_zonefile=True,
                decode_profile=True):
    """
    Given a name, look up an associated profile.
    Do so by first looking up the zonefile the name points to,
    and then loading the profile from that zonefile's public key.

    Notes on backwards compatibility (activated if use_legacy=True and use_legacy_zonefile=True):
    
    * (use_legacy=True) If the user's zonefile is really a legacy profile from Onename, then
    the profile returned will be the converted legacy profile.  The returned zonefile will still
    be a legacy profile, however.
    The caller can check this and perform the conversion automatically.

    * (use_legacy_zonefile=True) If the name points to a current zonefile that does not have a 
    data public key, then the owner address of the name will be used to verify
    the profile's authenticity.

    Returns (profile, zonefile) on success.  If include_name_record is True, then zonefile['name_record'] will be defined and will contain the user's blockchain information
    Returns (None, {'error': ...}) on failure
    """

    proxy = get_default_proxy() if proxy is None else proxy

    raw_zonefile = None
    if user_zonefile is None:
        user_zonefile = get_name_zonefile(
            name,
            proxy=proxy,
            name_record=name_record,
            include_name_record=True,
            storage_drivers=zonefile_storage_drivers,
            include_raw_zonefile=include_raw_zonefile,
            allow_legacy=True)

        if 'error' in user_zonefile:
            return None, user_zonefile

        raw_zonefile = None
        if include_raw_zonefile:
            raw_zonefile = user_zonefile.pop('raw_zonefile')

        user_zonefile = user_zonefile['zonefile']

    # is this really a legacy profile?
    if blockstack_profiles.is_profile_in_legacy_format(user_zonefile):
        if not use_legacy:
            return (None, {'error': 'Profile is in legacy format'})

        # convert it
        log.debug('Converting legacy profile to modern profile')
        user_profile = blockstack_profiles.get_person_from_legacy_format(
            user_zonefile)

    elif not user_db.is_user_zonefile(user_zonefile):
        if not use_legacy:
            return (None, {'error': 'Name zonefile is non-standard'})

        # not a legacy profile, but a custom profile
        log.debug('Using custom legacy profile')
        user_profile = copy.deepcopy(user_zonefile)

    else:
        # get user's data public key
        data_address, owner_address = None, None

        try:
            user_data_pubkey = user_db.user_zonefile_data_pubkey(user_zonefile)
            if user_data_pubkey is not None:
                user_data_pubkey = str(user_data_pubkey)
                data_address = virtualchain.BitcoinPublicKey(
                    user_data_pubkey).address()

        except ValueError:
            # multiple keys defined; we don't know which one to use
            user_data_pubkey = None

        if not use_legacy_zonefile and user_data_pubkey is None:
            # legacy zonefile without a data public key
            return (None, {'error': 'Name zonefile is missing a public key'})

        # find owner address
        if name_record is None:
            name_record = get_name_blockchain_record(name, proxy=proxy)
            if name_record is None or 'error' in name_record:
                log.error(
                    'Failed to look up name record for "{}"'.format(name))
                return None, {'error': 'Failed to look up name record'}

        assert 'address' in name_record.keys(), json.dumps(name_record,
                                                           indent=4,
                                                           sort_keys=True)
        owner_address = name_record['address']

        # get user's data public key from the zonefile
        urls = None
        if use_zonefile_urls and user_zonefile is not None:
            urls = user_db.user_zonefile_urls(user_zonefile)

        user_profile = storage.get_mutable_data(
            name,
            user_data_pubkey,
            data_address=data_address,
            owner_address=owner_address,
            urls=urls,
            drivers=profile_storage_drivers,
            decode=decode_profile,
        )

        if user_profile is None or json_is_error(user_profile):
            if user_profile is None:
                log.debug('WARN: no user profile for {}'.format(name))
            else:
                log.debug('WARN: failed to load profile for {}: {}'.format(
                    name, user_profile['error']))

            return None, {'error': 'Failed to load user profile'}

    # finally, if the caller asked for the name record, and we didn't get a chance to look it up,
    # then go get it.
    if include_name_record:
        if name_record is None:
            name_record = get_name_blockchain_record(name, proxy=proxy)

        if name_record is None or 'error' in name_record:
            log.error('Failed to look up name record for "{}"'.format(name))
            return None, {'error': 'Failed to look up name record'}

        user_zonefile['name_record'] = name_record

    if include_raw_zonefile:
        if raw_zonefile is not None:
            user_zonefile['raw_zonefile'] = raw_zonefile

    return user_profile, user_zonefile